@falkenslab/agent-kit 0.15.0 → 0.16.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.
Files changed (68) hide show
  1. package/README.md +3 -1
  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 +395 -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 +139 -0
  30. package/dist/core/knowledgeStore.js +84 -0
  31. package/dist/core/messages/de.js +65 -1
  32. package/dist/core/messages/en.d.ts +39 -0
  33. package/dist/core/messages/en.js +65 -1
  34. package/dist/core/messages/es.js +65 -1
  35. package/dist/core/messages/fr.js +65 -1
  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 +81 -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 +168 -0
  50. package/dist/core/tools/saveToSources.d.ts +27 -8
  51. package/dist/core/tools/saveToSources.js +217 -55
  52. package/dist/core/tools/time.d.ts +29 -0
  53. package/dist/core/tools/time.js +129 -0
  54. package/dist/core/version.d.ts +5 -0
  55. package/dist/core/version.js +12 -0
  56. package/dist/index.d.ts +7 -3
  57. package/dist/index.js +5 -1
  58. package/dist/tui/chatTui.js +14 -1
  59. package/dist/tui/consoleRenderer.js +16 -2
  60. package/dist/tui/ink/SessionView.js +67 -17
  61. package/dist/tui/ink/inkInteraction.d.ts +4 -2
  62. package/dist/tui/ink/inkInteraction.js +20 -2
  63. package/dist/tui/ink/runChatInk.js +5 -0
  64. package/dist/tui/ink/sessionModel.d.ts +3 -0
  65. package/dist/tui/ink/sessionModel.js +14 -1
  66. package/dist/tui/terminalInteraction.js +2 -0
  67. package/package.json +31 -1
  68. /package/assets/{knowledge-plugin → knowledge-plugin-files}/skills/knowledge-pages/SKILL.md +0 -0
