@falkenslab/agent-kit 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/assets/knowledge-plugin/.claude-plugin/plugin.json +4 -0
- package/assets/knowledge-plugin/commands/ingest.md +7 -0
- package/assets/knowledge-plugin/commands/lint.md +5 -0
- package/assets/knowledge-plugin/commands/query.md +5 -0
- package/assets/knowledge-plugin/skills/knowledge-ingest/SKILL.md +42 -0
- package/assets/knowledge-plugin/skills/knowledge-lint/SKILL.md +26 -0
- package/assets/knowledge-plugin/skills/knowledge-pages/SKILL.md +137 -0
- package/assets/knowledge-plugin/skills/knowledge-query/SKILL.md +14 -0
- package/dist/core/agentSpec.d.ts +132 -0
- package/dist/core/agentSpec.js +1 -0
- package/dist/core/claudeAuth.d.ts +24 -0
- package/dist/core/claudeAuth.js +27 -0
- package/dist/core/hooks/fileScopeGate.d.ts +33 -0
- package/dist/core/hooks/fileScopeGate.js +68 -0
- package/dist/core/hooks/humanInput.d.ts +15 -0
- package/dist/core/hooks/humanInput.js +52 -0
- package/dist/core/hooks/stepGate.d.ts +11 -0
- package/dist/core/hooks/stepGate.js +47 -0
- package/dist/core/hooks/subagentBashGate.d.ts +28 -0
- package/dist/core/hooks/subagentBashGate.js +40 -0
- package/dist/core/hooks/subagentForegroundGate.d.ts +18 -0
- package/dist/core/hooks/subagentForegroundGate.js +34 -0
- package/dist/core/hooks/subagentTypeGate.d.ts +16 -0
- package/dist/core/hooks/subagentTypeGate.js +33 -0
- package/dist/core/hooks/transcriptLogger.d.ts +14 -0
- package/dist/core/hooks/transcriptLogger.js +66 -0
- package/dist/core/interaction.d.ts +29 -0
- package/dist/core/interaction.js +7 -0
- package/dist/core/knowledge.d.ts +15 -0
- package/dist/core/knowledge.js +54 -0
- package/dist/core/mcpPermissions.d.ts +23 -0
- package/dist/core/mcpPermissions.js +47 -0
- package/dist/core/promptTemplate.d.ts +6 -0
- package/dist/core/promptTemplate.js +20 -0
- package/dist/core/runner.d.ts +137 -0
- package/dist/core/runner.js +115 -0
- package/dist/core/session.d.ts +63 -0
- package/dist/core/session.js +222 -0
- package/dist/core/toolLabels.d.ts +23 -0
- package/dist/core/toolLabels.js +114 -0
- package/dist/core/tools/humanApproval.d.ts +14 -0
- package/dist/core/tools/humanApproval.js +37 -0
- package/dist/core/tools/manualLogin.d.ts +16 -0
- package/dist/core/tools/manualLogin.js +27 -0
- package/dist/core/tools/saveToSources.d.ts +10 -0
- package/dist/core/tools/saveToSources.js +105 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +32 -0
- package/dist/tui/chatTui.d.ts +105 -0
- package/dist/tui/chatTui.js +265 -0
- package/dist/tui/claudeAuth.d.ts +19 -0
- package/dist/tui/claudeAuth.js +96 -0
- package/dist/tui/consoleRenderer.d.ts +32 -0
- package/dist/tui/consoleRenderer.js +72 -0
- package/dist/tui/ink/PromptInput.d.ts +40 -0
- package/dist/tui/ink/PromptInput.js +333 -0
- package/dist/tui/ink/SessionView.d.ts +42 -0
- package/dist/tui/ink/SessionView.js +223 -0
- package/dist/tui/ink/fileMentions.d.ts +13 -0
- package/dist/tui/ink/fileMentions.js +50 -0
- package/dist/tui/ink/fullscreen.d.ts +79 -0
- package/dist/tui/ink/fullscreen.js +175 -0
- package/dist/tui/ink/header.d.ts +17 -0
- package/dist/tui/ink/header.js +24 -0
- package/dist/tui/ink/inkInteraction.d.ts +22 -0
- package/dist/tui/ink/inkInteraction.js +64 -0
- package/dist/tui/ink/lineBuffer.d.ts +28 -0
- package/dist/tui/ink/lineBuffer.js +87 -0
- package/dist/tui/ink/markdown.d.ts +12 -0
- package/dist/tui/ink/markdown.js +148 -0
- package/dist/tui/ink/progressView.d.ts +27 -0
- package/dist/tui/ink/progressView.js +65 -0
- package/dist/tui/ink/promptEditing.d.ts +54 -0
- package/dist/tui/ink/promptEditing.js +123 -0
- package/dist/tui/ink/runChatInk.d.ts +103 -0
- package/dist/tui/ink/runChatInk.js +298 -0
- package/dist/tui/ink/sessionModel.d.ts +85 -0
- package/dist/tui/ink/sessionModel.js +262 -0
- package/dist/tui/ink/terminalCursor.d.ts +32 -0
- package/dist/tui/ink/terminalCursor.js +68 -0
- package/dist/tui/ink/terminalStatus.d.ts +20 -0
- package/dist/tui/ink/terminalStatus.js +55 -0
- package/dist/tui/ink/toolGroup.d.ts +36 -0
- package/dist/tui/ink/toolGroup.js +77 -0
- package/dist/tui/ink/wizard.d.ts +62 -0
- package/dist/tui/ink/wizard.js +167 -0
- package/dist/tui/promptErrors.d.ts +2 -0
- package/dist/tui/promptErrors.js +4 -0
- package/dist/tui/terminalInteraction.d.ts +18 -0
- package/dist/tui/terminalInteraction.js +85 -0
- package/dist/tui/ui.d.ts +21 -0
- package/dist/tui/ui.js +25 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Francisco Vargas Ruiz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# agent-kit
|
|
2
|
+
|
|
3
|
+
A foundation for building AI agents on top of the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk). It isn't an agent itself: it solves once what every agent needs (human oversight, safety, memory and a good terminal interface), so each concrete agent only has to write what belongs to its own domain: what it knows how to do, with which tools, and how it talks.
|
|
4
|
+
|
|
5
|
+
> **Status:** version `0.x`, still evolving. MIT license.
|
|
6
|
+
|
|
7
|
+
## What an agent built with it can do
|
|
8
|
+
|
|
9
|
+
- **Work at different levels of oversight.** Autonomous, guided (it asks for permission before anything that can't be undone or that other people will see) or step by step (it asks before every action). Within the same conversation you can switch from guided to step by step and back.
|
|
10
|
+
- **Ask a person for help.** It asks for approvals, or stops so someone can do something by hand, such as signing in to a website. It can be answered from the keyboard or from another program driving it, for example a desktop app.
|
|
11
|
+
- **Remember between sessions.** It keeps its own knowledge base, a small wiki of notes that grows with every session, and keeps the original documents you give it or that it downloads untouched.
|
|
12
|
+
- **Delegate to subagents safely.** Only to the ones it declares, always waiting for their result, and without the main agent being able to slip past its own limits through them.
|
|
13
|
+
- **Stay within limits.** It only reads and writes in the folders that belong to it, and never in protected files, such as one holding passwords.
|
|
14
|
+
- **Use external tools.** MCP servers of its own or of the project, and skills and commands organized in plugins.
|
|
15
|
+
- **Leave a trail.** It records every action and the whole conversation, with secrets hidden.
|
|
16
|
+
- **Chat in a terminal the way Claude Code does.** Full screen, with the reply written live and its markdown already formatted, the tools it uses summed up in one line (Ctrl+O unfolds it), panels for approvals, history and search, a suggestion for the next message, pasted blocks, file mentions with `@`, mouse selection (right-click to copy), and the agent's status in the tab and in the Windows taskbar.
|
|
17
|
+
- **Also work without a terminal.** The same foundation runs inside a desktop app or a server, which bring their own interface.
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Captain Whiskers
|
|
22
|
+
|
|
23
|
+
[Captain Whiskers](./examples/captain-whiskers) is a toy agent to see the kit in action: a retired pirate cat who tells jokes in the terminal, in Spanish. It starts full screen with its logo and has a small crew of subagents: one looks for new jokes on the web, another rates them before they're told, and a third, optional one tells the time. It lets you try the different levels of oversight and is the best starting point for an agent of your own. Its README explains how to run it.
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
### teacher-agent
|
|
28
|
+
|
|
29
|
+
[teacher-agent](https://github.com/falkenslab/teacher-agent) is a real assistant for Moodle teachers. It signs in with your account in a Chrome window and works as you would: it grades submissions, answers in the forum, creates or reviews content and sums up how the class is going. It asks for your permission before publishing anything students will see, and takes notes about the course to remember everything in the next session.
|
|
30
|
+
|
|
31
|
+
Oversight, memory, safety and the chat come from the kit; teacher-agent brings what it knows about Moodle and driving the browser.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
npm install @falkenslab/agent-kit
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To try changes to the kit without publishing them, point to a local clone with `"file:../agent-kit"` in your `package.json` and build it with `npm run build` after each change.
|
|
40
|
+
|
|
41
|
+
Requires Node.js 20 or later.
|
|
42
|
+
|
|
43
|
+
## A minimal agent
|
|
44
|
+
|
|
45
|
+
An agent only describes what belongs to it (who it is, which MCP servers, skills and subagents it has) and picks a level of oversight; the kit sets up the session and the chat:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { mkdir } from "node:fs/promises";
|
|
49
|
+
import path from "node:path";
|
|
50
|
+
import { buildSessionOptions, ensureClaudeAuth, runChatInk, type AgentSpec, type BaseSessionConfig } from "@falkenslab/agent-kit";
|
|
51
|
+
|
|
52
|
+
// The only part specific to the agent: who it is and what it has.
|
|
53
|
+
const spec: AgentSpec<BaseSessionConfig> = {
|
|
54
|
+
buildSystemPrompt: () => "You are a pirate cat who tells jokes. Always answer in character.",
|
|
55
|
+
buildMcpServers: () => ({}), // its own MCP servers, if any
|
|
56
|
+
pluginRoots: () => [], // folders with skills and commands
|
|
57
|
+
buildSubagents: () => undefined, // subagents, if it delegates to any
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
await ensureClaudeAuth(); // offers to create a Claude token if it finds none
|
|
61
|
+
|
|
62
|
+
// Each session keeps its logs in a folder of its own.
|
|
63
|
+
const runDir = path.resolve(".run", new Date().toISOString().replace(/[:.]/g, "-"));
|
|
64
|
+
await mkdir(runDir, { recursive: true });
|
|
65
|
+
|
|
66
|
+
const config: BaseSessionConfig = { mode: "guided", projectDir: process.cwd() };
|
|
67
|
+
const { options, modeControl } = await buildSessionOptions(config, runDir, spec);
|
|
68
|
+
|
|
69
|
+
await runChatInk(options, {
|
|
70
|
+
header: { title: "My agent" },
|
|
71
|
+
mode: config.mode,
|
|
72
|
+
modeControl, // Shift+Tab switches between guided and step by step
|
|
73
|
+
fullscreen: true,
|
|
74
|
+
sessionLogPath: path.join(runDir, "session.log"),
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Save it as `agent.ts` in a project with the kit and `tsx` installed and `"type": "module"` in its `package.json`, and run it with `npx tsx agent.ts`. Captain Whiskers is this same skeleton with more pieces: skills, subagents and its logo.
|
|
79
|
+
|
|
80
|
+
## More details
|
|
81
|
+
|
|
82
|
+
The specification in [`.minispec/`](./.minispec/README.md) explains how each part is built and why, including the SDK behaviors checked by hand that motivate several of its safeguards. Captain Whiskers' code is the quickest way to see how an agent is put together.
|
|
83
|
+
|
|
84
|
+
## Development
|
|
85
|
+
|
|
86
|
+
| Command | What it does |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `npm run build` | Builds the kit into `dist/` |
|
|
89
|
+
| `npm run typecheck` | Checks the types without building |
|
|
90
|
+
| `npm run lint` | Checks the code style |
|
|
91
|
+
| `npm test` | Runs all the tests |
|
|
92
|
+
|
|
93
|
+
To run a single test file:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
npx tsx --test test/tui/promptErrors.test.ts
|
|
97
|
+
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Ingest material into the knowledge base right now - the files or topic given, or everything in sources/ not ingested yet
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Apply the `knowledge-ingest` skill right now to: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
If nothing is given above, find every file in the sources folder that has no summary page in the knowledge base yet (check `index.md` and `summaries/`) and ingest them all. When you're done, summarize which pages you created and updated, and anything you couldn't read.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Health-check the knowledge base right now - broken links, orphans, duplicates, contradictions, gaps
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Apply the `knowledge-lint` skill right now over the whole knowledge base. Fix what's mechanical (links, index, missing back-links) and report the rest: what needs the human's decision, and the gaps worth filling next.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knowledge-ingest
|
|
3
|
+
description: Ingest one source into the knowledge base - a file in sources/, a document, a page - creating its summary page, creating or updating the concept and entity pages it touches, linking everything both ways, and updating index.md and log.md.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ingesting a source into the knowledge base
|
|
7
|
+
|
|
8
|
+
A source is ingested once, and from then on the knowledge base holds what it taught: later sessions read the pages, not the original again. Ingesting is not summarizing a file in isolation - its value is in how it connects to what the knowledge base already knows. Load the `knowledge-pages` skill first for the page templates.
|
|
9
|
+
|
|
10
|
+
## 1. Check it isn't already ingested
|
|
11
|
+
|
|
12
|
+
Look for it in `index.md` (Summaries) or `Grep` its file name or URL inside the knowledge base. If it already has a page:
|
|
13
|
+
- unchanged source: nothing to do;
|
|
14
|
+
- the source changed or the page is thin: update that page instead of creating another.
|
|
15
|
+
|
|
16
|
+
## 2. Read it for real
|
|
17
|
+
|
|
18
|
+
Read the whole source, not its first page. If it exists only outside the sources folder (a downloaded document, a remote file), save it with `save_to_sources` first, so the original is kept as obtained.
|
|
19
|
+
|
|
20
|
+
## 3. Write the summary page
|
|
21
|
+
|
|
22
|
+
Create `summaries/<slug>.md` with the template: summary, key points (literal wording where exactness matters), frontmatter `file`/`url` pointing to the original.
|
|
23
|
+
|
|
24
|
+
## 4. Update concepts and entities
|
|
25
|
+
|
|
26
|
+
For every concept or entity the source explains or relies on:
|
|
27
|
+
- no page yet: create it;
|
|
28
|
+
- page exists: add what is new (a better definition, an example, a nuance, a fact) with `Edit`, and link the summary under "Sources".
|
|
29
|
+
|
|
30
|
+
Check first whether it exists under another name (`Grep` the knowledge base for the term and look at `aliases`): a duplicate page is worse than none.
|
|
31
|
+
|
|
32
|
+
## 5. Link both ways and look for conflicts
|
|
33
|
+
|
|
34
|
+
Every link you added from page A to page B needs its counterpart in B. If the source contradicts an existing page, record both versions with attribution in both pages - don't overwrite.
|
|
35
|
+
|
|
36
|
+
## 6. Close
|
|
37
|
+
|
|
38
|
+
- `index.md`: new pages, and one-line descriptions that changed.
|
|
39
|
+
- `log.md`: `## [date] ingest | <source title>` with the pages created and updated.
|
|
40
|
+
- `overview.md`: only if the source changes the big picture.
|
|
41
|
+
|
|
42
|
+
When ingesting several sources in a row, do steps 1-5 for each and close once at the end.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knowledge-lint
|
|
3
|
+
description: Health-check the knowledge base - broken links, pages missing from index.md, orphan pages, one-way links, duplicated concepts, mentioned-but-missing concepts, contradictions, gaps - fix what's mechanical and report the rest.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Linting the knowledge base
|
|
7
|
+
|
|
8
|
+
A knowledge base maintained across sessions drifts: links break, pages go unlisted, the same concept appears under two names. Lint it when asked and after a large ingest. Load `knowledge-pages` first for the templates.
|
|
9
|
+
|
|
10
|
+
## Checks
|
|
11
|
+
|
|
12
|
+
Use `Glob` to list pages and `Grep` (always with `path` inside the knowledge base) to find links, instead of reading every page in full.
|
|
13
|
+
|
|
14
|
+
1. **Broken links**: a relative link whose target file doesn't exist. Fix it if the right target is obvious (a typo, a page under a close name); otherwise report it.
|
|
15
|
+
2. **Index**: every page is listed in `index.md` and every entry there points to an existing page. Fix it.
|
|
16
|
+
3. **Orphans**: pages nothing links to (`Grep` for the file name). Link them from where they belong, or report why they don't belong anywhere.
|
|
17
|
+
4. **One-way links**: a summary that feeds a concept which doesn't list it back... Add the missing side.
|
|
18
|
+
5. **Duplicates**: two pages for the same idea under different names. Merge the content into one, mark the other as superseded (never delete it) and repoint links.
|
|
19
|
+
6. **Missing pages**: terms that several pages treat as concepts or entities but have no page. Create them if the material is there; otherwise list them.
|
|
20
|
+
7. **Contradictions**: claims that disagree across pages without being recorded as such. Record both sides, attributed.
|
|
21
|
+
8. **Provenance**: claims with no traceable source. Add the link, or mark them as unsourced.
|
|
22
|
+
9. **Gaps**: files in the sources folder with no summary page yet, concepts mentioned but never explained by any source. Report them - they are what to ingest or find next.
|
|
23
|
+
|
|
24
|
+
## Close
|
|
25
|
+
|
|
26
|
+
Append `## [date] lint | <summary>` to `log.md` listing what was fixed. Then report to the human: what you fixed, and what needs their decision or more material. Don't make judgement calls silently - merging pages that are only similar, or choosing between contradictory sources, goes in the report.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knowledge-pages
|
|
3
|
+
description: Exact templates and conventions for every page of the knowledge base (index, log, overview, summary, concept, entity and synthesis pages) - load it before creating a knowledge base page or restructuring one, so every page keeps the same shape and links.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Knowledge base page templates
|
|
7
|
+
|
|
8
|
+
The knowledge base's layout and rules are in the system prompt ("Knowledge base"). This skill only adds the exact shape of each page. Headings and frontmatter keys stay in English (they are structure); the text under them is in the human's language. Dates are `YYYY-MM-DD`. Links are relative markdown links (from `concepts/x.md`, a summary is `../summaries/y.md`; an original file is `../../sources/...`, adjusted to where the folders really are).
|
|
9
|
+
|
|
10
|
+
Every page starts with YAML frontmatter; `updated` changes whenever you edit the page.
|
|
11
|
+
|
|
12
|
+
## index.md
|
|
13
|
+
|
|
14
|
+
The catalog and entry point. One line per page, grouped by section: Summaries, Concepts, Entities, Syntheses (plus any section the domain adds).
|
|
15
|
+
|
|
16
|
+
```markdown
|
|
17
|
+
# Index
|
|
18
|
+
|
|
19
|
+
## Summaries
|
|
20
|
+
- [Q3 architecture review](summaries/q3-architecture-review.md) - decisions taken on the billing service
|
|
21
|
+
## Concepts
|
|
22
|
+
- [Idempotency](concepts/idempotency.md) - safe retries of a request
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## log.md
|
|
26
|
+
|
|
27
|
+
Append-only; never edit past entries. Newest at the bottom.
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
## [2026-10-03] ingest | Q3 architecture review
|
|
31
|
+
- created: summaries/q3-architecture-review.md, concepts/idempotency.md
|
|
32
|
+
- updated: entities/billing-service.md, index.md
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## overview.md
|
|
36
|
+
|
|
37
|
+
A living synthesis of the whole knowledge base, rewritten (not appended) as understanding grows: what it covers, its main blocks and how they connect (with links), what is still unknown.
|
|
38
|
+
|
|
39
|
+
## Summary page (`summaries/<slug>.md`)
|
|
40
|
+
|
|
41
|
+
One per ingested source.
|
|
42
|
+
|
|
43
|
+
```markdown
|
|
44
|
+
---
|
|
45
|
+
type: summary
|
|
46
|
+
title: "<Title>"
|
|
47
|
+
file: <relative link to the original in sources/, if any>
|
|
48
|
+
url: <original URL, if any>
|
|
49
|
+
ingested: YYYY-MM-DD
|
|
50
|
+
updated: YYYY-MM-DD
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# <Title>
|
|
54
|
+
|
|
55
|
+
## Summary
|
|
56
|
+
<Two to four paragraphs: what it actually says.>
|
|
57
|
+
|
|
58
|
+
## Key points
|
|
59
|
+
- Claims, definitions or figures likely to be needed later, quoted literally when exact wording matters.
|
|
60
|
+
|
|
61
|
+
## Pages it feeds
|
|
62
|
+
- [Concept or entity](../concepts/<slug>.md)
|
|
63
|
+
|
|
64
|
+
## New or surprising
|
|
65
|
+
- What this source adds to what the knowledge base already had.
|
|
66
|
+
|
|
67
|
+
## Contradictions
|
|
68
|
+
- Conflicts with other pages, linked.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Concept page (`concepts/<slug>.md`)
|
|
72
|
+
|
|
73
|
+
One idea per page.
|
|
74
|
+
|
|
75
|
+
```markdown
|
|
76
|
+
---
|
|
77
|
+
type: concept
|
|
78
|
+
aliases: []
|
|
79
|
+
updated: YYYY-MM-DD
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
# <Concept>
|
|
83
|
+
|
|
84
|
+
<Definition in one or two sentences.>
|
|
85
|
+
|
|
86
|
+
## Explanation
|
|
87
|
+
<How it works, at the depth the sources give.>
|
|
88
|
+
|
|
89
|
+
## Connections
|
|
90
|
+
- Requires: [Concept](<slug>.md)
|
|
91
|
+
- Related: [Concept](<slug>.md) - how
|
|
92
|
+
|
|
93
|
+
## Sources
|
|
94
|
+
- [Summary](../summaries/<slug>.md) - what it says about this
|
|
95
|
+
|
|
96
|
+
## Contradictions and open questions
|
|
97
|
+
- "<Source A> says X; <source B> says Y" - both attributed.
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Entity page (`entities/<slug>.md`)
|
|
101
|
+
|
|
102
|
+
A concrete thing: a system, a component, an organization, a document, a person only when they are a public role rather than private individual details.
|
|
103
|
+
|
|
104
|
+
```markdown
|
|
105
|
+
---
|
|
106
|
+
type: entity
|
|
107
|
+
kind: <system | component | organization | document | other>
|
|
108
|
+
aliases: []
|
|
109
|
+
updated: YYYY-MM-DD
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
# <Name>
|
|
113
|
+
|
|
114
|
+
<What it is, one or two sentences.>
|
|
115
|
+
|
|
116
|
+
## Facts
|
|
117
|
+
- Attributed facts, each linking the summary or source it comes from.
|
|
118
|
+
|
|
119
|
+
## Connections
|
|
120
|
+
- [Concept or entity](../concepts/<slug>.md) - how
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Synthesis page (`syntheses/<slug>.md`)
|
|
124
|
+
|
|
125
|
+
An answer worth keeping: a comparison, an analysis, a report.
|
|
126
|
+
|
|
127
|
+
```markdown
|
|
128
|
+
---
|
|
129
|
+
type: synthesis
|
|
130
|
+
question: "<The question it answers>"
|
|
131
|
+
updated: YYYY-MM-DD
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
# <Title>
|
|
135
|
+
|
|
136
|
+
<The answer, with links to every page it draws on.>
|
|
137
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knowledge-query
|
|
3
|
+
description: Answer a question from the knowledge base - find the relevant pages through the index, read them, answer with links to them, and file the answer back as a synthesis page when it is worth keeping.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Querying the knowledge base
|
|
7
|
+
|
|
8
|
+
The knowledge base exists so a question doesn't start from zero. Answer from it first; go back to the originals or the web only for what it doesn't hold.
|
|
9
|
+
|
|
10
|
+
1. **Start from the index.** Read `index.md`, pick the pages that can matter, read those (not the whole knowledge base). `Grep` inside the knowledge base for terms and aliases the index doesn't reveal.
|
|
11
|
+
2. **Answer from the pages**, linking each one you drew on. Say what comes from the knowledge base, what comes from an original in the sources folder (read it if the knowledge base is thin or contradictory) and what comes from outside (label it as such).
|
|
12
|
+
3. **Say what is missing.** If the knowledge base can't answer part of the question, say so and name the source that would fill it, instead of filling the gap from memory silently.
|
|
13
|
+
4. **File it back.** If the answer is a comparison, an analysis or a report that will be asked again, save it as `syntheses/<slug>.md` (template in `knowledge-pages`) with links to its inputs, add it to `index.md`, and log it: `## [date] query | <question>`. A quick factual answer needs no page.
|
|
14
|
+
5. **Feed the knowledge base.** If answering revealed a concept or fact that has no page, or a contradiction, fix that in the knowledge base too and log it.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { AgentDefinition as SdkSubagentDefinition, McpServerConfig } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* The human-supervision spectrum every agent built on this kit shares, independent of
|
|
4
|
+
* domain: "interactive" pauses before every single action, "guided" only pauses before a
|
|
5
|
+
* hard-to-undo/visible-to-others action, "autonomous" has no human-in-the-loop channel at
|
|
6
|
+
* all. See session.ts's buildSessionOptions() for exactly what each mode changes.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately orthogonal to whether the session is a one-shot run or a multi-turn chat
|
|
9
|
+
* (that's a matter of which entry point the caller uses — a single `query()` call vs.
|
|
10
|
+
* `runChatTui()`/`createInputQueue()` — not a supervision level): a concrete agent that
|
|
11
|
+
* wants to know "is this session chat-shaped" for its own purposes (e.g. picking a
|
|
12
|
+
* different system-prompt template) should track that as its own domain field on its
|
|
13
|
+
* config type, not conflate it with `Mode`.
|
|
14
|
+
*/
|
|
15
|
+
export type Mode = "interactive" | "guided" | "autonomous";
|
|
16
|
+
/**
|
|
17
|
+
* The minimum a config object needs to drive `buildSessionOptions()` — a concrete agent's
|
|
18
|
+
* own config type (e.g. a `Config`) extends this with whatever domain fields
|
|
19
|
+
* it needs (a course URL, credentials, ...), which `buildSessionOptions()` itself never
|
|
20
|
+
* looks at directly: only `AgentSpec`'s methods receive the full concrete config.
|
|
21
|
+
*/
|
|
22
|
+
export interface BaseSessionConfig {
|
|
23
|
+
mode: Mode;
|
|
24
|
+
/** Project root (the session's cwd): the folder under which the agent's notes, sources and runs live. */
|
|
25
|
+
projectDir: string;
|
|
26
|
+
/**
|
|
27
|
+
* Where the agent writes its own notes (the "wiki"). Setting this or `sourcesDir` is what
|
|
28
|
+
* gives the session the file tools (Read/Write/Edit/Glob/Grep, scoped — see
|
|
29
|
+
* hooks/fileScopeGate.ts); without either, the agent has no file access at all.
|
|
30
|
+
*/
|
|
31
|
+
knowledgeDir?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Original files, kept as obtained and apart from the notes in `knowledgeDir`: material
|
|
34
|
+
* the user drops in, plus whatever the agent saves with `save_to_sources` (downloaded
|
|
35
|
+
* documents, transcripts...). The agent can read and search it but never edit it — Write/
|
|
36
|
+
* Edit are scoped to `knowledgeDir` — and `save_to_sources` never overwrites.
|
|
37
|
+
*/
|
|
38
|
+
sourcesDir?: string;
|
|
39
|
+
/** Directories besides `knowledgeDir`/`sourcesDir` where Write/Edit are allowed (see hooks/fileScopeGate.ts). */
|
|
40
|
+
extraWritableDirs?: string[];
|
|
41
|
+
/** Files or directories the agent must never read, search or write, e.g. a config file holding a password (see hooks/fileScopeGate.ts). */
|
|
42
|
+
deniedPaths?: string[];
|
|
43
|
+
/** Extra values (e.g. a password) to scrub out of the transcript log — see hooks/transcriptLogger.ts. */
|
|
44
|
+
secrets?: string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Everything actually *about the domain* that `buildSessionOptions()` needs but doesn't
|
|
48
|
+
* decide itself: which system prompt to build, which MCP servers to talk to besides the
|
|
49
|
+
* generic human-in-the-loop/knowledge ones this kit already wires up, which local plugin
|
|
50
|
+
* roots (skills/commands) to load, and which opt-in subagents (if any) to register.
|
|
51
|
+
*
|
|
52
|
+
* Deliberately not named `AgentDefinition`: the SDK already uses that name for a single
|
|
53
|
+
* subagent's own definition (`Options.agents: Record<string, AgentDefinition>`), and this
|
|
54
|
+
* is a level above that — it's what decides *which* subagents get registered, among other
|
|
55
|
+
* things.
|
|
56
|
+
*/
|
|
57
|
+
export interface AgentSpec<TConfig extends BaseSessionConfig> {
|
|
58
|
+
/** The system prompt for this run — everything role/mode/domain-specific lives behind this call. */
|
|
59
|
+
buildSystemPrompt(config: TConfig): string;
|
|
60
|
+
/**
|
|
61
|
+
* MCP servers this agent always registers (e.g. Playwright for a browser-driving
|
|
62
|
+
* agent), on top of the generic ones `buildSessionOptions()` wires up itself when
|
|
63
|
+
* applicable (human approval, manual intervention, save-to-knowledge) — those aren't
|
|
64
|
+
* part of the spec because they're driven by `mode` plus this spec's own
|
|
65
|
+
* `manualInterventionTexts`/`knowledgeDir`/`sourcesDir`, not by anything else
|
|
66
|
+
* domain-specific.
|
|
67
|
+
*/
|
|
68
|
+
buildMcpServers(config: TConfig, runDir: string): Record<string, McpServerConfig>;
|
|
69
|
+
/**
|
|
70
|
+
* Local plugin roots (skills/commands, SDK "local" plugin type) to load, in the order
|
|
71
|
+
* given, when the session has file tools (`config.knowledgeDir`/`config.sourcesDir`). Absolute paths; `buildSessionOptions()`
|
|
72
|
+
* doesn't know or care where they live on disk.
|
|
73
|
+
*/
|
|
74
|
+
pluginRoots(config: TConfig): string[];
|
|
75
|
+
/**
|
|
76
|
+
* Opt-in subagents (the SDK's `Options.agents`) this spec wants registered for this
|
|
77
|
+
* config, plus which `subagent_type` values are therefore allowed to be spawned via the
|
|
78
|
+
* `Agent` tool (see hooks/subagentTypeGate.ts — the SDK's own built-in
|
|
79
|
+
* "general-purpose" type is always spawnable regardless of this list, which is exactly
|
|
80
|
+
* why that gate exists). Returns `undefined` when this config needs no subagents at
|
|
81
|
+
* all, so `buildSessionOptions()` knows not to grant `Agent`/`Bash` in the first place.
|
|
82
|
+
*/
|
|
83
|
+
buildSubagents(config: TConfig): {
|
|
84
|
+
agents: Record<string, SdkSubagentDefinition>;
|
|
85
|
+
allowedSubagentTypes: string[];
|
|
86
|
+
} | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Tool names to explicitly block regardless of what `canUseTool`/`allowAnyMcpTool`
|
|
89
|
+
* would otherwise allow (e.g. a browser-automation agent blocking
|
|
90
|
+
* "mcp__playwright__browser_run_code_unsafe", which Microsoft's own description calls
|
|
91
|
+
* RCE-equivalent). Confirmed empirically that `disallowedTools` takes full precedence
|
|
92
|
+
* over a permissive `canUseTool` — a disallowed tool never even reaches that callback.
|
|
93
|
+
*/
|
|
94
|
+
disallowedTools?: string[];
|
|
95
|
+
/** Overrides this kit's generic `save_to_sources` tool description (registered whenever `config.sourcesDir` is set) with domain-specific wording. */
|
|
96
|
+
saveToSourcesDescription?: string;
|
|
97
|
+
/**
|
|
98
|
+
* The built-in knowledge base (see knowledge.ts): when `config.knowledgeDir` is set, the kit
|
|
99
|
+
* appends its "Knowledge base" rules to the system prompt and loads its plugin (skills
|
|
100
|
+
* knowledge-pages/knowledge-ingest/knowledge-query/knowledge-lint, commands /knowledge:ingest, /knowledge:query,
|
|
101
|
+
* /knowledge:lint), so the agent maintains its notes as an interlinked wiki. On by default;
|
|
102
|
+
* set `false` for an agent that writes its own rules for `knowledgeDir` (or wants plain
|
|
103
|
+
* notes) — `knowledgePromptSection()`/`knowledgePluginRoot()` are exported to reuse the pieces.
|
|
104
|
+
*/
|
|
105
|
+
knowledgeBase?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Text for the generic human-approval checkpoint (see tools/humanApproval.ts) — what
|
|
108
|
+
* counts as "publishing something visible to others" is entirely domain-specific.
|
|
109
|
+
* Omit to use this kit's own generic, domain-neutral defaults.
|
|
110
|
+
*/
|
|
111
|
+
humanApprovalTexts?: {
|
|
112
|
+
description: string;
|
|
113
|
+
approved: string;
|
|
114
|
+
rejected: string;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Text for the generic manual-intervention checkpoint (see tools/manualLogin.ts), AND
|
|
118
|
+
* this domain's opt-in signal for offering that checkpoint at all: `buildSessionOptions()`
|
|
119
|
+
* only registers `request_manual_login` when this is set (and `mode !== "autonomous"`).
|
|
120
|
+
* Manual intervention only makes sense for a domain that drives some live UI a human could
|
|
121
|
+
* actually step into by hand (a browser window, say) — there's no generic default to fall
|
|
122
|
+
* back to the way there is for `humanApprovalTexts` above, because this kit can't assume
|
|
123
|
+
* every agent built on it has such a UI at all. Leave unset for a domain that doesn't.
|
|
124
|
+
*/
|
|
125
|
+
manualInterventionTexts?: {
|
|
126
|
+
toolDescription: string;
|
|
127
|
+
confirmedMessage: string;
|
|
128
|
+
checkpointTitle: string;
|
|
129
|
+
checkpointLines: string[];
|
|
130
|
+
checkpointQuestion?: string;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ClaudeAuthConfig {
|
|
2
|
+
/** A previously-resolved token the caller already has (from wherever it stores one, if
|
|
3
|
+
* anywhere) — this module owns no persistence of its own, unlike an earlier version
|
|
4
|
+
* that read/wrote `~/.<appName>/config.json` itself (see git history). Sourcing this
|
|
5
|
+
* value across runs, if a concrete agent wants that at all, is entirely its own concern. */
|
|
6
|
+
claudeCodeOAuthToken?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Pure lookup, no I/O and no console output of any kind — safe to call from a
|
|
10
|
+
* headless/non-interactive consumer (a server, an Electron main process) as much as from a
|
|
11
|
+
* terminal one.
|
|
12
|
+
*
|
|
13
|
+
* Checks, in order: `ANTHROPIC_API_KEY` (a complete authentication path on its own — API
|
|
14
|
+
* billing instead of subscription, so nothing else is checked when it's set),
|
|
15
|
+
* `CLAUDE_CODE_OAUTH_TOKEN`, then `config.claudeCodeOAuthToken` if the caller passed one.
|
|
16
|
+
* Returns `true` and (for the `config` case) sets `CLAUDE_CODE_OAUTH_TOKEN` on the process
|
|
17
|
+
* as a side effect once a token is found — the SDK's own subprocess reads that env var
|
|
18
|
+
* directly, never anything this kit passes it explicitly; `false` when none of the three
|
|
19
|
+
* has one. Getting a token in that case (e.g. by walking the user through
|
|
20
|
+
* `claude setup-token`) is a terminal-UX concern, not this function's — see
|
|
21
|
+
* `ensureClaudeAuth()` in tui/claudeAuth.ts, which calls this first and only falls back to
|
|
22
|
+
* that interactive flow when it returns `false`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveClaudeAuth(config?: ClaudeAuthConfig): boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure lookup, no I/O and no console output of any kind — safe to call from a
|
|
3
|
+
* headless/non-interactive consumer (a server, an Electron main process) as much as from a
|
|
4
|
+
* terminal one.
|
|
5
|
+
*
|
|
6
|
+
* Checks, in order: `ANTHROPIC_API_KEY` (a complete authentication path on its own — API
|
|
7
|
+
* billing instead of subscription, so nothing else is checked when it's set),
|
|
8
|
+
* `CLAUDE_CODE_OAUTH_TOKEN`, then `config.claudeCodeOAuthToken` if the caller passed one.
|
|
9
|
+
* Returns `true` and (for the `config` case) sets `CLAUDE_CODE_OAUTH_TOKEN` on the process
|
|
10
|
+
* as a side effect once a token is found — the SDK's own subprocess reads that env var
|
|
11
|
+
* directly, never anything this kit passes it explicitly; `false` when none of the three
|
|
12
|
+
* has one. Getting a token in that case (e.g. by walking the user through
|
|
13
|
+
* `claude setup-token`) is a terminal-UX concern, not this function's — see
|
|
14
|
+
* `ensureClaudeAuth()` in tui/claudeAuth.ts, which calls this first and only falls back to
|
|
15
|
+
* that interactive flow when it returns `false`.
|
|
16
|
+
*/
|
|
17
|
+
export function resolveClaudeAuth(config = {}) {
|
|
18
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
19
|
+
return true;
|
|
20
|
+
if (process.env.CLAUDE_CODE_OAUTH_TOKEN)
|
|
21
|
+
return true;
|
|
22
|
+
if (config.claudeCodeOAuthToken) {
|
|
23
|
+
process.env.CLAUDE_CODE_OAUTH_TOKEN = config.claudeCodeOAuthToken;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* Where the built-in file tools may act. Without this, the only boundary is the SDK's own
|
|
4
|
+
* working-directory scope — the whole project directory — so keeping the agent out of the
|
|
5
|
+
* user's own files (a config file holding a password, the originals in sources/) would rest on the system
|
|
6
|
+
* prompt alone.
|
|
7
|
+
*/
|
|
8
|
+
export interface FileScope {
|
|
9
|
+
/** Base for relative paths: the session's cwd. */
|
|
10
|
+
projectDir: string;
|
|
11
|
+
/** Write/Edit are only allowed inside these. */
|
|
12
|
+
writableDirs: string[];
|
|
13
|
+
/** Readable and searchable but never writable; only affects the wording of the denial. */
|
|
14
|
+
readOnlyDirs?: string[];
|
|
15
|
+
/** Grep is only allowed inside these — it prints file contents, so it isn't let loose on the whole project. */
|
|
16
|
+
searchableDirs: string[];
|
|
17
|
+
/** Never readable, searchable or writable. */
|
|
18
|
+
deniedPaths: string[];
|
|
19
|
+
}
|
|
20
|
+
/** Whether `candidate` is `base` itself or somewhere inside it (case-insensitive on Windows, like path.relative). */
|
|
21
|
+
export declare function isWithin(base: string, candidate: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* The reason to deny `toolName` with `input` under `scope`, or `undefined` to let it
|
|
24
|
+
* through. Tools other than Read/Write/Edit/Grep are never judged here, and a call
|
|
25
|
+
* without a path is left for the tool itself to reject.
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkFileScope(scope: FileScope, toolName: string, input: Record<string, unknown>): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* PreToolUse hook enforcing `scope` on Read/Write/Edit/Grep, for the main agent and its
|
|
30
|
+
* subagents alike. Bash, when a subagent has it, is not covered: it can reach any path,
|
|
31
|
+
* which is why Bash-granting features stay opt-in.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createFileScopeGate(scope: FileScope): HookCallback;
|