@falkenslab/agent-kit 0.14.0 → 0.16.0

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.
Files changed (70) hide show
  1. package/README.md +110 -110
  2. package/assets/knowledge-plugin/.claude-plugin/plugin.json +1 -1
  3. package/assets/knowledge-plugin/commands/ingest.md +1 -1
  4. package/assets/knowledge-plugin/commands/lint.md +1 -1
  5. package/assets/knowledge-plugin/skills/knowledge-ingest/SKILL.md +13 -19
  6. package/assets/knowledge-plugin/skills/knowledge-lint/SKILL.md +18 -14
  7. package/assets/knowledge-plugin/skills/knowledge-query/SKILL.md +5 -5
  8. package/assets/knowledge-plugin-files/.claude-plugin/plugin.json +4 -0
  9. package/assets/knowledge-plugin-files/commands/ingest.md +7 -0
  10. package/assets/knowledge-plugin-files/commands/lint.md +5 -0
  11. package/assets/knowledge-plugin-files/commands/query.md +5 -0
  12. package/assets/knowledge-plugin-files/skills/knowledge-ingest/SKILL.md +46 -0
  13. package/assets/knowledge-plugin-files/skills/knowledge-lint/SKILL.md +26 -0
  14. package/assets/knowledge-plugin-files/skills/knowledge-query/SKILL.md +14 -0
  15. package/dist/core/agentSpec.d.ts +27 -2
  16. package/dist/core/extractText.d.ts +13 -0
  17. package/dist/core/extractText.js +147 -0
  18. package/dist/core/fileKnowledgeStore.d.ts +10 -0
  19. package/dist/core/fileKnowledgeStore.js +378 -0
  20. package/dist/core/hooks/fileScopeGate.d.ts +8 -0
  21. package/dist/core/hooks/fileScopeGate.js +20 -0
  22. package/dist/core/hooks/humanInput.d.ts +18 -0
  23. package/dist/core/hooks/humanInput.js +30 -2
  24. package/dist/core/hooks/planGate.js +14 -5
  25. package/dist/core/hooks/stepGate.js +6 -0
  26. package/dist/core/interaction.d.ts +20 -0
  27. package/dist/core/knowledge.d.ts +14 -4
  28. package/dist/core/knowledge.js +36 -6
  29. package/dist/core/knowledgeStore.d.ts +134 -0
  30. package/dist/core/knowledgeStore.js +84 -0
  31. package/dist/core/messages/de.js +65 -0
  32. package/dist/core/messages/en.d.ts +41 -0
  33. package/dist/core/messages/en.js +65 -0
  34. package/dist/core/messages/es.js +65 -0
  35. package/dist/core/messages/fr.js +65 -0
  36. package/dist/core/modeControl.d.ts +36 -0
  37. package/dist/core/modeControl.js +61 -0
  38. package/dist/core/session.d.ts +6 -31
  39. package/dist/core/session.js +76 -71
  40. package/dist/core/sources.d.ts +122 -0
  41. package/dist/core/sources.js +312 -0
  42. package/dist/core/todos.d.ts +23 -0
  43. package/dist/core/todos.js +33 -0
  44. package/dist/core/toolLabels.d.ts +1 -1
  45. package/dist/core/toolLabels.js +42 -2
  46. package/dist/core/tools/humanApproval.d.ts +9 -5
  47. package/dist/core/tools/humanApproval.js +39 -7
  48. package/dist/core/tools/knowledgeTools.d.ts +16 -0
  49. package/dist/core/tools/knowledgeTools.js +161 -0
  50. package/dist/core/tools/saveToSources.d.ts +27 -8
  51. package/dist/core/tools/saveToSources.js +203 -55
  52. package/dist/core/tools/time.d.ts +29 -0
  53. package/dist/core/tools/time.js +129 -0
  54. package/dist/index.d.ts +7 -4
  55. package/dist/index.js +4 -1
  56. package/dist/tui/chatTui.js +14 -1
  57. package/dist/tui/consoleRenderer.js +16 -2
  58. package/dist/tui/ink/SessionView.js +72 -17
  59. package/dist/tui/ink/inkInteraction.d.ts +4 -2
  60. package/dist/tui/ink/inkInteraction.js +20 -2
  61. package/dist/tui/ink/progressView.d.ts +5 -1
  62. package/dist/tui/ink/runChatInk.d.ts +16 -1
  63. package/dist/tui/ink/runChatInk.js +7 -0
  64. package/dist/tui/ink/sessionModel.d.ts +10 -3
  65. package/dist/tui/ink/sessionModel.js +26 -4
  66. package/dist/tui/ink/toolGroup.d.ts +23 -2
  67. package/dist/tui/ink/toolGroup.js +17 -4
  68. package/dist/tui/terminalInteraction.js +2 -0
  69. package/package.json +31 -1
  70. /package/assets/{knowledge-plugin → knowledge-plugin-files}/skills/knowledge-pages/SKILL.md +0 -0
@@ -1,8 +1,17 @@
1
1
  import path from "node:path";
