chapterhouse 0.7.0 → 0.8.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.
- package/agents/korg.agent.md +65 -0
- package/dist/api/korg.js +34 -0
- package/dist/api/korg.test.js +42 -0
- package/dist/api/server.js +238 -2
- package/dist/api/server.test.js +199 -0
- package/dist/config.js +28 -0
- package/dist/config.test.js +20 -0
- package/dist/copilot/agents.js +3 -4
- package/dist/copilot/agents.test.js +12 -1
- package/dist/copilot/orchestrator.js +12 -1
- package/dist/copilot/orchestrator.test.js +3 -7
- package/dist/copilot/system-message.js +11 -10
- package/dist/copilot/system-message.test.js +6 -1
- package/dist/copilot/tools.js +184 -376
- package/dist/copilot/tools.memory.test.js +32 -0
- package/dist/copilot/tools.wiki.test.js +53 -59
- package/dist/daemon.js +9 -0
- package/dist/memory/decisions.js +6 -5
- package/dist/memory/entities.js +20 -9
- package/dist/memory/hooks.js +151 -0
- package/dist/memory/hooks.test.js +325 -0
- package/dist/memory/hot-tier.js +37 -0
- package/dist/memory/hot-tier.test.js +30 -0
- package/dist/memory/housekeeping-scheduler.js +35 -0
- package/dist/memory/housekeeping-scheduler.test.js +50 -0
- package/dist/memory/inbox.js +10 -0
- package/dist/memory/index.js +3 -1
- package/dist/memory/migration.js +244 -0
- package/dist/memory/migration.test.js +100 -0
- package/dist/memory/reflect.js +273 -0
- package/dist/memory/reflect.test.js +254 -0
- package/dist/store/db.js +119 -4
- package/dist/store/db.test.js +19 -1
- package/dist/test/setup-env.js +1 -0
- package/dist/wiki/consolidation.js +641 -0
- package/dist/wiki/consolidation.test.js +140 -0
- package/dist/wiki/frontmatter.js +48 -0
- package/dist/wiki/frontmatter.test.js +42 -0
- package/dist/wiki/index-manager.js +246 -330
- package/dist/wiki/index-manager.test.js +138 -145
- package/dist/wiki/ingest.js +347 -0
- package/dist/wiki/ingest.test.js +111 -0
- package/dist/wiki/links.js +151 -0
- package/dist/wiki/links.test.js +176 -0
- package/dist/wiki/migrate-topics.test.js +16 -6
- package/dist/wiki/scheduler.js +118 -0
- package/dist/wiki/scheduler.test.js +64 -0
- package/dist/wiki/timeline.js +51 -0
- package/dist/wiki/timeline.test.js +65 -0
- package/dist/wiki/topic-structure.js +1 -1
- package/package.json +1 -1
- package/skills/pkb-ideas/SKILL.md +78 -0
- package/skills/pkb-ideas/_meta.json +4 -0
- package/skills/pkb-org/SKILL.md +82 -0
- package/skills/pkb-org/_meta.json +4 -0
- package/skills/pkb-people/SKILL.md +74 -0
- package/skills/pkb-people/_meta.json +4 -0
- package/skills/pkb-research/SKILL.md +83 -0
- package/skills/pkb-research/_meta.json +4 -0
- package/skills/pkb-source/SKILL.md +38 -0
- package/skills/pkb-source/_meta.json +4 -0
- package/skills/wiki-conventions/SKILL.md +5 -5
- package/web/dist/assets/{index-DuKYxMIR.css → index-5kz9aRU9.css} +1 -1
- package/web/dist/assets/{index-DytB69KC.js → index-BbX9RKf3.js} +91 -89
- package/web/dist/assets/index-BbX9RKf3.js.map +1 -0
- package/web/dist/index.html +2 -2
- package/dist/wiki/context.js +0 -138
- package/dist/wiki/fix.js +0 -335
- package/dist/wiki/fix.test.js +0 -350
- package/dist/wiki/lint.js +0 -451
- package/dist/wiki/lint.test.js +0 -329
- package/web/dist/assets/index-DytB69KC.js.map +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Wiki timeline — append-only timeline section management
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { readPage, writePage } from "./fs.js";
|
|
5
|
+
import { parseWikiFrontmatter } from "./frontmatter.js";
|
|
6
|
+
import { upsertWikiPage } from "./index-manager.js";
|
|
7
|
+
import { normalizeWikiPath } from "./path-utils.js";
|
|
8
|
+
const TIMELINE_HEADING = "## Timeline";
|
|
9
|
+
/**
|
|
10
|
+
* Append an entry to the `## Timeline` section of a wiki page.
|
|
11
|
+
* Creates the section (and the page itself) if absent.
|
|
12
|
+
* Calls upsertWikiPage to keep SQLite in sync after writing.
|
|
13
|
+
*/
|
|
14
|
+
export function appendTimeline(pagePath, entry, timestamp) {
|
|
15
|
+
const normalized = normalizeWikiPath(pagePath);
|
|
16
|
+
const ts = timestamp ?? new Date().toISOString();
|
|
17
|
+
const block = `\n### ${ts}\n\n${entry.trim()}\n`;
|
|
18
|
+
let content = readPage(normalized);
|
|
19
|
+
if (!content) {
|
|
20
|
+
// Create a minimal page with frontmatter + empty timeline
|
|
21
|
+
const title = deriveTitleFromPath(normalized);
|
|
22
|
+
content = `---\ntitle: ${title}\nsummary: ${title}\nupdated: ${ts.slice(0, 10)}\ntags: []\n---\n\n# ${title}\n\n${TIMELINE_HEADING}\n${block}`;
|
|
23
|
+
writePage(normalized, content);
|
|
24
|
+
}
|
|
25
|
+
else if (content.includes(TIMELINE_HEADING)) {
|
|
26
|
+
// Append after the last content in the timeline section
|
|
27
|
+
const idx = content.lastIndexOf(TIMELINE_HEADING);
|
|
28
|
+
const before = content.slice(0, idx + TIMELINE_HEADING.length);
|
|
29
|
+
const after = content.slice(idx + TIMELINE_HEADING.length);
|
|
30
|
+
content = before + after + block;
|
|
31
|
+
writePage(normalized, content);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Add timeline section at end of page
|
|
35
|
+
const trimmed = content.trimEnd();
|
|
36
|
+
content = trimmed + `\n\n${TIMELINE_HEADING}\n${block}`;
|
|
37
|
+
writePage(normalized, content);
|
|
38
|
+
}
|
|
39
|
+
// Keep SQLite in sync
|
|
40
|
+
const { parsed: fm } = parseWikiFrontmatter(readPage(normalized) ?? content);
|
|
41
|
+
const summary = fm.summary ?? deriveTitleFromPath(normalized);
|
|
42
|
+
upsertWikiPage(normalized, fm, summary);
|
|
43
|
+
}
|
|
44
|
+
function deriveTitleFromPath(path) {
|
|
45
|
+
const segs = path.split("/").filter(Boolean);
|
|
46
|
+
const file = segs[segs.length - 1] || path;
|
|
47
|
+
const base = file.replace(/\.md$/, "");
|
|
48
|
+
const titleBase = base === "index" && segs.length >= 2 ? segs[segs.length - 2] : base;
|
|
49
|
+
return titleBase.split(/[-_]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=timeline.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Timeline tests — appendTimeline
|
|
3
|
+
// Sandbox: single CHAPTERHOUSE_HOME per file to avoid module-singleton confusion
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
let SANDBOX;
|
|
10
|
+
let mods;
|
|
11
|
+
test.before(async () => {
|
|
12
|
+
mkdirSync(join(process.cwd(), ".test-work"), { recursive: true });
|
|
13
|
+
SANDBOX = mkdtempSync(join(process.cwd(), ".test-work", "timeline-"));
|
|
14
|
+
process.env.CHAPTERHOUSE_HOME = SANDBOX;
|
|
15
|
+
const nonce = `${Date.now()}-${Math.random()}`;
|
|
16
|
+
const timeline = await import(new URL(`./timeline.js?c=${nonce}`, import.meta.url).href);
|
|
17
|
+
const wikiFs = await import(new URL(`./fs.js?c=${nonce}`, import.meta.url).href);
|
|
18
|
+
const indexManager = await import(new URL(`./index-manager.js?c=${nonce}`, import.meta.url).href);
|
|
19
|
+
mods = { timeline, wikiFs, indexManager };
|
|
20
|
+
mods.wikiFs.ensureWikiStructure();
|
|
21
|
+
});
|
|
22
|
+
test.after(() => {
|
|
23
|
+
try {
|
|
24
|
+
rmSync(SANDBOX, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
catch { /* best-effort */ }
|
|
27
|
+
});
|
|
28
|
+
test("appendTimeline creates page if it doesn't exist", () => {
|
|
29
|
+
mods.timeline.appendTimeline("pages/topics/rust/index.md", "First mention of Rust");
|
|
30
|
+
const content = mods.wikiFs.readPage("pages/topics/rust/index.md");
|
|
31
|
+
assert.ok(content, "Page should be created");
|
|
32
|
+
assert.ok(content.includes("## Timeline"), "Should have Timeline section");
|
|
33
|
+
assert.ok(content.includes("First mention of Rust"), "Should contain entry text");
|
|
34
|
+
});
|
|
35
|
+
test("appendTimeline creates Timeline section if absent", () => {
|
|
36
|
+
mods.wikiFs.writePage("pages/topics/go/index.md", "---\ntitle: Go\nsummary: Go programming language\nupdated: 2026-01-01\ntags: []\n---\n\n# Go\n\nSimple language.\n");
|
|
37
|
+
mods.timeline.appendTimeline("pages/topics/go/index.md", "Go 1.22 released");
|
|
38
|
+
const content = mods.wikiFs.readPage("pages/topics/go/index.md");
|
|
39
|
+
assert.ok(content.includes("## Timeline"), "Should have added Timeline section");
|
|
40
|
+
assert.ok(content.includes("Go 1.22 released"), "Should contain the entry");
|
|
41
|
+
assert.ok(content.includes("# Go"), "Should preserve original content");
|
|
42
|
+
});
|
|
43
|
+
test("appendTimeline is append-only: existing entries are unchanged", () => {
|
|
44
|
+
mods.timeline.appendTimeline("pages/topics/node/index.md", "First entry");
|
|
45
|
+
const after1 = mods.wikiFs.readPage("pages/topics/node/index.md");
|
|
46
|
+
assert.ok(after1.includes("First entry"));
|
|
47
|
+
mods.timeline.appendTimeline("pages/topics/node/index.md", "Second entry");
|
|
48
|
+
const after2 = mods.wikiFs.readPage("pages/topics/node/index.md");
|
|
49
|
+
assert.ok(after2.includes("First entry"), "First entry should still be present");
|
|
50
|
+
assert.ok(after2.includes("Second entry"), "Second entry should be appended");
|
|
51
|
+
assert.ok(after2.indexOf("First entry") < after2.indexOf("Second entry"), "Order should be preserved");
|
|
52
|
+
});
|
|
53
|
+
test("appendTimeline calls upsertWikiPage to sync SQLite", () => {
|
|
54
|
+
mods.timeline.appendTimeline("pages/topics/typescript/index.md", "TypeScript 5.0 notes");
|
|
55
|
+
const results = mods.indexManager.wikiSearch("typescript");
|
|
56
|
+
assert.ok(results.some((r) => r.path === "pages/topics/typescript/index.md"), "Page should be in SQLite after appendTimeline");
|
|
57
|
+
});
|
|
58
|
+
test("appendTimeline adds timestamp heading to each entry", () => {
|
|
59
|
+
const ts = "2026-05-14T20:45:55.900Z";
|
|
60
|
+
mods.timeline.appendTimeline("pages/topics/deno/index.md", "Deno 2.0 released", ts);
|
|
61
|
+
const content = mods.wikiFs.readPage("pages/topics/deno/index.md");
|
|
62
|
+
assert.ok(content.includes(`### ${ts}`), "Should have timestamp heading");
|
|
63
|
+
assert.ok(content.includes("Deno 2.0 released"), "Should have entry text");
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=timeline.test.js.map
|
|
@@ -31,7 +31,7 @@ export const EXEMPT_PREFIXES = [
|
|
|
31
31
|
export const EXEMPT_PAGES = [
|
|
32
32
|
"pages/index.md",
|
|
33
33
|
];
|
|
34
|
-
/** Map a
|
|
34
|
+
/** Map a legacy category name to its directory under pages/. */
|
|
35
35
|
const CATEGORY_DIR_MAP = {
|
|
36
36
|
person: "people",
|
|
37
37
|
people: "people",
|
package/package.json
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pkb-ideas
|
|
3
|
+
description: Lightweight idea capture in the PKB. Creates pages/ideas/<slug>.md with a status lifecycle (seed → germinating → mature → archived) and links to related topics.
|
|
4
|
+
when_to_load: When the user wants to capture a new idea, hypothesis, or proposal to develop over time.
|
|
5
|
+
related_skills:
|
|
6
|
+
- wiki-conventions
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# PKB Idea Capture
|
|
10
|
+
|
|
11
|
+
Use this skill to record and develop ideas in the wiki. Ideas live as flat pages (not topic directories) under `pages/ideas/`.
|
|
12
|
+
|
|
13
|
+
## Page Location
|
|
14
|
+
|
|
15
|
+
`pages/ideas/<slug>.md`
|
|
16
|
+
|
|
17
|
+
- Slug is lowercase kebab-case from the idea title (e.g. `async-wiki-sync`, `personal-memory-tiering`).
|
|
18
|
+
- Ideas are flat pages, not directories — no sub-facets.
|
|
19
|
+
|
|
20
|
+
## Frontmatter Template
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
---
|
|
24
|
+
title: <Idea Title>
|
|
25
|
+
entity_type: idea
|
|
26
|
+
status: seed
|
|
27
|
+
summary: <One sentence capturing the core hypothesis or proposal>
|
|
28
|
+
tags: [<topic-tag>]
|
|
29
|
+
related:
|
|
30
|
+
- pages/topics/<topic-slug>/index.md
|
|
31
|
+
updated: <YYYY-MM-DD>
|
|
32
|
+
---
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Status Lifecycle
|
|
36
|
+
|
|
37
|
+
| Status | Meaning |
|
|
38
|
+
|--------|---------|
|
|
39
|
+
| `seed` | Just captured — raw, unvalidated |
|
|
40
|
+
| `germinating` | Actively exploring — some evidence or development |
|
|
41
|
+
| `mature` | Well-developed — ready to act on or publish |
|
|
42
|
+
| `archived` | Abandoned or superseded — kept for reference |
|
|
43
|
+
|
|
44
|
+
Progress the status forward as the idea develops. Move `archived` ideas to `pages/_archive/ideas/<slug>.md`.
|
|
45
|
+
|
|
46
|
+
## Page Structure
|
|
47
|
+
|
|
48
|
+
```markdown
|
|
49
|
+
## Hypothesis
|
|
50
|
+
|
|
51
|
+
<Core claim or proposal — what would be true if this idea is right?>
|
|
52
|
+
|
|
53
|
+
## Evidence / Notes
|
|
54
|
+
|
|
55
|
+
- <Supporting observation, date>
|
|
56
|
+
- <Counter-evidence or open question>
|
|
57
|
+
|
|
58
|
+
## Next Steps
|
|
59
|
+
|
|
60
|
+
- [ ] <Concrete action to develop this idea>
|
|
61
|
+
|
|
62
|
+
## Related
|
|
63
|
+
|
|
64
|
+
- [[<Topic or Project>]] — <connection>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Workflow
|
|
68
|
+
|
|
69
|
+
1. Run `wiki_search` for the idea topic to check for existing related ideas or pages.
|
|
70
|
+
2. Create the page with `wiki_update` using the template above.
|
|
71
|
+
3. Set `status: seed` for fresh captures.
|
|
72
|
+
4. Link to relevant topic pages in `related:` frontmatter.
|
|
73
|
+
5. When returning to an idea, update the status and append new evidence or notes.
|
|
74
|
+
|
|
75
|
+
## Link Conventions
|
|
76
|
+
|
|
77
|
+
- Add `related:` links to topic pages (`pages/topics/<slug>/index.md`) that this idea draws from.
|
|
78
|
+
- When an idea matures into a decision or project, add a `related:` link to the decision or project page and set status to `archived`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pkb-org
|
|
3
|
+
description: Capture and maintain organization knowledge in the PKB. Creates or updates pages/orgs/<slug>/index.md with compiled truth, interaction timeline, and bidirectional links to people, projects, and topics.
|
|
4
|
+
when_to_load: When the user mentions an organization, company, team, or employer and wants it recorded or updated in the wiki.
|
|
5
|
+
related_skills:
|
|
6
|
+
- wiki-conventions
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# PKB Organization Capture
|
|
10
|
+
|
|
11
|
+
Use this skill when capturing or updating knowledge about an organization (company, team, consortium, employer) in the wiki.
|
|
12
|
+
|
|
13
|
+
## Page Location
|
|
14
|
+
|
|
15
|
+
Organization pages live at `pages/orgs/<slug>/index.md`.
|
|
16
|
+
|
|
17
|
+
- Slug is lowercase kebab-case from the org name (e.g. `acme-corp`, `rust-foundation`).
|
|
18
|
+
- Create `pages/orgs/<slug>/` as a topic directory; `index.md` is the compiled truth overview.
|
|
19
|
+
|
|
20
|
+
## Frontmatter Template
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
---
|
|
24
|
+
title: <Org Name>
|
|
25
|
+
entity_type: org
|
|
26
|
+
summary: <One sentence describing what this org does>
|
|
27
|
+
tags: [<industry-tag>, <tech-tag>]
|
|
28
|
+
related:
|
|
29
|
+
- pages/people/<person-slug>/index.md
|
|
30
|
+
- pages/projects/<project-slug>/index.md
|
|
31
|
+
- pages/topics/<topic-slug>/index.md
|
|
32
|
+
updated: <YYYY-MM-DD>
|
|
33
|
+
---
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Page Structure
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
## Overview
|
|
40
|
+
|
|
41
|
+
<Compiled truth: 2–4 sentences about the org — mission, size, domain, key products.>
|
|
42
|
+
|
|
43
|
+
## People
|
|
44
|
+
|
|
45
|
+
- [[<Person Name>]] — <role>
|
|
46
|
+
|
|
47
|
+
## Projects
|
|
48
|
+
|
|
49
|
+
- [[<Project Name>]] — <brief description>
|
|
50
|
+
|
|
51
|
+
## Topics
|
|
52
|
+
|
|
53
|
+
- [[<Topic>]] — <relevance>
|
|
54
|
+
|
|
55
|
+
## Timeline
|
|
56
|
+
|
|
57
|
+
- _(<YYYY-MM-DD>)_ <What happened in this interaction or what was learned>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Workflow
|
|
61
|
+
|
|
62
|
+
1. Run `wiki_search` for the org name to check if a page exists.
|
|
63
|
+
2. If it exists, read it with `wiki_read`. If not, create it with the template above.
|
|
64
|
+
3. Append a new timeline entry with today's date.
|
|
65
|
+
4. Update compiled truth (Overview section) if materially new facts emerged.
|
|
66
|
+
5. Add or update `related` frontmatter with links to people/projects/topics.
|
|
67
|
+
6. Write with `wiki_update`.
|
|
68
|
+
|
|
69
|
+
## Link Conventions
|
|
70
|
+
|
|
71
|
+
- Bidirectional: after writing the org page, check linked person/project pages and add `related:` back-links where they're missing.
|
|
72
|
+
- Use `[[Page Name]]` for inline cross-references in body text.
|
|
73
|
+
- Keep `related:` frontmatter in sync with `[[...]]` body links.
|
|
74
|
+
|
|
75
|
+
## Status Lifecycle
|
|
76
|
+
|
|
77
|
+
Add a `status` field in frontmatter when relevant:
|
|
78
|
+
|
|
79
|
+
- `active` — current engagement or known ongoing relationship
|
|
80
|
+
- `past` — former employer, closed project, ended relationship
|
|
81
|
+
- `prospect` — potential future engagement
|
|
82
|
+
- `archived` — org no longer relevant; move page to `pages/_archive/orgs/<slug>/`
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pkb-people
|
|
3
|
+
description: Capture and maintain a PKB profile for a person — compiled truth, interaction timeline, and follow-up extraction.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PKB People Capture
|
|
7
|
+
|
|
8
|
+
Creates and maintains wiki profiles for people in the Personal Knowledge Base.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- User mentions a person they've interacted with
|
|
13
|
+
- User says "add {name} to my PKB", "capture notes about {name}", "I met with {name}"
|
|
14
|
+
- User wants to update someone's profile with new information
|
|
15
|
+
- After meetings, calls, or interactions where a person is central
|
|
16
|
+
|
|
17
|
+
## Person Page Structure
|
|
18
|
+
|
|
19
|
+
Page path: `pages/people/{slug}/index.md`
|
|
20
|
+
|
|
21
|
+
Sections:
|
|
22
|
+
1. **Frontmatter** — title, summary, updated, tags
|
|
23
|
+
2. **## Summary** — compiled truth: role, affiliation, key facts, relationship to user (rewritten on updates)
|
|
24
|
+
3. **## Timeline** — append-only record of interactions and observations
|
|
25
|
+
|
|
26
|
+
## Steps
|
|
27
|
+
|
|
28
|
+
### 1. Find or Create Profile
|
|
29
|
+
|
|
30
|
+
Search first:
|
|
31
|
+
```
|
|
32
|
+
wiki_search("{person name}")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If no profile exists, create one:
|
|
36
|
+
```
|
|
37
|
+
wiki_update(
|
|
38
|
+
path="pages/people/{slug}/index.md",
|
|
39
|
+
title="{Person Name}",
|
|
40
|
+
summary="{one-line description of who this person is}",
|
|
41
|
+
content="---\ntitle: {Person Name}\nsummary: {description}\nupdated: {today}\ntags: [person]\nmetadata:\n entity_type: person\n---\n\n# {Person Name}\n\n## Summary\n\n{Compiled truth about this person: role, org, relationship to user, key facts}\n\n## Timeline\n"
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Add Interaction to Timeline
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
wiki_append_timeline(
|
|
49
|
+
page="pages/people/{slug}/index.md",
|
|
50
|
+
entry="{Description of interaction, meeting, or observation}\n\n{Key points discussed or observed}"
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Update Compiled Truth
|
|
55
|
+
|
|
56
|
+
After significant new information, rewrite the `## Summary` section via `wiki_update` to reflect current understanding. Keep it factual and concise (3–8 bullet points).
|
|
57
|
+
|
|
58
|
+
### 4. Extract Follow-ups
|
|
59
|
+
|
|
60
|
+
If the interaction produced action items or follow-ups, propose them to memory:
|
|
61
|
+
```
|
|
62
|
+
memory_propose(kind="action_item", payload={
|
|
63
|
+
title: "Follow up with {name} on {topic}",
|
|
64
|
+
detail: "{context from interaction}",
|
|
65
|
+
due_at: "{date if mentioned}"
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Notes
|
|
70
|
+
|
|
71
|
+
- Always rewrite the summary after new interactions — don't let it become stale
|
|
72
|
+
- Timeline entries get their own `### {ISO timestamp}` heading automatically
|
|
73
|
+
- Use `wiki_ingest_source` with `type: "text"` to ingest meeting notes or email text
|
|
74
|
+
- Keep summaries under 200 chars for the frontmatter field; use the `## Summary` section for detail
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pkb-research
|
|
3
|
+
description: Manage a PKB research session — create a research hub, ingest sources, maintain a living synthesis document.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PKB Research Session
|
|
7
|
+
|
|
8
|
+
Manages a structured research session in the Personal Knowledge Base.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- User wants to research a topic deeply using multiple sources
|
|
13
|
+
- User says "start a research session on X", "help me research X", "I'm researching X"
|
|
14
|
+
- User wants a synthesis document that stays updated as they add sources
|
|
15
|
+
|
|
16
|
+
## Session Structure
|
|
17
|
+
|
|
18
|
+
For a research topic `{slug}` (derived from the topic name):
|
|
19
|
+
|
|
20
|
+
- **Hub page**: `pages/research/{slug}/index.md` — overview, links, and timeline
|
|
21
|
+
- **Synthesis**: `pages/research/{slug}/synthesis.md` — compiled truth, rewritten after each source
|
|
22
|
+
|
|
23
|
+
## Steps
|
|
24
|
+
|
|
25
|
+
### 1. Open the Session
|
|
26
|
+
|
|
27
|
+
Create the hub page if it doesn't exist:
|
|
28
|
+
```
|
|
29
|
+
wiki_update(
|
|
30
|
+
path="pages/research/{slug}/index.md",
|
|
31
|
+
title="{Topic Name} Research",
|
|
32
|
+
summary="Research hub for {topic}",
|
|
33
|
+
content="---\ntitle: {Topic Name} Research\nsummary: Research hub for {topic}\nupdated: {today}\ntags: [research]\n---\n\n# {Topic Name} Research\n\n## Sources\n\n_(none yet)_\n\n## Timeline\n"
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then open a timeline entry:
|
|
38
|
+
```
|
|
39
|
+
wiki_append_timeline(page="pages/research/{slug}/index.md", entry="Research session opened.")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Ingest Each Source
|
|
43
|
+
|
|
44
|
+
For each source the user provides:
|
|
45
|
+
```
|
|
46
|
+
wiki_ingest_source(source="{url_or_text}", topic="{slug}")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then update the synthesis:
|
|
50
|
+
```
|
|
51
|
+
wiki_update(
|
|
52
|
+
path="pages/research/{slug}/synthesis.md",
|
|
53
|
+
title="{Topic Name} Synthesis",
|
|
54
|
+
summary="Compiled synthesis for {topic} research",
|
|
55
|
+
content="... rewritten synthesis based on all sources so far ..."
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Append to hub timeline:
|
|
60
|
+
```
|
|
61
|
+
wiki_append_timeline(
|
|
62
|
+
page="pages/research/{slug}/index.md",
|
|
63
|
+
entry="Source ingested: {title}\n\nKey takeaways: {1-2 sentences}",
|
|
64
|
+
source_id="{source_id from ingest result}"
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 3. Close the Session
|
|
69
|
+
|
|
70
|
+
When the user is done:
|
|
71
|
+
```
|
|
72
|
+
wiki_append_timeline(
|
|
73
|
+
page="pages/research/{slug}/index.md",
|
|
74
|
+
entry="Research session closed. Final synthesis written to pages/research/{slug}/synthesis.md"
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Notes
|
|
79
|
+
|
|
80
|
+
- The synthesis document is rewritten (not appended) after each new source
|
|
81
|
+
- The hub timeline is always append-only
|
|
82
|
+
- Provide the user a brief summary after each source ingestion
|
|
83
|
+
- If a source was already ingested, note it and skip synthesis update
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pkb-source
|
|
3
|
+
description: Ingest a source (URL, PDF, repo, or text) into the PKB wiki and get a summary of pages created or updated.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PKB Source Ingestion
|
|
7
|
+
|
|
8
|
+
Accepts a URL, PDF file path, Git repo URL, or raw text and ingests it into the Personal Knowledge Base.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- User shares a URL they want saved to their PKB
|
|
13
|
+
- User wants to ingest a PDF, Git repo, or raw text
|
|
14
|
+
- User says "add this to my PKB", "save this source", "ingest this"
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
1. **Determine source type** — if not specified, auto-detect:
|
|
19
|
+
- `http://` or `https://` URL → `url` (or `repo` for GitHub/GitLab)
|
|
20
|
+
- Path ending in `.pdf` → `pdf`
|
|
21
|
+
- Everything else → `text`
|
|
22
|
+
|
|
23
|
+
2. **Call `wiki_ingest_source`**:
|
|
24
|
+
```
|
|
25
|
+
wiki_ingest_source(source="{source}", type="{type}", topic="{optional topic hint}")
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
3. **Report results** to the user:
|
|
29
|
+
- Number of pages created/updated
|
|
30
|
+
- List of entity pages (names and paths)
|
|
31
|
+
- Source ID for future reference
|
|
32
|
+
- If `already_existed: true`, tell the user it was already in the PKB
|
|
33
|
+
|
|
34
|
+
## Notes
|
|
35
|
+
|
|
36
|
+
- Ingestion is idempotent — the same source will not be duplicated
|
|
37
|
+
- Entity extraction uses LLM; it may miss entities for very short or unstructured content
|
|
38
|
+
- For raw text >10KB, consider splitting into multiple topical ingestions with `topic` hints
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wiki-conventions
|
|
3
3
|
description: Use when creating, editing, linting, restructuring, or reviewing Chapterhouse wiki content.
|
|
4
|
-
when_to_load: Wiki tools,
|
|
4
|
+
when_to_load: Wiki tools, wiki_update/wiki_ingest_source workflows, or direct edits under the Chapterhouse wiki.
|
|
5
5
|
related_skills:
|
|
6
6
|
- squad
|
|
7
7
|
---
|
|
@@ -12,7 +12,7 @@ Use this skill before creating, editing, linting, restructuring, archiving, or r
|
|
|
12
12
|
|
|
13
13
|
## Orientation Ritual — Mandatory Before Any Wiki Write
|
|
14
14
|
|
|
15
|
-
Perform this checklist before using `wiki_update`, `
|
|
15
|
+
Perform this checklist before using `wiki_update`, `wiki_append_timeline`, or `wiki_ingest_source`:
|
|
16
16
|
|
|
17
17
|
1. Read `pages/index.md` with `wiki_read`.
|
|
18
18
|
2. Scan the last 20-30 entries of `pages/_meta/log.md` to see recent edits, archives, and reorganizations.
|
|
@@ -52,8 +52,8 @@ Do not skip the ritual because the task feels small. The point is to avoid dupli
|
|
|
52
52
|
|
|
53
53
|
## Decision Recording Protocol
|
|
54
54
|
|
|
55
|
-
- With `remember`, use `category: "decision"` plus both `about` and `entity`.
|
|
56
55
|
- Record the decision against the actual entity that owns it.
|
|
56
|
+
- Use `wiki_update` to write to that entity's canonical `decisions.md` page.
|
|
57
57
|
- Keep decision facets canonical; prefer `decisions.md` over inventing one-off decision page names.
|
|
58
58
|
|
|
59
59
|
## Cross-Reference Standards
|
|
@@ -139,7 +139,7 @@ Use `autostub: true` only for genuine stubs that are intentionally thin and expe
|
|
|
139
139
|
|
|
140
140
|
- A durable fact about a named project, tool, person, org, topic, or area -> `pages/<category>/<slug>/index.md`
|
|
141
141
|
- A durable subtopic of an existing entity -> `pages/<category>/<slug>/<facet>.md`
|
|
142
|
-
- A decision about an entity ->
|
|
142
|
+
- A decision about an entity -> write to that entity's `decisions.md` with `wiki_update`
|
|
143
143
|
- A user preference or routine -> the corresponding flat category page
|
|
144
|
-
- Source material you want preserved before synthesis -> `
|
|
144
|
+
- Source material you want preserved before synthesis -> `wiki_ingest_source`
|
|
145
145
|
- A passing mention with no durable value yet -> do not create a new page; add it to existing context or leave it out
|