package/README.md CHANGED
@@ -34,7 +34,9 @@ A foundation for building AI agents on top of the [Claude Agent SDK](https://www
34
34
 
35
35
  [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 your language. 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 tells the time with a tool of the captain's own. 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.
36
36
 
37
- ![Captain Whiskers in Windows Terminal: its logo at the top, the conversation in the middle and the framed prompt at the bottom, with a suggestion for the next message](docs/assets/captain-whiskers.png)
37
+ ![Captain Whiskers in Windows Terminal: his logo with the mode and the agent-kit version at the top, his crew at work in the middle, a task list under the spinner, and the framed prompt and status bar at the bottom](docs/assets/captain-whiskers.png)
38
+
39
+ See [everything he can do](https://falkenslab.github.io/agent-kit/docs/next/examples/captain-whiskers#what-he-can-do), with screenshots.
38
40
 
39
41
  ### teacher-agent
40
42
 
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "knowledge",
3
- "description": "Built-in knowledge base workflows (an LLM wiki): page templates, ingesting sources, querying the knowledge base and health-checking it."
3
+ "description": "Built-in knowledge base workflows (an LLM wiki) over the kit's knowledge_* tools: ingesting sources, querying the knowledge base and health-checking it."
4
4
  }
@@ -4,4 +4,4 @@ description: Ingest material into the knowledge base right now - the files or to
4
4
 
5
5
  Apply the `knowledge-ingest` skill right now to: $ARGUMENTS
6
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.
7
+ If nothing is given above, ingest every original `list_sources` reports as new, and update the summaries of the ones it reports as changed. When you're done, summarize which pages you created and updated, and anything you couldn't read.
@@ -2,4 +2,4 @@
2
2
  description: Health-check the knowledge base right now - broken links, orphans, duplicates, contradictions, gaps
3
3
  ---
4
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.
5
+ Apply the `knowledge-lint` skill right now over the whole knowledge base. Fix what's mechanical (broken links, orphans, links to retired pages) and report the rest: what needs the human's decision, and the gaps worth filling next.
@@ -1,42 +1,36 @@
1
1
  ---
2
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.
3
+ description: Ingest one source into the knowledge base - an original in sources/, a document, a page - creating its summary page, creating or updating the concept and entity pages it touches, and logging it.
4
4
  ---
5
5
 
6
6
  # Ingesting a source into the knowledge base
7
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.
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. Its value is in how it connects to what the knowledge base already knows. Work only through the `knowledge_*` tools; links between pages are ids, `[Bowline](concept/bowline)`, and the index and backlinks are kept for you.
9
9
 
10
10
  ## 1. Check it isn't already ingested
11
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.
12
+ `list_sources` gives each original's status and summary pages: *new* needs ingesting; *ingested* has a page; *changed* changed after its ingest (update its summary). For a document from elsewhere, `knowledge_search` its title or URL.
15
13
 
16
14
  ## 2. Read it for real
17
15
 
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.
16
+ Read the whole source, not its first page (a long PDF in parts: `list_sources` gives its pages). Its path is the sources folder's (named in the system prompt) plus the path `list_sources` gives, from the project folder, not from this skill's folder. `Read` reads PDFs and images; `extract_text` reads DOCX, PPTX (with its speaker notes) and XLSX. If it exists only outside the sources folder, keep the original first: `download_to_sources` for a URL (a web page is kept as markdown too: read that one), `save_to_sources` for a file in the run's folder, `request_file` to ask the person for one.
19
17
 
20
18
  ## 3. Write the summary page
21
19
 
22
- Create `summaries/<slug>.md` with the template: summary, key points (literal wording where exactness matters), frontmatter `file`/`url` pointing to the original.
20
+ `knowledge_create` with type `summary` (call it once without content to get the template), and the field `file` (the original, relative to sources/) or `url`. Key points quote literally where exactness matters. Create the new concept and entity pages it feeds in the same call (`also`): they can link to each other.
23
21
 
24
22
  ## 4. Update concepts and entities
25
23
 
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".
24
+ For every concept or entity the source explains or relies on, `knowledge_search` it first (under other names and aliases too: a duplicate page is worse than none):
25
+ - no page yet: create it, with the summary (`also`) or on its own;
26
+ - page exists: add what is new (a better definition, an example, a nuance) with `knowledge_edit`, and link the summary under its sources.
29
27
 
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.
28
+ If the source contradicts a page, record both versions with attribution in both pages; don't overwrite.
31
29
 
32
- ## 5. Link both ways and look for conflicts
30
+ ## 5. Close
33
31
 
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.
32
+ `knowledge_log` with operation `ingest`, the source's title and the pages created and updated. Rewrite the `overview` (`knowledge_rewrite`) only if the source changes the big picture. When ingesting several sources in a row, log once at the end.
35
33
 
36
- ## 6. Close
34
+ ## An original that was wrong or replaced
37
35
 
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.
36
+ `retire_source` (the person approves it) moves it aside and marks its summary retired (wrong) or superseded (replaced). Then `knowledge_check` lists the passages that cite it: after a wrong one, correct or remove what it taught (`knowledge_edit`, or `knowledge_retire` for a page that was only about it); after a replaced one, ingest the new original and re-ground those passages on it.
@@ -1,26 +1,30 @@
1
1
  ---
2
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.
3
+ description: Health-check the knowledge base - broken links, orphan pages, links to retired pages, missing or changed originals, duplicated concepts, mentioned-but-missing concepts, contradictions, gaps - fix what's mechanical and report the rest.
4
4
  ---
5
5
 
6
6
  # Linting the knowledge base
7
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.
8
+ A knowledge base maintained across sessions drifts. Lint it when asked and after a large ingest.
9
9
 
10
- ## Checks
10
+ ## 1. The mechanical checks, in one call
11
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.
12
+ `knowledge_check` returns broken links, orphan pages, links to retired pages, summaries whose original is gone, originals that are new, changed or missing, and the passages that cite a retired original. The index and backlinks are kept by the store: there's nothing to fix there.
13
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.
14
+ - **Broken links**: fix them with `knowledge_edit` when the right target is obvious (a typo, a page under a close name: `knowledge_search`); otherwise report them.
15
+ - **Orphans**: link them from the pages where they belong, or report why they don't belong anywhere.
16
+ - **Links to retired pages, passages citing a retired original**: correct or re-ground them.
17
+ - **New or changed originals**: report them as what to ingest or update next (or do it, if asked).
18
+
19
+ ## 2. The checks that need judgement
20
+
21
+ Use `knowledge_index` and `knowledge_search`; read only the pages a check needs.
22
+
23
+ - **Duplicates**: two pages for the same idea under different names. Merge the content into one with `knowledge_edit`, `knowledge_supersede` the other (never delete it) and repoint links.
24
+ - **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.
25
+ - **Contradictions**: claims that disagree across pages without being recorded as such. Record both sides, attributed.
26
+ - **Provenance**: claims with no traceable source. Add the link, or mark them as unsourced.
23
27
 
24
28
  ## Close
25
29
 
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.
30
+ `knowledge_log` with operation `lint` and what was fixed. Then report to the person: 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.
@@ -1,14 +1,14 @@
1
1
  ---
2
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.
3
+ description: Answer a question from the knowledge base - find the relevant pages, read them, answer citing them, and file the answer back as a synthesis page when it is worth keeping.
4
4
  ---
5
5
 
6
6
  # Querying the knowledge base
7
7
 
8
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
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).
10
+ 1. **Find the pages.** `knowledge_search` the question's terms (and their synonyms), or `knowledge_index` for the catalog; `knowledge_read` only the pages that matter.
11
+ 2. **Answer from the pages**, citing each one you drew on. Say what comes from the knowledge base, what from an original in the sources folder (read it if the knowledge base is thin or contradictory) and what from outside (label it as such).
12
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.
13
+ 4. **File it back.** If the answer is a comparison, an analysis or a report that will be asked again, `knowledge_create` a `synthesis` page with links to its inputs, and `knowledge_log` it (operation `query`). A quick factual answer needs no page.
14
+ 5. **Feed the knowledge base.** If answering revealed a concept with no page, or a contradiction, fix that too and log it.
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "knowledge",
3
+ "description": "Built-in knowledge base workflows (an LLM wiki): page templates, ingesting sources, querying the knowledge base and health-checking it."
4
+ }
@@ -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, ingest every original `list_sources` reports as new, and update the summaries of the ones it reports as changed. 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,5 @@
1
+ ---
2
+ description: Answer a question from the knowledge base, and file the answer back if it is worth keeping
3
+ ---
4
+
5
+ Apply the `knowledge-query` skill right now to answer: $ARGUMENTS
@@ -0,0 +1,46 @@
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
+ `list_sources` gives each original's status and summary pages: *new* needs ingesting; *ingested* has a page; *changed* changed after its ingest. For a document from elsewhere, look for it in `index.md` (Summaries) or `Grep` its 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 (a long PDF in parts: `list_sources` gives its pages). If it exists only outside the sources folder, keep the original first: `download_to_sources` for a URL (a web page is kept as markdown too: read that one), `save_to_sources` for a file in the run's folder, `request_file` to ask the person for one. `Read` reads PDFs and images; read DOCX, PPTX (with its speaker notes) and XLSX with `extract_text`.
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.
43
+
44
+ ## An original that was wrong or replaced
45
+
46
+ `retire_source` (the person approves it) moves it aside and marks its summary retired (wrong) or superseded (replaced by a better one). Then update `index.md`, and fix the pages that cite it (`Grep` its file name): after a wrong one, correct or remove what it taught; after a replaced one, ingest the new original and re-ground those passages on it.
@@ -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**: originals `list_sources` reports as new (no summary yet), changed (their summary may be stale) or missing (deleted by hand: their summaries point nowhere), and concepts mentioned but never explained by any source. Report them - they are what to ingest, update 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,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.
@@ -1,4 +1,5 @@
1
1
  import type { AgentDefinition as SdkSubagentDefinition, McpServerConfig, SettingSource } from "@anthropic-ai/claude-agent-sdk";