2
- /** Built-in tools that only read, search, load a skill or delegate (a subagent's own calls come through this gate too). */
3
- const READING_TOOLS = new Set(["Read", "Glob", "Grep", "WebFetch", "WebSearch", "Skill", "Agent"]);
4
- /** The kit's own MCP tools that only ask a human: approvals and manual intervention. */
5
- const ASKING_SERVERS = ["mcp__approvals__", "mcp__manualLogin__"];
2
+ /** Built-in tools that only read, search, load a skill, delegate or keep the task list (a subagent's own calls come through this gate too). */
3
+ const READING_TOOLS = new Set(["Read", "Glob", "Grep", "WebFetch", "WebSearch", "Skill", "Agent", "TodoWrite"]);
4
+ /** The kit's own MCP servers whose tools only ask a human (approvals, manual intervention) or only read (the date and time). */
5
+ const ASKING_SERVERS = ["mcp__approvals__", "mcp__manualLogin__", "mcp__time__"];
6
+ /** The kit's own MCP tools that only read, on servers that also have tools that write. */
7
+ const READING_KIT_TOOLS = new Set([
8
+ "mcp__sourceFiles__list_sources",
9
+ "mcp__sourceFiles__extract_text",
10
+ "mcp__knowledge__knowledge_index",
11
+ "mcp__knowledge__knowledge_search",
12
+ "mcp__knowledge__knowledge_read",
13
+ "mcp__knowledge__knowledge_check",
14
+ ]);
6
15
  /** Why a call is denied in plan mode, and what the model should do instead. */
7
16
  const denial = "Plan mode: nothing can be changed until the user leaves plan mode. Keep reading and researching, then present the plan; the user switches out of plan mode when they want it carried out.";
8
17
  /**
@@ -13,7 +22,7 @@ const denial = "Plan mode: nothing can be changed until the user leaves plan mod
13
22
  export function checkPlanScope(scope, toolName, input) {
14
23
  if (READING_TOOLS.has(toolName))
15
24
  return undefined;
16
- if (ASKING_SERVERS.some((prefix) => toolName.startsWith(prefix)))
25
+ if (ASKING_SERVERS.some((prefix) => toolName.startsWith(prefix)) || READING_KIT_TOOLS.has(toolName))
17
26
  return undefined;
18
27
  if (toolName === "Write" || toolName === "Edit") {
19
28
  const target = typeof input.file_path === "string" && input.file_path !== "" ? path.resolve(scope.projectDir, input.file_path) : undefined;
@@ -1,5 +1,7 @@
1
1
  import { askForDecision } from "./humanInput.js";
2
2
  import { t } from "../messages/index.js";
3
+ const SELF_ASKING_SERVERS = ["mcp__approvals__", "mcp__manualLogin__"];
4
+ const SELF_ASKING_TOOLS = new Set(["mcp__sourceFiles__request_file", "mcp__sourceFiles__retire_source", "mcp__knowledge__knowledge_retire", "TodoWrite"]);
3
5
  /**
4
6
  * PreToolUse hook for "interactive" mode: pauses before every action and asks for
5
7
  * confirmation (by keyboard or by file, see humanInput.ts), like reviewing a plan step
@@ -14,6 +16,10 @@ export function createStepGate(runDir, isActive = () => true) {
14
16
  if (!isActive())
15
17
  return {};
16
18
  const pre = input;
19
+ // A tool that asks the person itself (an approval, a question, the plan, a file, retiring
20
+ // a source) or only keeps the task list isn't asked about first: that would be asking to ask.
21
+ if (SELF_ASKING_SERVERS.some((prefix) => pre.tool_name.startsWith(prefix)) || SELF_ASKING_TOOLS.has(pre.tool_name))
22
+ return {};
17
23
  const answer = await askForDecision(runDir, {
18
24
  title: t().proposedAction,
19
25
  lines: [t().toolLine(pre.tool_name), t().parametersLine(JSON.stringify(pre.tool_input, null, 2))],
@@ -4,6 +4,13 @@ export interface ApprovalPrompt {
4
4
  lines: string[];
5
5
  /** Terminal question text; defaults to the approve/reject one. */
6
6
  question?: string;
7
+ /** The lines are markdown (a plan): a UI that can render it should. */
8
+ markdown?: boolean;
9
+ }
10
+ /** How `askChoice()` asks: the options to pick from, and whether several can be picked. */
11
+ export interface ChoiceSettings {
12
+ options: readonly string[];
13
+ multiple: boolean;
7
14
  }
8
15
  /**
9
16
  * The UI side of a human-in-the-loop checkpoint: how a host shows the question and reads a
@@ -23,6 +30,19 @@ export interface InteractionPort {
23
30
  askDecision(prompt: ApprovalPrompt, signal: AbortSignal): Promise<string>;
24
31
  /** Waits until the person confirms they intervened by hand (e.g. logged in). */
25
32
  askManualIntervention(prompt: ApprovalPrompt, signal: AbortSignal): Promise<string>;
