Finalize remaining repo updates
This commit is contained in:
96
README.md
96
README.md
@@ -1,44 +1,59 @@
|
|||||||
# Feynman
|
<p align="center">
|
||||||
|
<a href="https://feynman.is">
|
||||||
|
<img src="assets/hero.png" alt="Feynman CLI" width="800" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p align="center">The open source AI research agent.</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://feynman.is/docs"><img alt="Docs" src="https://img.shields.io/badge/docs-feynman.is-0d9668?style=flat-square" /></a>
|
||||||
|
<a href="https://www.npmjs.com/package/@companion-ai/feynman"><img alt="npm" src="https://img.shields.io/npm/v/@companion-ai/feynman?style=flat-square" /></a>
|
||||||
|
<a href="https://github.com/getcompanion-ai/feynman/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/getcompanion-ai/feynman?style=flat-square" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
The open source AI research agent
|
---
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://feynman.is/install | bash
|
curl -fsSL https://feynman.is/install | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
# Windows
|
||||||
irm https://feynman.is/install.ps1 | iex
|
irm https://feynman.is/install.ps1 | iex
|
||||||
```
|
```
|
||||||
|
|
||||||
Or install the npm fallback:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# npm fallback
|
||||||
npm install -g @companion-ai/feynman
|
npm install -g @companion-ai/feynman
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
Then run `feynman setup` to configure your model and get started.
|
||||||
feynman setup
|
|
||||||
feynman
|
---
|
||||||
|
|
||||||
|
### What you type → what happens
|
||||||
|
|
||||||
|
```
|
||||||
|
$ feynman "what do we know about scaling laws"
|
||||||
|
→ Searches papers and web, produces a cited research brief
|
||||||
|
|
||||||
|
$ feynman deepresearch "mechanistic interpretability"
|
||||||
|
→ Multi-agent investigation with parallel researchers, synthesis, verification
|
||||||
|
|
||||||
|
$ feynman lit "RLHF alternatives"
|
||||||
|
→ Literature review with consensus, disagreements, open questions
|
||||||
|
|
||||||
|
$ feynman audit 2401.12345
|
||||||
|
→ Compares paper claims against the public codebase
|
||||||
|
|
||||||
|
$ feynman replicate "chain-of-thought improves math"
|
||||||
|
→ Asks where to run, then builds a replication plan
|
||||||
```
|
```
|
||||||
|
|
||||||
Feynman works directly inside your folder or repo. For long-running work, keep the stable repo contract in `AGENTS.md`, the current task brief in `outputs/.plans/`, and the chronological lab notebook in `CHANGELOG.md`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What you type → what happens
|
### Workflows
|
||||||
|
|
||||||
| Prompt | Result |
|
|
||||||
| --- | --- |
|
|
||||||
| `feynman "what do we know about scaling laws"` | Searches papers and web, produces a cited research brief |
|
|
||||||
| `feynman deepresearch "mechanistic interpretability"` | Multi-agent investigation with parallel researchers, synthesis, verification |
|
|
||||||
| `feynman lit "RLHF alternatives"` | Literature review with consensus, disagreements, open questions |
|
|
||||||
| `feynman audit 2401.12345` | Compares paper claims against the public codebase |
|
|
||||||
| `feynman replicate "chain-of-thought improves math"` | Asks where to run, then builds a replication plan |
|
|
||||||
| `feynman "summarize this PDF" --prompt paper.pdf` | One-shot mode, no REPL |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Workflows
|
|
||||||
|
|
||||||
Ask naturally or use slash commands as shortcuts.
|
Ask naturally or use slash commands as shortcuts.
|
||||||
|
|
||||||
@@ -56,9 +71,9 @@ Ask naturally or use slash commands as shortcuts.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Agents
|
### Agents
|
||||||
|
|
||||||
Four bundled research agents, dispatched automatically or via subagent commands.
|
Four bundled research agents, dispatched automatically.
|
||||||
|
|
||||||
- **Researcher** — gather evidence across papers, web, repos, docs
|
- **Researcher** — gather evidence across papers, web, repos, docs
|
||||||
- **Reviewer** — simulated peer review with severity-graded feedback
|
- **Reviewer** — simulated peer review with severity-graded feedback
|
||||||
@@ -67,42 +82,23 @@ Four bundled research agents, dispatched automatically or via subagent commands.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Tools
|
### Tools
|
||||||
|
|
||||||
- **[AlphaXiv](https://www.alphaxiv.org/)** — paper search, Q&A, code reading, persistent annotations
|
- **[AlphaXiv](https://www.alphaxiv.org/)** — paper search, Q&A, code reading, persistent annotations
|
||||||
- **Docker** — isolated container execution for safe experiments on your machine
|
- **Docker** — isolated container execution for safe experiments on your machine
|
||||||
- **Web search** — Gemini or Perplexity, zero-config default via signed-in Chromium
|
- **Web search** — Gemini or Perplexity, zero-config default
|
||||||
- **Session search** — optional indexed recall across prior research sessions
|
- **Session search** — indexed recall across prior research sessions
|
||||||
- **Preview** — browser and PDF export of generated artifacts
|
- **Preview** — browser and PDF export of generated artifacts
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CLI
|
### How it works
|
||||||
|
|
||||||
```bash
|
Built on [Pi](https://github.com/badlogic/pi-mono) for the agent runtime, [alphaXiv](https://www.alphaxiv.org/) for paper search and analysis, and [Docker](https://www.docker.com/) for isolated local execution. Every output is source-grounded — claims link to papers, docs, or repos with direct URLs.
|
||||||
feynman # REPL
|
|
||||||
feynman setup # guided setup
|
|
||||||
feynman doctor # diagnose everything
|
|
||||||
feynman status # current config summary
|
|
||||||
feynman model login [provider] # model auth
|
|
||||||
feynman model set <provider/model> # set default model
|
|
||||||
feynman alpha login # alphaXiv auth
|
|
||||||
feynman packages list # core vs optional packages
|
|
||||||
feynman packages install memory # opt into heavier packages on demand
|
|
||||||
feynman search status # web search config
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How it works
|
### Contributing
|
||||||
|
|
||||||
Built on [Pi](https://github.com/badlogic/pi-mono) for the agent runtime, [alphaXiv](https://www.alphaxiv.org/) for paper search and analysis, and [Docker](https://www.docker.com/) for isolated local execution
|
|
||||||
|
|
||||||
Every output is source-grounded — claims link to papers, docs, or repos with direct URLs
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/getcompanion-ai/feynman.git
|
git clone https://github.com/getcompanion-ai/feynman.git
|
||||||
|
|||||||
BIN
assets/hero-raw.png
Normal file
BIN
assets/hero-raw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 884 KiB |
BIN
assets/hero.png
Normal file
BIN
assets/hero.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 MiB |
@@ -16,7 +16,174 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "@sinclair/typebox";
|
||||||
|
|
||||||
import { getExtensionCommandSpec } from "../../metadata/commands.mjs";
|
import { getExtensionCommandSpec } from "../../metadata/commands.mjs";
|
||||||
import { formatToolText } from "./shared.js";
|
import { collapseExcessBlankLines, formatToolText } from "./shared.js";
|
||||||
|
|
||||||
|
type JsonRecord = Record<string, unknown>;
|
||||||
|
|
||||||
|
type AlphaSearchHit = {
|
||||||
|
rank?: number;
|
||||||
|
title?: string;
|
||||||
|
publishedAt?: string;
|
||||||
|
organizations?: string;
|
||||||
|
authors?: string;
|
||||||
|
abstract?: string;
|
||||||
|
arxivId?: string;
|
||||||
|
arxivUrl?: string;
|
||||||
|
alphaXivUrl?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AlphaSearchSection = {
|
||||||
|
count: number;
|
||||||
|
results: AlphaSearchHit[];
|
||||||
|
note?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type AlphaSearchPayload = {
|
||||||
|
query?: string;
|
||||||
|
mode?: string;
|
||||||
|
results?: AlphaSearchHit[];
|
||||||
|
semantic?: AlphaSearchSection;
|
||||||
|
keyword?: AlphaSearchSection;
|
||||||
|
agentic?: AlphaSearchSection;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is JsonRecord {
|
||||||
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanText(value: unknown, maxLength = 320): string | undefined {
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const collapsed = collapseExcessBlankLines(value)
|
||||||
|
.replace(/\s*\n\s*/g, " ")
|
||||||
|
.replace(/[ \t]+/g, " ");
|
||||||
|
if (!collapsed) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return collapsed.length > maxLength ? `${collapsed.slice(0, maxLength - 1).trimEnd()}…` : collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeHit(value: unknown, fallbackRank: number): AlphaSearchHit | null {
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = cleanText(value.title, 220);
|
||||||
|
if (!title) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rank: typeof value.rank === "number" ? value.rank : fallbackRank,
|
||||||
|
title,
|
||||||
|
publishedAt: cleanText(value.publishedAt, 48),
|
||||||
|
organizations: cleanText(value.organizations, 180),
|
||||||
|
authors: cleanText(value.authors, 220),
|
||||||
|
abstract: cleanText(value.abstract, 360),
|
||||||
|
arxivId: cleanText(value.arxivId, 32),
|
||||||
|
arxivUrl: cleanText(value.arxivUrl, 160),
|
||||||
|
alphaXivUrl: cleanText(value.alphaXivUrl, 160),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeHits(value: unknown): AlphaSearchHit[] {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
.map((entry, index) => sanitizeHit(entry, index + 1))
|
||||||
|
.filter((entry): entry is AlphaSearchHit => entry !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeSection(value: unknown): AlphaSearchSection {
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
return { count: 0, results: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = sanitizeHits(value.results);
|
||||||
|
const note = results.length === 0 ? cleanText(value.raw, 600) : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
count: results.length,
|
||||||
|
results,
|
||||||
|
...(note ? { note } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sanitizeAlphaSearchPayload(value: unknown): AlphaSearchPayload {
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload: AlphaSearchPayload = {
|
||||||
|
query: cleanText(value.query, 240),
|
||||||
|
mode: cleanText(value.mode, 32),
|
||||||
|
};
|
||||||
|
|
||||||
|
const topLevelResults = sanitizeHits(value.results);
|
||||||
|
if (topLevelResults.length > 0) {
|
||||||
|
payload.results = topLevelResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of ["semantic", "keyword", "agentic"] as const) {
|
||||||
|
if (key in value) {
|
||||||
|
payload[key] = sanitizeSection(value[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushHitLines(lines: string[], hit: AlphaSearchHit): void {
|
||||||
|
lines.push(`${hit.rank ?? "?"}. ${hit.title ?? "Untitled result"}`);
|
||||||
|
if (hit.arxivId) lines.push(` arXiv: ${hit.arxivId}`);
|
||||||
|
if (hit.publishedAt) lines.push(` published: ${hit.publishedAt}`);
|
||||||
|
if (hit.organizations) lines.push(` orgs: ${hit.organizations}`);
|
||||||
|
if (hit.authors) lines.push(` authors: ${hit.authors}`);
|
||||||
|
if (hit.abstract) lines.push(` abstract: ${hit.abstract}`);
|
||||||
|
if (hit.arxivUrl) lines.push(` arXiv URL: ${hit.arxivUrl}`);
|
||||||
|
if (hit.alphaXivUrl) lines.push(` alphaXiv URL: ${hit.alphaXivUrl}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushSectionLines(lines: string[], label: string, section: AlphaSearchSection): void {
|
||||||
|
lines.push(`${label} (${section.count})`);
|
||||||
|
if (section.results.length === 0) {
|
||||||
|
lines.push(section.note ? ` note: ${section.note}` : " no parsed results");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const hit of section.results) {
|
||||||
|
pushHitLines(lines, hit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatAlphaSearchContext(value: unknown): string {
|
||||||
|
const payload = sanitizeAlphaSearchPayload(value);
|
||||||
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
if (payload.query) lines.push(`query: ${payload.query}`);
|
||||||
|
if (payload.mode) lines.push(`mode: ${payload.mode}`);
|
||||||
|
|
||||||
|
if (payload.results) {
|
||||||
|
pushSectionLines(lines, "results", { count: payload.results.length, results: payload.results });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [label, section] of [
|
||||||
|
["semantic", payload.semantic],
|
||||||
|
["keyword", payload.keyword],
|
||||||
|
["agentic", payload.agentic],
|
||||||
|
] as const) {
|
||||||
|
if (section) {
|
||||||
|
pushSectionLines(lines, label, section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.length > 0 ? lines.join("\n") : "No alpha search results returned.";
|
||||||
|
}
|
||||||
|
|
||||||
export function registerAlphaCommands(pi: ExtensionAPI): void {
|
export function registerAlphaCommands(pi: ExtensionAPI): void {
|
||||||
pi.registerCommand("alpha-login", {
|
pi.registerCommand("alpha-login", {
|
||||||
@@ -72,9 +239,10 @@ export function registerAlphaTools(pi: ExtensionAPI): void {
|
|||||||
async execute(_toolCallId, params) {
|
async execute(_toolCallId, params) {
|
||||||
try {
|
try {
|
||||||
const result = await searchPapers(params.query, params.mode?.trim() || "all");
|
const result = await searchPapers(params.query, params.mode?.trim() || "all");
|
||||||
|
const sanitized = sanitizeAlphaSearchPayload(result);
|
||||||
return {
|
return {
|
||||||
content: [{ type: "text", text: formatToolText(result) }],
|
content: [{ type: "text", text: formatAlphaSearchContext(sanitized) }],
|
||||||
details: result,
|
details: sanitized,
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
await disconnect();
|
await disconnect();
|
||||||
|
|||||||
@@ -27,8 +27,13 @@ export const FEYNMAN_RESEARCH_TOOLS = [
|
|||||||
"preview_file",
|
"preview_file",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function collapseExcessBlankLines(text: string): string {
|
||||||
|
return text.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
||||||
|
}
|
||||||
|
|
||||||
export function formatToolText(result: unknown): string {
|
export function formatToolText(result: unknown): string {
|
||||||
return typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
||||||
|
return collapseExcessBlankLines(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFeynmanHome(): string {
|
export function getFeynmanHome(): string {
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ export const OPTIONAL_PACKAGE_PRESETS = {
|
|||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export type OptionalPackagePresetName = keyof typeof OPTIONAL_PACKAGE_PRESETS;
|
||||||
|
|
||||||
const LEGACY_DEFAULT_PACKAGE_SOURCES = [
|
const LEGACY_DEFAULT_PACKAGE_SOURCES = [
|
||||||
...CORE_PACKAGE_SOURCES,
|
...CORE_PACKAGE_SOURCES,
|
||||||
"npm:pi-generative-ui",
|
"npm:pi-generative-ui",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type OptionalPackagePresetName = keyof typeof OPTIONAL_PACKAGE_PRESETS;
|
|
||||||
|
|
||||||
function arraysMatchAsSets(left: readonly string[], right: readonly string[]): boolean {
|
function arraysMatchAsSets(left: readonly string[], right: readonly string[]): boolean {
|
||||||
if (left.length !== right.length) {
|
if (left.length !== right.length) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
86
tests/alpha-tools.test.ts
Normal file
86
tests/alpha-tools.test.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
|
import { formatAlphaSearchContext, sanitizeAlphaSearchPayload } from "../extensions/research-tools/alpha.js";
|
||||||
|
import { formatToolText } from "../extensions/research-tools/shared.js";
|
||||||
|
|
||||||
|
test("sanitizeAlphaSearchPayload drops raw alpha search text while keeping parsed hits", () => {
|
||||||
|
const payload = sanitizeAlphaSearchPayload({
|
||||||
|
query: "scaling laws",
|
||||||
|
mode: "all",
|
||||||
|
semantic: {
|
||||||
|
raw: "\n\n\n1. **Paper A**\n- Abstract: noisy raw block",
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
rank: 1,
|
||||||
|
title: "Paper A",
|
||||||
|
publishedAt: "2025-09-28",
|
||||||
|
organizations: "Stanford University, EPFL",
|
||||||
|
authors: "A. Author, B. Author",
|
||||||
|
abstract: "Line one.\n\n\nLine two.",
|
||||||
|
arxivId: "2509.24012",
|
||||||
|
arxivUrl: "https://arxiv.org/abs/2509.24012",
|
||||||
|
alphaXivUrl: "https://www.alphaxiv.org/overview/2509.24012",
|
||||||
|
raw: "internal raw block that should be dropped",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
keyword: {
|
||||||
|
raw: "\n\n\nNoisy keyword fallback",
|
||||||
|
results: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(payload, {
|
||||||
|
query: "scaling laws",
|
||||||
|
mode: "all",
|
||||||
|
semantic: {
|
||||||
|
count: 1,
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
rank: 1,
|
||||||
|
title: "Paper A",
|
||||||
|
publishedAt: "2025-09-28",
|
||||||
|
organizations: "Stanford University, EPFL",
|
||||||
|
authors: "A. Author, B. Author",
|
||||||
|
abstract: "Line one. Line two.",
|
||||||
|
arxivId: "2509.24012",
|
||||||
|
arxivUrl: "https://arxiv.org/abs/2509.24012",
|
||||||
|
alphaXivUrl: "https://www.alphaxiv.org/overview/2509.24012",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
keyword: {
|
||||||
|
count: 0,
|
||||||
|
results: [],
|
||||||
|
note: "Noisy keyword fallback",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("formatAlphaSearchContext emits compact model-facing text without raw JSON escapes", () => {
|
||||||
|
const text = formatAlphaSearchContext({
|
||||||
|
query: "scaling laws",
|
||||||
|
mode: "semantic",
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
rank: 1,
|
||||||
|
title: "Paper A",
|
||||||
|
abstract: "First line.\n\n\nSecond line.",
|
||||||
|
arxivId: "2509.24012",
|
||||||
|
raw: "should not appear",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
raw: "\n\n\nvery noisy raw payload",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.match(text, /query: scaling laws/);
|
||||||
|
assert.match(text, /1\. Paper A/);
|
||||||
|
assert.match(text, /abstract: First line\. Second line\./);
|
||||||
|
assert.ok(!text.includes("\\n"));
|
||||||
|
assert.ok(!text.includes("raw"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("formatToolText collapses excess blank lines in plain strings", () => {
|
||||||
|
assert.equal(formatToolText("alpha\n\n\n\nbeta"), "alpha\n\nbeta");
|
||||||
|
});
|
||||||
1
website/.astro/data-store.json
Normal file
1
website/.astro/data-store.json
Normal file
File diff suppressed because one or more lines are too long
@@ -7,8 +7,8 @@ export default defineConfig({
|
|||||||
markdown: {
|
markdown: {
|
||||||
shikiConfig: {
|
shikiConfig: {
|
||||||
themes: {
|
themes: {
|
||||||
light: 'github-light',
|
light: 'everforest-light',
|
||||||
dark: 'github-dark',
|
dark: 'everforest-dark',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
website/public/hero-raw.png
Normal file
BIN
website/public/hero-raw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 884 KiB |
BIN
website/public/hero.png
Normal file
BIN
website/public/hero.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 MiB |
@@ -6,7 +6,6 @@ import AsciiLogo from '../components/AsciiLogo.astro';
|
|||||||
<Base title="Feynman — The open source AI research agent" active="home">
|
<Base title="Feynman — The open source AI research agent" active="home">
|
||||||
<section class="text-center pt-24 pb-20 px-6">
|
<section class="text-center pt-24 pb-20 px-6">
|
||||||
<div class="max-w-2xl mx-auto">
|
<div class="max-w-2xl mx-auto">
|
||||||
<AsciiLogo size="hero" class="mb-4" />
|
|
||||||
<h1 class="text-5xl sm:text-6xl font-bold tracking-tight mb-6" style="text-wrap: balance">The open source AI research agent</h1>
|
<h1 class="text-5xl sm:text-6xl font-bold tracking-tight mb-6" style="text-wrap: balance">The open source AI research agent</h1>
|
||||||
<p class="text-lg text-text-muted mb-10 leading-relaxed" style="text-wrap: pretty">Investigate topics, write papers, run experiments, review research, audit codebases — every output cited and source-grounded</p>
|
<p class="text-lg text-text-muted mb-10 leading-relaxed" style="text-wrap: pretty">Investigate topics, write papers, run experiments, review research, audit codebases — every output cited and source-grounded</p>
|
||||||
<button id="copy-btn" class="group inline-flex items-center justify-between gap-3 bg-surface rounded-lg px-5 py-3 mb-8 font-mono text-sm hover:border-accent/40 hover:text-accent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent rounded-lg border border-border max-w-full" aria-label="Copy install command">
|
<button id="copy-btn" class="group inline-flex items-center justify-between gap-3 bg-surface rounded-lg px-5 py-3 mb-8 font-mono text-sm hover:border-accent/40 hover:text-accent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent rounded-lg border border-border max-w-full" aria-label="Copy install command">
|
||||||
@@ -20,6 +19,9 @@ import AsciiLogo from '../components/AsciiLogo.astro';
|
|||||||
<a href="https://github.com/getcompanion-ai/feynman" target="_blank" rel="noopener" class="px-6 py-2.5 rounded-lg border border-border text-text-muted font-semibold text-sm hover:border-text-dim hover:text-text-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg">GitHub</a>
|
<a href="https://github.com/getcompanion-ai/feynman" target="_blank" rel="noopener" class="px-6 py-2.5 rounded-lg border border-border text-text-muted font-semibold text-sm hover:border-text-dim hover:text-text-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg">GitHub</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="max-w-4xl mx-auto mt-16">
|
||||||
|
<img src="/hero-raw.png" alt="Feynman CLI" class="w-full" />
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="py-20 px-6">
|
<section class="py-20 px-6">
|
||||||
|
|||||||
@@ -3,31 +3,31 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color-bg: #f0f5f1;
|
--color-bg: #f3ead3;
|
||||||
--color-surface: #e4ece6;
|
--color-surface: #eae4ca;
|
||||||
--color-surface-2: #d8e3db;
|
--color-surface-2: #e0dbc2;
|
||||||
--color-border: #c2d1c6;
|
--color-border: #c9c4b0;
|
||||||
--color-text: #1a2e22;
|
--color-text: #3a464c;
|
||||||
--color-text-muted: #3d5c4a;
|
--color-text-muted: #5c6a72;
|
||||||
--color-text-dim: #6b8f7a;
|
--color-text-dim: #859289;
|
||||||
--color-accent: #0d9668;
|
--color-accent: #6e8b53;
|
||||||
--color-accent-hover: #077a54;
|
--color-accent-hover: #5a7342;
|
||||||
--color-accent-subtle: #c6e4d4;
|
--color-accent-subtle: #d5e3bf;
|
||||||
--color-teal: #0e8a7d;
|
--color-teal: #5da09a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--color-bg: #050a08;
|
--color-bg: #2d353b;
|
||||||
--color-surface: #0c1410;
|
--color-surface: #343f44;
|
||||||
--color-surface-2: #131f1a;
|
--color-surface-2: #3a464c;
|
||||||
--color-border: #1b2f26;
|
--color-border: #5c6a72;
|
||||||
--color-text: #f0f5f2;
|
--color-text: #d3c6aa;
|
||||||
--color-text-muted: #8aaa9a;
|
--color-text-muted: #9da9a0;
|
||||||
--color-text-dim: #4d7565;
|
--color-text-dim: #859289;
|
||||||
--color-accent: #34d399;
|
--color-accent: #a7c080;
|
||||||
--color-accent-hover: #10b981;
|
--color-accent-hover: #93ad6c;
|
||||||
--color-accent-subtle: #064e3b;
|
--color-accent-subtle: #425047;
|
||||||
--color-teal: #2dd4bf;
|
--color-teal: #7fbbb3;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -87,7 +87,7 @@ body {
|
|||||||
.prose code {
|
.prose code {
|
||||||
font-family: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
|
font-family: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
background-color: var(--color-surface);
|
background-color: var(--color-surface-2);
|
||||||
padding: 0.125rem 0.375rem;
|
padding: 0.125rem 0.375rem;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
@@ -95,7 +95,6 @@ body {
|
|||||||
|
|
||||||
.prose pre {
|
.prose pre {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--color-surface) !important;
|
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
padding: 1rem 1.25rem;
|
padding: 1rem 1.25rem;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
@@ -103,13 +102,13 @@ body {
|
|||||||
font-family: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
|
font-family: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose pre code {
|
.prose pre code {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: var(--color-text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-code {
|
.copy-code {
|
||||||
@@ -137,9 +136,6 @@ pre:hover .copy-code {
|
|||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose pre code span {
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose table {
|
.prose table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -201,6 +197,34 @@ pre:hover .copy-code {
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--color-border) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--color-text-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--color-accent-subtle);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
.agent-entry {
|
.agent-entry {
|
||||||
background-color: var(--color-surface);
|
background-color: var(--color-surface);
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user