2
+ import type { KnowledgeStore, PageType } from "./knowledgeStore.js";
2
3
  /**
3
4
  * The human-supervision spectrum every agent built on this kit shares, independent of
4
5
  * domain: "interactive" pauses before every single action, "guided" only pauses before a
@@ -49,6 +50,11 @@ export interface BaseSessionConfig {
49
50
  * language (see language.ts). Pass the same value to the chat, wizard or progress view.
50
51
  */
51
52
  language?: string;
53
+ /**
54
+ * The time zone the date and time tools answer in (`current_time`, `date_math`), an IANA
55
+ * name such as "Europe/Madrid"; the system's if not given.
56
+ */
57
+ timeZone?: string;
52
58
  }
53
59
  /**
54
60
  * Everything actually *about the domain* that `buildSessionOptions()` needs but doesn't
@@ -104,12 +110,31 @@ export interface AgentSpec<TConfig extends BaseSessionConfig> {
104
110
  /**
105
111
  * The built-in knowledge base (see knowledge.ts): when `config.knowledgeDir` is set, the kit
106
112
  * appends its "Knowledge base" rules to the system prompt and loads its plugin (skills
107
- * knowledge-pages/knowledge-ingest/knowledge-query/knowledge-lint, commands /knowledge:ingest, /knowledge:query,
108
- * /knowledge:lint), so the agent maintains its notes as an interlinked wiki. On by default;
113
+ * knowledge-ingest/knowledge-query/knowledge-lint, commands /knowledge:ingest, /knowledge:query,
114
+ * /knowledge:lint), so the agent maintains its notes as an interlinked wiki, through the
115
+ * `knowledge_*` tools by default (see `knowledgeTools`). On by default;
109
116
  * set `false` for an agent that writes its own rules for `knowledgeDir` (or wants plain
110
117
  * notes) — `knowledgePromptSection()`/`knowledgePluginRoot()` are exported to reuse the pieces.
111
118
  */