33
+ /**
34
+ * A free-text answer (e.g. the path of a file the agent asked for), with case and spaces
35
+ * kept; "" for none. Optional: without it the kit asks through `askDecision()`, whose
36
+ * answer a plain terminal reads as text too.
37
+ */
38
+ askText?(prompt: ApprovalPrompt, signal: AbortSignal): Promise<string>;
39
+ /**
40
+ * A choice between options. Resolves to the raw answer: the chosen options' numbers
41
+ * (1-based, comma-separated, "1" or "1,3") on the first line, and the person's own answer
42
+ * ("Other") on the lines after it, or alone. Optional: without it the kit asks through
43
+ * `askDecision()`, with the options numbered in the prompt.
44
+ */
45
+ askChoice?(prompt: ApprovalPrompt, choice: ChoiceSettings, signal: AbortSignal): Promise<string>;
26
46
  /** A one-way message for the person. */
27
47
  notify(message: string): void;
28
48
  }
@@ -1,3 +1,4 @@
1
+ import type { PageType } from "./knowledgeStore.js";
1
2
  /**
2
3
  * The built-in knowledge base (an "LLM wiki"): `sourcesDir` holds the originals, `knowledgeDir`
3
4
  * is the wiki the agent writes and maintains, and the rules below plus the plugin's skills
@@ -6,10 +7,19 @@
6
7
  * with its own page types (topics, activities...) opts out with `AgentSpec.knowledgeBase: false` and
7
8
  * writes its own rules, or extends these.
8
9
  */
9
- /** Absolute path of the plugin shipped with the kit — `assets/knowledge-plugin` next to `dist/` (or `src/` under tsx). */
10
- export declare function knowledgePluginRoot(): string;
10
+ /**
11
+ * Absolute path of the plugin shipped with the kit, next to `dist/` (or `src/` under tsx):
12
+ * `assets/knowledge-plugin`, whose skills work through the `knowledge_*` tools, or with
13
+ * `"files"` `assets/knowledge-plugin-files`, whose skills work on the files (ADR-024).
14
+ */
15
+ export declare function knowledgePluginRoot(variant?: "tools" | "files"): string;
11
16
  /**
12
17
  * The "Knowledge base" section appended to the system prompt: the layout and the working
13
- * rules. The exact page templates live in the `knowledge-pages` skill, to keep this short.
18
+ * rules. With `tools` (the kit's default since ADR-024), for a knowledge base reached through
19
+ * the `knowledge_*` tools, listing `pageTypes`; otherwise for one kept with the file tools,
20
+ * whose page templates live in the `knowledge-pages` skill.
14
21
  */
15
- export declare function knowledgePromptSection(projectDir: string, knowledgeDir: string, sourcesDir?: string): string;
22
+ export declare function knowledgePromptSection(projectDir: string, knowledgeDir: string, sourcesDir?: string, options?: {
23
+ tools?: boolean;
24
+ pageTypes?: readonly PageType[];
25
+ }): string;
@@ -8,26 +8,56 @@ import { fileURLToPath } from "node:url";
8
8
  * with its own page types (topics, activities...) opts out with `AgentSpec.knowledgeBase: false` and
9
9
  * writes its own rules, or extends these.
10
10
  */
11
- /** Absolute path of the plugin shipped with the kit — `assets/knowledge-plugin` next to `dist/` (or `src/` under tsx). */
12
- export function knowledgePluginRoot() {
13
- return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "knowledge-plugin");
11
+ /**
12
+ * Absolute path of the plugin shipped with the kit, next to `dist/` (or `src/` under tsx):
13
+ * `assets/knowledge-plugin`, whose skills work through the `knowledge_*` tools, or with
14
+ * `"files"` `assets/knowledge-plugin-files`, whose skills work on the files (ADR-024).
15
+ */
16
+ export function knowledgePluginRoot(variant = "tools") {
17
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", variant === "files" ? "knowledge-plugin-files" : "knowledge-plugin");
14
18
  }
15
19
  /** A path as shown to the model: relative to the project, forward slashes, with a trailing slash. */
16
20
  function shown(projectDir, dir) {
17
21
  return `${path.relative(projectDir, dir).split(path.sep).join("/")}/`;
18
22
  }
23
+ /** The originals' line of the prompt section, the same in both variants. */
24
+ function originalsLine(originals) {
25
+ return `- **Originals (read-only for you)**: \`${originals}\`. Never rewrite one; build pages *about* them. \`list_sources\` says which are new (not ingested yet), changed since their ingest or missing; add a file only with \`save_to_sources\`, \`download_to_sources\` or by asking the person (\`request_file\`), and take out a wrong or superseded one only with \`retire_source\`. \`Read\` reads PDFs and images; \`extract_text\` reads DOCX, PPTX (with speaker notes) and XLSX.\n`;
26
+ }
27
+ /** The section for a knowledge base reached through the `knowledge_*` tools (ADR-024). */
28
+ function toolsSection(originals, pageTypes) {
29
+ return `## Knowledge base
30
+ Your memory across sessions is an interlinked knowledge base of pages that you write and maintain yourself — a wiki, not a pile of notes. A future session only knows what is written there, so anything worth remembering must end up in a page, not just in this turn's reply. You reach it only through the \`knowledge_*\` tools. Write page content in the language you are using with the human.
31
+
32
+ ### Layers
33
+ ${originals ? originalsLine(originals) : ""}- **The knowledge base**: pages identified by type and slug (\`concept/spring-tides\`), plus the \`overview\`, a living synthesis of the whole. Page types: ${pageTypes.map((type) => `\`${type.type}\` (${type.description})`).join("; ")}.
34
+ - **The schema**: these rules plus the \`knowledge-ingest\`, \`knowledge-query\` and \`knowledge-lint\` skills. \`knowledge_create\` without content gives a type's template.
35
+
36
+ ### Working rules
37
+ - Start from \`knowledge_search\` or \`knowledge_index\`, then \`knowledge_read\` only the pages the task needs.
38
+ - Link pages by id, \`[Spring tides](concept/spring-tides)\`; links must point to existing pages. The index and the backlinks ("linked from") are kept for you.
39
+ - Change a page with \`knowledge_edit\` (a fragment); \`knowledge_rewrite\` only to redo it whole. Pages are never deleted or renamed: \`knowledge_supersede\` one replaced by another; \`knowledge_retire\` one that was wrong.
40
+ - When an operation is done, \`knowledge_log\` it: \`ingest\`, \`query\`, \`lint\` or \`update\`, with the pages touched.
41
+ - Every claim must be traceable to a summary page, an original, or — clearly labelled as outside the knowledge base — the web.
42
+ - Contradictions are kept, not overwritten: record both versions with attribution in the affected pages.
43
+ - Short, focused, well-linked pages beat long ones: when a page mixes two things, split it.`;
44
+ }
19
45
  /**
20
46
  * The "Knowledge base" section appended to the system prompt: the layout and the working
21
- * rules. The exact page templates live in the `knowledge-pages` skill, to keep this short.
47
+ * rules. With `tools` (the kit's default since ADR-024), for a knowledge base reached through
48
+ * the `knowledge_*` tools, listing `pageTypes`; otherwise for one kept with the file tools,
49
+ * whose page templates live in the `knowledge-pages` skill.
22
50
  */