112
119
  knowledgeBase?: boolean;
120
+ /**
121
+ * How the agent reaches the built-in knowledge base: `"store"` (the default), only through
122
+ * the kit's `knowledge_*` tools over a `KnowledgeStore`, never with the file tools, so the
123
+ * storage can change and the wiki's rules are kept by code (ADR-024); `"files"`, with the
124
+ * file tools on `knowledgeDir` and the rules in the prompt, as before.
125
+ */
126
+ knowledgeTools?: "store" | "files";
127
+ /**
128
+ * The agent's own page types for the built-in knowledge base, besides the kit's four
129
+ * (summary, concept, entity, synthesis): each with its folder (`""` for the root), index
130
+ * section, description (told to the model) and template.
131
+ */
132
+ knowledgePageTypes?: PageType[];
133
+ /**
134
+ * The knowledge base's store, instead of the kit's over markdown files in `knowledgeDir`
135
+ * (e.g. a database or a vector store implementing `KnowledgeStore`).
136
+ */
137
+ knowledgeStore?(config: TConfig): KnowledgeStore;
113
138
  /**
114
139
  * Whether the kit tells the agent (and each subagent) to reply in the resolved language
115
140
  * (`config.language`, `--language`, the system's), following the human if they write in
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Word, PowerPoint and Excel originals as markdown: the SDK's `Read` refuses DOCX and PPTX
3
+ * (confirmed empirically), and course material is often both. The libraries are optional
4
+ * peer dependencies of the kit (`mammoth` for DOCX, `fflate` to open PPTX and XLSX, which
5
+ * are zips of XML); without them the caller is told what to install.
6
+ */
7
+ export declare const EXTRACTABLE: Set<string>;
8
+ export declare class MissingLibraryError extends Error {
9
+ readonly library: string;
10
+ constructor(library: string, options?: ErrorOptions);
11
+ }
12
+ /** An original's text as markdown; `from`/`to` pick slides of a deck. */
13
+ export declare function extractText(file: string, from?: number, to?: number): Promise<string>;
@@ -0,0 +1,147 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ /**
4
+ * Word, PowerPoint and Excel originals as markdown: the SDK's `Read` refuses DOCX and PPTX
5
+ * (confirmed empirically), and course material is often both. The libraries are optional
6
+ * peer dependencies of the kit (`mammoth` for DOCX, `fflate` to open PPTX and XLSX, which
7
+ * are zips of XML); without them the caller is told what to install.
8
+ */
9
+ export const EXTRACTABLE = new Set(["docx", "pptx", "xlsx"]);
10
+ /** The most rows of a sheet written out; the rest are counted. */
11
+ const MAX_SHEET_ROWS = 200;
12
+ export class MissingLibraryError extends Error {
13
+ library;
14
+ constructor(library, options) {
15
+ super(`Reading this format needs the optional "${library}" library: npm install ${library}`, options);
16
+ this.library = library;
17
+ }
18
+ }
19
+ async function load(library) {
20
+ try {
21
+ return (await import(library));
22
+ }
23
+ catch (error) {
24
+ throw new MissingLibraryError(library, { cause: error });
25
+ }
26
+ }
27
+ const decode = (xml) => xml
28
+ .replace(/&lt;/g, "<")
29
+ .replace(/&gt;/g, ">")
30
+ .replace(/&quot;/g, '"')
31
+ .replace(/&apos;/g, "'")
32
+ .replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code)))
33
+ .replace(/&amp;/g, "&");
34
+ /** The paragraphs of a DrawingML text body (slides and notes): each `<a:p>` its runs' `<a:t>` text. */
35
+ function paragraphs(xml) {
36
+ return [...xml.matchAll(/<a:p\b[\s\S]*?<\/a:p>/g)]
37
+ .map((p) => [...p[0].matchAll(/<a:t>([\s\S]*?)<\/a:t>/g)].map((t) => decode(t[1])).join(""))
38
+ .map((text) => text.trim())
39
+ .filter(Boolean);
40
+ }
41
+ const text = (files, name) => (files[name] ? new TextDecoder().decode(files[name]) : undefined);
42
+ async function unzip(file) {
43
+ const { unzipSync } = await load("fflate");
44
+ return unzipSync(new Uint8Array(await readFile(file)));
45
+ }
46
+ /** A slide deck: a section per slide (its title, text and speaker notes), from slide `from` to `to` (1-based). */
47
+ async function pptx(file, from, to) {
48
+ const files = await unzip(file);
49
+ const slides = Object.keys(files)
50
+ .map((name) => /^ppt\/slides\/slide(\d+)\.xml$/.exec(name))
51
+ .filter((m) => Boolean(m))
52
+ .map((m) => Number(m[1]))
53
+ .sort((a, b) => a - b);
54
+ const first = Math.max(1, from ?? 1);
55
+ const last = Math.min(slides.length, to ?? slides.length);
56
+ const parts = [`# ${path.basename(file)}`, `${slides.length} slides${first > 1 || last < slides.length ? `; slides ${first} to ${last}` : ""}.`];
57
+ let images = 0;
58
+ for (const n of slides.slice(first - 1, last)) {
59
+ const xml = text(files, `ppt/slides/slide${n}.xml`) ?? "";
60
+ images += (xml.match(/<p:pic\b/g) ?? []).length;
61
+ const [title, ...body] = paragraphs(xml);
62
+ parts.push(`## Slide ${n}${title ? `: ${title}` : ""}`);
63
+ if (body.length)
64
+ parts.push(body.map((line) => `- ${line}`).join("\n"));
65
+ // A slide's notes are linked from its relationships; their slide-number placeholder is left out.
66
+ const rels = text(files, `ppt/slides/_rels/slide${n}.xml.rels`) ?? "";
67
+ const notesTarget = /Target="\.\.\/notesSlides\/(notesSlide\d+\.xml)"/.exec(rels)?.[1];
68
+ const notes = notesTarget ? paragraphs(text(files, `ppt/notesSlides/${notesTarget}`) ?? "").filter((line) => !/^\d+$/.test(line)) : [];
69
+ if (notes.length)
70
+ parts.push(`Speaker notes:\n${notes.map((line) => `> ${line}`).join("\n")}`);
71
+ }
72
+ if (images)
73
+ parts.push(`(${images} images left out.)`);
74
+ return `${parts.join("\n\n")}\n`;
75
+ }
76
+ /** The column index of a cell reference ("C7" → 2). */
77
+ function columnOf(ref) {
78
+ let index = 0;
79
+ for (const letter of /^[A-Z]+/.exec(ref)?.[0] ?? "A")
80
+ index = index * 26 + letter.charCodeAt(0) - 64;
81
+ return index - 1;
82
+ }
83
+ const cell = (value) => value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
84
+ /** A workbook: a markdown table per sheet, capped in rows. */
85
+ async function xlsx(file) {
86
+ const files = await unzip(file);
87
+ const shared = [...(text(files, "xl/sharedStrings.xml") ?? "").matchAll(/<si>([\s\S]*?)<\/si>/g)].map((si) => [...si[1].matchAll(/<t[^>]*>([\s\S]*?)<\/t>/g)].map((t) => decode(t[1])).join(""));
88
+ const workbook = text(files, "xl/workbook.xml") ?? "";
89
+ const names = [...workbook.matchAll(/<sheet\b[^>]*name="([^"]*)"/g)].map((m) => decode(m[1]));
90
+ const sheets = Object.keys(files)
91
+ .map((name) => /^xl\/worksheets\/sheet(\d+)\.xml$/.exec(name))
92
+ .filter((m) => Boolean(m))
93
+ .map((m) => Number(m[1]))
94
+ .sort((a, b) => a - b);
95
+ const parts = [`# ${path.basename(file)}`];
96
+ for (const n of sheets) {
97
+ const xml = text(files, `xl/worksheets/sheet${n}.xml`) ?? "";
98
+ const rows = [...xml.matchAll(/<row\b[\s\S]*?<\/row>/g)].map((row) => {
99
+ const values = [];
100
+ for (const c of row[0].matchAll(/<c\b([^>]*?)(?:\/>|>([\s\S]*?)<\/c>)/g)) {
101
+ const attrs = c[1];
102
+ const ref = /r="([A-Z]+\d+)"/.exec(attrs)?.[1] ?? "";
103
+ const type = /t="(\w+)"/.exec(attrs)?.[1];
104
+ const raw = /<v>([\s\S]*?)<\/v>/.exec(c[2] ?? "")?.[1] ?? /<t[^>]*>([\s\S]*?)<\/t>/.exec(c[2] ?? "")?.[1] ?? "";
105
+ const value = type === "s" ? (shared[Number(raw)] ?? "") : decode(raw);
106
+ values[ref ? columnOf(ref) : values.length] = value;
107
+ }
108
+ return Array.from(values, (v) => cell(v ?? ""));
109
+ });
110
+ const filled = rows.filter((row) => row.some(Boolean));
111
+ parts.push(`## ${names[n - 1] ?? `Sheet ${n}`}`);
112
+ if (!filled.length) {
113
+ parts.push("(empty)");
114
+ continue;
115
+ }
116
+ const width = Math.max(...filled.map((row) => row.length));
117
+ const pad = (row) => `| ${Array.from({ length: width }, (_, i) => row[i] ?? "").join(" | ")} |`;
118
+ const shown = filled.slice(0, MAX_SHEET_ROWS);
119
+ parts.push([pad(shown[0]), `|${" --- |".repeat(width)}`, ...shown.slice(1).map(pad)].join("\n"));
120
+ if (filled.length > MAX_SHEET_ROWS)
121
+ parts.push(`(${filled.length - MAX_SHEET_ROWS} more rows.)`);
122
+ }
123
+ return `${parts.join("\n\n")}\n`;
124
+ }
125
+ /** A Word document: headings, paragraphs, lists and tables. */
126
+ async function docx(file) {
127
+ const mammoth = await load("mammoth");
128
+ const { value: html } = await mammoth.convertToHtml({ path: file });
129
+ const images = (html.match(/<img\b/g) ?? []).length;
130
+ // Markdown with turndown (also optional); plain text without it.
131
+ const turndown = await load("turndown").catch(() => null);
132
+ const body = turndown
133
+ ? new turndown.default({ headingStyle: "atx" }).turndown(html.replace(/<img\b[^>]*>/g, ""))
134
+ : decode(html.replace(/<\/(p|h\d|li|tr)>/g, "\n").replace(/<[^>]+>/g, "")).trim();
135
+ return `# ${path.basename(file)}\n\n${body}\n${images ? `\n(${images} images left out.)\n` : ""}`;
136
+ }
137
+ /** An original's text as markdown; `from`/`to` pick slides of a deck. */
138
+ export async function extractText(file, from, to) {
139
+ const type = path.extname(file).slice(1).toLowerCase();
140
+ if (type === "docx")
141
+ return docx(file);
142
+ if (type === "pptx")
143
+ return pptx(file, from, to);
144
+ if (type === "xlsx")
145
+ return xlsx(file);
146
+ throw new Error(`extract_text reads DOCX, PPTX and XLSX; for "${path.basename(file)}" use Read (PDFs, images, text).`);
147
+ }
@@ -0,0 +1,10 @@
1
+ import { type KnowledgeStore, type PageType } from "./knowledgeStore.js";
2
+ /** Options of `createFileKnowledgeStore()`. */
3
+ export interface FileKnowledgeStoreOptions {
4
+ /** The agent's own page types, besides the kit's four. */
5
+ pageTypes?: readonly PageType[];
6
+ /** The sources folder: a summary's `file` field is given relative to it. */
7
+ sourcesDir?: string;
8
+ }
9
+ /** A `KnowledgeStore` over the markdown files of `knowledgeDir`. */
10
+ export declare function createFileKnowledgeStore(knowledgeDir: string, options?: FileKnowledgeStoreOptions): KnowledgeStore;