23
- export function knowledgePromptSection(projectDir, knowledgeDir, sourcesDir) {
51
+ export function knowledgePromptSection(projectDir, knowledgeDir, sourcesDir, options = {}) {
24
52
  const notes = shown(projectDir, knowledgeDir);
25
53
  const originals = sourcesDir ? shown(projectDir, sourcesDir) : undefined;
54
+ if (options.tools)
55
+ return toolsSection(originals, options.pageTypes ?? []);
26
56
  return `## Knowledge base (${notes})
27
57
  \`${notes}\` is your memory across sessions, kept as an interlinked knowledge base of markdown pages that you write and maintain yourself — a wiki, not a pile of notes. A future session only knows what is written there, so anything worth remembering must end up in a page, not just in this turn's reply. Write page content in the language you are using with the human; file names stay lowercase ASCII with hyphens.
28
58
 
29
59
  ### Layers
30
- ${originals ? `- **Originals (read-only for you)**: \`${originals}\`. Never rewrite one; build pages *about* them. Add a file there only with \`save_to_sources\`.\n` : ""}- **The knowledge base**: \`${notes}\`, entirely yours to write.
60
+ ${originals ? originalsLine(originals) : ""}- **The knowledge base**: \`${notes}\`, entirely yours to write.
31
61
  - **The schema**: these rules plus the \`knowledge-pages\` skill (exact template of every page type — load it before creating a page) and the \`knowledge-ingest\`, \`knowledge-query\` and \`knowledge-lint\` skills.
32
62
 
33
63
  ### Layout
@@ -0,0 +1,134 @@
1
+ /**
2
+ * The knowledge base as pages, not files (ADR-024): the agent reaches it only through the
3
+ * kit's `knowledge_*` tools, which sit on this interface, so the storage can change (markdown
4
+ * files today, a vector database tomorrow) without touching the tools, the prompt or the
5
+ * skills. A page is identified by its type and slug, `concept/bowline`, and links between
6
+ * pages use that id too: `[Bowline](concept/bowline)`.
7
+ */
8
+ /** A kind of page: the kit's four, or one an agent declares (`AgentSpec.knowledgePageTypes`). */
9
+ export interface PageType {
10
+ /** Its name, lowercase: `concept`, `topic`. Page ids start with it. */
11
+ type: string;
12
+ /** Where a file store keeps its pages, relative to the knowledge folder; "" for the root. */
13
+ dir: string;
14
+ /** Its section in the index, e.g. "Concepts". */
15
+ indexSection: string;
16
+ /** What it holds and when to create one, in one line: told to the model. */
17
+ description: string;
18
+ /** The page's body skeleton (markdown, under its title), shown when one is created. */
19
+ template: string;
20
+ /** Frontmatter fields shown in the page's index line, e.g. `["mastery"]`. */
21
+ indexFields?: string[];
22
+ }
23
+ /** One page, as the store returns it. */
24
+ export interface KnowledgePage {
25
+ id: string;
26
+ type: string;
27
+ slug: string;
28
+ title: string;
29
+ /** Frontmatter fields besides type and title (aliases, file, url, updated, status...). */
30
+ fields: Record<string, string>;
31
+ /** The markdown body, links to other pages as ids. */
32
+ content: string;
33
+ /** The pages that link to this one. */
34
+ linkedFrom: string[];
35
+ }
36
+ /** A page's line in a listing. */
37
+ export interface PageInfo {
38
+ id: string;
39
+ type: string;
40
+ title: string;
41
+ /** One line: its `description` field, or its first sentence. */
42
+ description: string;
43
+ /** `active`, `superseded` or `retired`. */
44
+ status: string;
45
+ fields: Record<string, string>;
46
+ }
47
+ export interface SearchHit {
48
+ id: string;
49
+ title: string;
50
+ /** Where it matched, one line. */
51
+ snippet: string;
52
+ }
53
+ /** What `check()` finds: the mechanical problems, for the agent to fix or report. */
54
+ export interface CheckReport {
55
+ /** Links to pages that don't exist. */
56
+ brokenLinks: {
57
+ page: string;
58
+ target: string;
59
+ }[];
60
+ /** Pages nothing links to (summaries and syntheses aside, which the index reaches). */
61
+ orphans: string[];
62
+ /** Links to retired pages. */
63
+ linksToRetired: {
64
+ page: string;
65
+ target: string;
66
+ }[];
67
+ /** Summary pages whose original is gone from the sources folder. */
68
+ missingOriginals: {
69
+ page: string;
70
+ file: string;
71
+ }[];
72
+ }
73
+ /** A page to create. */
74
+ export interface NewPage {
75
+ type: string;
76
+ slug: string;
77
+ title: string;
78
+ content: string;
79
+ fields?: FieldChanges;
80
+ }
81
+ /** A change to a page's frontmatter fields: a string sets it, null removes it. */
82
+ export type FieldChanges = Record<string, string | null>;
83
+ /**
84
+ * Where the knowledge base lives. The kit ships one over markdown files
85
+ * (`createFileKnowledgeStore()`); another one (a database, a vector store) implements this
86
+ * interface and is passed with `AgentSpec.knowledgeStore`. Pages are never deleted or renamed:
87
+ * their ids are what links point to.
88
+ */
89
+ export interface KnowledgeStore {
90
+ /** The page types it holds, the kit's and the agent's. */
91
+ types(): readonly PageType[];
92
+ /** Every page but the retired ones. */
93
+ list(): Promise<PageInfo[]>;
94
+ /** A page, retired ones included; `null` if there's none. */
95
+ read(id: string): Promise<KnowledgePage | null>;
96
+ /** A new page; fails if the id exists or a link points nowhere. Returns its id. */
97
+ create(type: string, slug: string, title: string, content: string, fields?: FieldChanges): Promise<string>;
98
+ /**
99
+ * Several new pages at once, whose links may point to each other (a summary and the
100
+ * concepts it feeds): all are created, or none. Returns their ids.
101
+ */
102
+ createMany(pages: readonly NewPage[]): Promise<string[]>;
103
+ /** Replaces the one occurrence of `oldText` in a page's body; fails if it's missing or appears more than once. */
104
+ edit(id: string, oldText: string, newText: string, fields?: FieldChanges): Promise<void>;
105
+ /** Replaces a page's whole body, keeping its id. */
106
+ rewrite(id: string, content: string, fields?: FieldChanges): Promise<void>;
107
+ /** Marks a page superseded by another, with a notice at its top. */
108
+ supersede(id: string, by: string, reason?: string): Promise<void>;
109
+ /** Takes a page out of the index and the search (it's kept, and can be restored). */
110
+ retire(id: string, reason: string): Promise<void>;
111
+ /** Pages matching words of `query` in their title, aliases or body, best first. */
112
+ search(query: string, limit?: number): Promise<SearchHit[]>;
113
+ /** The catalog: every active or superseded page, one line each, by section. */
114
+ index(): Promise<string>;
115
+ /** Adds an entry to the operation log, dated today. */
116
+ log(operation: string, what: string, pages?: readonly string[]): Promise<void>;
117
+ /** The mechanical problems. */
118
+ check(): Promise<CheckReport>;
119
+ /** The overview: a living synthesis of the whole knowledge base ("" if none yet). */
120
+ overview(): Promise<string>;
121
+ /** Replaces the overview. */
122
+ writeOverview(content: string): Promise<void>;
123
+ }
124
+ /** The kit's page types (ADR-008). */
125
+ export declare const BUILT_IN_PAGE_TYPES: readonly PageType[];
126
+ /** Whether `slug` is lowercase ASCII words joined by hyphens. */
127
+ export declare function isSlug(slug: string): boolean;
128
+ /** `type/slug` split, or null when it isn't one. */
129
+ export declare function parseId(id: string): {
130
+ type: string;
131
+ slug: string;
132
+ } | null;
133
+ /** The ids every `[text](id)` link in `markdown` points to (links that look like page ids). */
134
+ export declare function linkedIds(markdown: string): string[];
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The knowledge base as pages, not files (ADR-024): the agent reaches it only through the
3
+ * kit's `knowledge_*` tools, which sit on this interface, so the storage can change (markdown
4
+ * files today, a vector database tomorrow) without touching the tools, the prompt or the
5
+ * skills. A page is identified by its type and slug, `concept/bowline`, and links between
6
+ * pages use that id too: `[Bowline](concept/bowline)`.
7
+ */
8
+ /** The kit's page types (ADR-008). */
9
+ export const BUILT_IN_PAGE_TYPES = [
10
+ {
11
+ type: "summary",
12
+ dir: "summaries",
13
+ indexSection: "Summaries",
14
+ description: "One per ingested source: what it says, its key points and the pages it feeds. Fields: file (the original, relative to sources/) or url; ingested.",
15
+ template: `## Summary
16
+ <Two to four paragraphs: what it actually says.>
17
+
18
+ ## Key points
19
+ - <Claims, definitions or figures likely to be needed later, quoted literally when exact wording matters.>
20
+
21
+ ## Pages it feeds
22
+ - [<Concept or entity>](concept/<slug>)
23
+
24
+ ## New or surprising
25
+ - <What this source adds to what the knowledge base already had.>
26
+
27
+ ## Contradictions
28
+ - <Conflicts with other pages, linked.>`,
29
+ },
30
+ {
31
+ type: "concept",
32
+ dir: "concepts",
33
+ indexSection: "Concepts",
34
+ description: "One idea per page: its definition, explanation, connections and sources. Fields: aliases (other names, comma-separated).",
35
+ template: `<Definition in one or two sentences.>
36
+
37
+ ## Explanation
38
+ <How it works, at the depth the sources give.>
39
+
40
+ ## Connections
41
+ - Requires: [<Concept>](concept/<slug>)
42
+ - Related: [<Concept>](concept/<slug>) - how
43
+
44
+ ## Sources
45
+ - [<Summary>](summary/<slug>) - what it says about this
46
+
47
+ ## Contradictions and open questions
48
+ - "<Source A> says X; <source B> says Y" - both attributed.`,
49
+ },
50
+ {
51
+ type: "entity",
52
+ dir: "entities",
53
+ indexSection: "Entities",
54
+ description: "A concrete thing: a system, component, organization or document (a person only as a public role). Fields: kind, aliases.",
55
+ template: `<What it is, one or two sentences.>
56
+
57
+ ## Facts
58
+ - <Attributed facts, each linking the summary it comes from.>
59
+
60
+ ## Connections
61
+ - [<Concept or entity>](concept/<slug>) - how`,
62
+ },
63
+ {
64
+ type: "synthesis",
65
+ dir: "syntheses",
66
+ indexSection: "Syntheses",
67
+ description: "An answer worth keeping: a comparison, an analysis, a report. Fields: question.",
68
+ template: `<The answer, with links to every page it draws on.>`,
69
+ },
70
+ ];
71
+ const SLUG = /^[a-z0-9]+(-[a-z0-9]+)*$/;
72
+ /** Whether `slug` is lowercase ASCII words joined by hyphens. */
73
+ export function isSlug(slug) {
74
+ return SLUG.test(slug);
75
+ }
76
+ /** `type/slug` split, or null when it isn't one. */
77
+ export function parseId(id) {
78
+ const match = /^([a-z][a-z0-9-]*)\/([a-z0-9]+(?:-[a-z0-9]+)*)$/.exec(id.trim());
79
+ return match ? { type: match[1], slug: match[2] } : null;
80
+ }
81
+ /** The ids every `[text](id)` link in `markdown` points to (links that look like page ids). */
82
+ export function linkedIds(markdown) {
83
+ return [...markdown.matchAll(/\]\(([a-z][a-z0-9-]*\/[a-z0-9]+(?:-[a-z0-9]+)*)(?:#[^)]*)?\)/g)].map((m) => m[1]);
84
+ }
@@ -54,6 +54,29 @@ export const de = {
54
54
  parametersLine: (parameters) => `Parameter: ${parameters}`,
55
55
  approvalTitle: "Vor dem Veröffentlichen ist eine menschliche Bestätigung nötig",
56
56
  summaryLine: (summary) => `Zusammenfassung: ${summary}`,
57
+ noAnswer: "(keine Antwort)",
58
+ textAnswerHint: "Antwort eingeben",
59
+ textAnswerKeys: "Enter zum Senden · Esc für keine Antwort",
60
+ fileRequestTitle: "Der Agent bittet um eine Datei",
61
+ fileRequestQuestion: "Pfad der Datei (hierher ziehen), oder Enter, wenn du sie nicht hast: ",
62
+ retireTitle: "Eine Quelle zurückziehen?",
63
+ retirePageTitle: "Eine Seite der Wissensbasis zurückziehen?",
64
+ choiceQuestion: (multiple) => (multiple ? "Nummern durch Kommas getrennt, oder deine eigene Antwort: " : "Nummer, oder deine eigene Antwort: "),
65
+ otherOption: "Andere (eigene Antwort eingeben)",
66
+ choiceKeys: (multiple) => (multiple ? "Leertaste markiert · Enter sendet" : "↑/↓ wählen · Enter sendet"),
67
+ questionTitle: "Der Agent fragt",
68
+ planTitle: "Der Plan, zur Freigabe",
69
+ runPlan: "Ausführen",
70
+ keepPlanning: "Weiter planen",
71
+ cancelPlan: "Abbrechen",
72
+ planCommentQuestion: "Was soll sich ändern? (Enter zum Überspringen): ",
73
+ retireLines: (source, why, reason, replacedBy) => [
74
+ `Quelle: ${source}`,
75
+ why === "wrong" ? "Warum: sie ist falsch; ihre Zusammenfassung wird zurückgezogen" : "Warum: sie wird ersetzt; ihre Zusammenfassung wird als ersetzt markiert",
76
+ `Grund: ${reason}`,
77
+ ...(replacedBy ? [`Ersetzt durch: ${replacedBy}`] : []),
78
+ "Sie wird nach sources/.agent-kit/retired/ verschoben, nicht gelöscht.",
79
+ ],
57
80
  toolPhrases: {
58
81
  Read: ["hat {n} Datei gelesen", "hat {n} Dateien gelesen"],
59
82
  Write: ["hat {n} Datei geschrieben", "hat {n} Dateien geschrieben"],
@@ -66,16 +89,40 @@ export const de = {
66
89
  Skill: ["hat {n} Skill verwendet", "hat {n} Skills verwendet"],
67
90
  Agent: ["hat {n} Subagenten gestartet", "hat {n} Subagenten gestartet"],
68
91
  Task: ["hat {n} Subagenten gestartet", "hat {n} Subagenten gestartet"],
92
+ mcp__time__current_time: ["Uhrzeit abgefragt", "Uhrzeit {n}-mal abgefragt"],
93
+ mcp__time__date_math: ["Daten berechnet", "Daten {n}-mal berechnet"],
94
+ mcp__sourceFiles__list_sources: ["Quellen geprüft", "Quellen {n}-mal geprüft"],
95
+ mcp__sourceFiles__extract_text: ["{n} Dokument gelesen", "{n} Dokumente gelesen"],
96
+ mcp__sourceFiles__save_to_sources: ["{n} Quelle gespeichert", "{n} Quellen gespeichert"],
97
+ mcp__sourceFiles__download_to_sources: ["{n} Quelle heruntergeladen", "{n} Quellen heruntergeladen"],
98
+ mcp__sourceFiles__request_file: ["um {n} Datei gebeten", "um {n} Dateien gebeten"],
99
+ mcp__sourceFiles__retire_source: ["{n} Quelle zurückgezogen", "{n} Quellen zurückgezogen"],
100
+ mcp__knowledge__knowledge_index: ["Index gelesen", "Index {n}-mal gelesen"],
101
+ mcp__knowledge__knowledge_search: ["Wissensbasis durchsucht", "Wissensbasis {n}-mal durchsucht"],
102
+ mcp__knowledge__knowledge_read: ["{n} Seite gelesen", "{n} Seiten gelesen"],
103
+ mcp__knowledge__knowledge_create: ["{n} Seite angelegt", "{n} Seiten angelegt"],
104
+ mcp__knowledge__knowledge_edit: ["{n} Seite bearbeitet", "{n} Seiten bearbeitet"],
105
+ mcp__knowledge__knowledge_rewrite: ["{n} Seite neu geschrieben", "{n} Seiten neu geschrieben"],
106
+ mcp__knowledge__knowledge_supersede: ["{n} Seite ersetzt", "{n} Seiten ersetzt"],
107
+ mcp__knowledge__knowledge_retire: ["{n} Seite zurückgezogen", "{n} Seiten zurückgezogen"],
108
+ mcp__knowledge__knowledge_log: ["Protokoll aktualisiert", "Protokoll {n}-mal aktualisiert"],
109
+ mcp__knowledge__knowledge_check: ["Wissensbasis geprüft", "Wissensbasis {n}-mal geprüft"],
110
+ mcp__approvals__ask_human: ["{n} Frage gestellt", "{n} Fragen gestellt"],
111
+ mcp__approvals__present_plan: ["Plan vorgelegt", "Plan {n}-mal vorgelegt"],
69
112
  },
70
113
  otherTools: ["hat {n} Werkzeug verwendet", "hat {n} Werkzeuge verwendet"],
71
114
  noOutput: "(keine Ausgabe)",
72
115
  error: "Fehler",
116
+ toolFailed: "Konnte nicht abgeschlossen werden",
73
117
  moreResultLines: (count) => `(+${count} ${plural(count, "Zeile", "Zeilen")})`,
74
118
  earlierCalls: (count) => `… ${count} frühere`,
119
+ moreTodos: (count) => `… ${count} weitere ${plural(count, "Aufgabe", "Aufgaben")}`,
75
120
  labels: {
76
121
  askingApproval: (summary) => `Bittet um menschliche Genehmigung: ${summary}`,
77
122
  waitingManual: "Wartet auf einen manuellen Eingriff",
78
123
  savingToSources: (destination) => `Speichert eine Datei in sources/${destination}`,
124
+ checkingTime: "Datum und Uhrzeit abfragen",
125
+ calculatingDates: "Daten berechnen",
79
126
  reading: (file) => `Liest ${file}`,
80
127
  writing: (file) => `Schreibt in ${file}`,
81
128
  editing: (file) => `Bearbeitet ${file}`,
@@ -91,6 +138,24 @@ export const de = {
91
138
  searchingWeb: (query) => `Sucht im Web nach "${query}"`,
92
139
  applyingSkill: (skill) => `Wendet den Skill "${skill}" an`,
93
140
  aSkill: "einen Skill",
141
+ updatingTasks: "Aufgabenliste aktualisieren",
142
+ listingSources: "Quellen prüfen",
143
+ extractingText: (source) => `${source} lesen`,
144
+ downloading: (url) => `${url} in die Quellen herunterladen`,
145
+ requestingFile: (description) => `Um eine Datei bitten: ${description}`,
146
+ retiringSource: (source) => `Quelle ${source} zurückziehen`,
147
+ knowledgeIndex: "Index der Wissensbasis lesen",
148
+ knowledgeSearch: (query) => `Wissensbasis nach "${query}" durchsuchen`,
149
+ knowledgeRead: (page) => `${page} lesen`,
150
+ knowledgeCreate: (page) => `${page} anlegen`,
151
+ knowledgeEdit: (page) => `${page} bearbeiten`,
152
+ knowledgeRewrite: (page) => `${page} neu schreiben`,
153
+ knowledgeSupersede: (page) => `${page} als ersetzt markieren`,
154
+ knowledgeRetire: (page) => `${page} zurückziehen`,
155
+ knowledgeLog: "Protokoll der Wissensbasis aktualisieren",
156
+ knowledgeCheck: "Wissensbasis prüfen",
157
+ askingHuman: (question) => `Frage: ${question}`,
158
+ presentingPlan: "Plan vorlegen",
94
159
  },
95
160
  auth: {
96
161
  noToken: "Es wurde kein Claude-Authentifizierungstoken gefunden",
@@ -51,16 +51,39 @@ export interface Messages {
51
51
  parametersLine(parameters: string): string;
52
52
  approvalTitle: string;
53
53
  summaryLine(summary: string): string;
54
+ noAnswer: string;
55
+ textAnswerHint: string;
56
+ textAnswerKeys: string;
57
+ fileRequestTitle: string;
58
+ fileRequestQuestion: string;
59
+ retireTitle: string;
60
+ retirePageTitle: string;
61
+ choiceQuestion(multiple: boolean): string;
62
+ otherOption: string;
63
+ choiceKeys(multiple: boolean): string;
64
+ questionTitle: string;
65
+ planTitle: string;
66
+ runPlan: string;
67
+ keepPlanning: string;
68
+ cancelPlan: string;
69
+ planCommentQuestion: string;
70
+ retireLines(source: string, why: "wrong" | "replaced", reason: string, replacedBy?: string): string[];
54
71
  toolPhrases: Record<string, ToolPhrase>;
55
72
  otherTools: ToolPhrase;
56
73
  noOutput: string;
57
74
  error: string;
75
+ /** A failed tool call when results are hidden (`toolDetail: "calls"`): no tool output, just that it failed. */
76
+ toolFailed: string;
58
77
  moreResultLines(count: number): string;
59
78
  earlierCalls(count: number): string;
79
+ /** Tasks of the task list left out of the view. */
80
+ moreTodos(count: number): string;
60
81
  labels: {
61
82
  askingApproval(summary: string): string;
62
83
  waitingManual: string;
63
84
  savingToSources(destination: string): string;
85
+ checkingTime: string;
86
+ calculatingDates: string;
64
87
  reading(file: string): string;
65
88
  writing(file: string): string;
66
89
  editing(file: string): string;
@@ -76,6 +99,24 @@ export interface Messages {
76
99
  searchingWeb(query: string): string;
77
100
  applyingSkill(skill: string): string;
78
101
  aSkill: string;
102
+ updatingTasks: string;
103
+ listingSources: string;
104
+ extractingText(source: string): string;
105
+ downloading(url: string): string;
106
+ requestingFile(description: string): string;
107
+ retiringSource(source: string): string;
108
+ knowledgeIndex: string;
109
+ knowledgeSearch(query: string): string;
110
+ knowledgeRead(page: string): string;
111
+ knowledgeCreate(page: string): string;
112
+ knowledgeEdit(page: string): string;
113
+ knowledgeRewrite(page: string): string;
114
+ knowledgeSupersede(page: string): string;
115
+ knowledgeRetire(page: string): string;
116
+ knowledgeLog: string;
117
+ knowledgeCheck: string;
118
+ askingHuman(question: string): string;
119
+ presentingPlan: string;
79
120
  };
80
121
  auth: {
81
122
  noToken: string;