@treeseed/dsh-chapters 0.1.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 (119) hide show
  1. package/LICENSE +14 -0
  2. package/README.md +644 -0
  3. package/cordis.patch.yml +88 -0
  4. package/docs/architecture.md +460 -0
  5. package/docs/contract.md +375 -0
  6. package/docs/development.md +437 -0
  7. package/docs/host-compaction-seam.md +117 -0
  8. package/docs/knowledge-repo.md +730 -0
  9. package/docs/provider.md +98 -0
  10. package/docs/verify.md +200 -0
  11. package/lib/archive.d.ts +88 -0
  12. package/lib/archive.js +114 -0
  13. package/lib/archive.js.map +1 -0
  14. package/lib/arrival.d.ts +23 -0
  15. package/lib/arrival.js +86 -0
  16. package/lib/arrival.js.map +1 -0
  17. package/lib/artifact-query.d.ts +53 -0
  18. package/lib/artifact-query.js +130 -0
  19. package/lib/artifact-query.js.map +1 -0
  20. package/lib/client.js +234 -0
  21. package/lib/client.js.map +1 -0
  22. package/lib/commands.d.ts +28 -0
  23. package/lib/commands.js +305 -0
  24. package/lib/commands.js.map +1 -0
  25. package/lib/compose.d.ts +30 -0
  26. package/lib/compose.js +131 -0
  27. package/lib/compose.js.map +1 -0
  28. package/lib/continue-core.d.ts +153 -0
  29. package/lib/continue-core.js +252 -0
  30. package/lib/continue-core.js.map +1 -0
  31. package/lib/engine-core.d.ts +219 -0
  32. package/lib/engine-core.js +405 -0
  33. package/lib/engine-core.js.map +1 -0
  34. package/lib/engine.d.ts +211 -0
  35. package/lib/engine.js +571 -0
  36. package/lib/engine.js.map +1 -0
  37. package/lib/enrich-queue.d.ts +66 -0
  38. package/lib/enrich-queue.js +100 -0
  39. package/lib/enrich-queue.js.map +1 -0
  40. package/lib/enrich-store.d.ts +31 -0
  41. package/lib/enrich-store.js +103 -0
  42. package/lib/enrich-store.js.map +1 -0
  43. package/lib/enrich-wire.d.ts +73 -0
  44. package/lib/enrich-wire.js +209 -0
  45. package/lib/enrich-wire.js.map +1 -0
  46. package/lib/enrich.d.ts +73 -0
  47. package/lib/enrich.js +150 -0
  48. package/lib/enrich.js.map +1 -0
  49. package/lib/gitops.d.ts +107 -0
  50. package/lib/gitops.js +539 -0
  51. package/lib/gitops.js.map +1 -0
  52. package/lib/index.d.ts +81 -0
  53. package/lib/index.js +305 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/indexing.d.ts +85 -0
  56. package/lib/indexing.js +212 -0
  57. package/lib/indexing.js.map +1 -0
  58. package/lib/injections.d.ts +66 -0
  59. package/lib/injections.js +83 -0
  60. package/lib/injections.js.map +1 -0
  61. package/lib/notice.d.ts +60 -0
  62. package/lib/notice.js +84 -0
  63. package/lib/notice.js.map +1 -0
  64. package/lib/provider.d.ts +44 -0
  65. package/lib/provider.js +66 -0
  66. package/lib/provider.js.map +1 -0
  67. package/lib/redact.d.ts +24 -0
  68. package/lib/redact.js +85 -0
  69. package/lib/redact.js.map +1 -0
  70. package/lib/registry.d.ts +120 -0
  71. package/lib/registry.js +130 -0
  72. package/lib/registry.js.map +1 -0
  73. package/lib/render.d.ts +40 -0
  74. package/lib/render.js +280 -0
  75. package/lib/render.js.map +1 -0
  76. package/lib/repo.d.ts +78 -0
  77. package/lib/repo.js +184 -0
  78. package/lib/repo.js.map +1 -0
  79. package/lib/rules-commands.d.ts +24 -0
  80. package/lib/rules-commands.js +138 -0
  81. package/lib/rules-commands.js.map +1 -0
  82. package/lib/rules.d.ts +90 -0
  83. package/lib/rules.js +185 -0
  84. package/lib/rules.js.map +1 -0
  85. package/lib/search.d.ts +48 -0
  86. package/lib/search.js +152 -0
  87. package/lib/search.js.map +1 -0
  88. package/lib/signature.d.ts +28 -0
  89. package/lib/signature.js +149 -0
  90. package/lib/signature.js.map +1 -0
  91. package/lib/store.d.ts +130 -0
  92. package/lib/store.js +235 -0
  93. package/lib/store.js.map +1 -0
  94. package/lib/sync.d.ts +183 -0
  95. package/lib/sync.js +719 -0
  96. package/lib/sync.js.map +1 -0
  97. package/lib/tools.d.ts +84 -0
  98. package/lib/tools.js +581 -0
  99. package/lib/tools.js.map +1 -0
  100. package/lib/treedx/client.d.ts +57 -0
  101. package/lib/treedx/client.js +82 -0
  102. package/lib/treedx/client.js.map +1 -0
  103. package/lib/treedx/provider.d.ts +39 -0
  104. package/lib/treedx/provider.js +479 -0
  105. package/lib/treedx/provider.js.map +1 -0
  106. package/lib/treedx/state.d.ts +27 -0
  107. package/lib/treedx/state.js +65 -0
  108. package/lib/treedx/state.js.map +1 -0
  109. package/lib/types.d.ts +100 -0
  110. package/lib/types.js +19 -0
  111. package/lib/types.js.map +1 -0
  112. package/lib/vocabulary.d.ts +41 -0
  113. package/lib/vocabulary.js +118 -0
  114. package/lib/vocabulary.js.map +1 -0
  115. package/package.json +132 -0
  116. package/presets/chapters/agent.cordis.yml +272 -0
  117. package/presets/chapters/preset.yml +3 -0
  118. package/presets/probe-roots/agent.cordis.yml +11 -0
  119. package/presets/probe-roots/preset.yml +3 -0
package/lib/render.js ADDED
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Chapter renderer: durable events in a seq range → one Markdown file, deterministically.
3
+ *
4
+ * This module is pure and cordis-free. The model never writes chapter text (invariant 4 in AGENTS.md) — it
5
+ * supplies ranges, and every byte of the body comes from the log. So correctness here IS the "retrievable
6
+ * memory" promise.
7
+ *
8
+ * Two hazards this file exists to handle:
9
+ *
10
+ * 1. **Fence collision.** Conversation text routinely contains ``` blocks. A Markdown file that embeds a
11
+ * triple-backtick fence inside a triple-backtick fence terminates early and silently truncates the rest
12
+ * of the chapter. `longerFence()` solves this by measuring the longest backtick run in the payload and
13
+ * using one longer. Verified by test, because the failure is invisible by eye.
14
+ * 2. **Silent holes.** An event that renders to nothing must never vanish quietly — it is recorded in
15
+ * `stats.unrenderedSeqs` so a gap becomes a visible assertion rather than lost history.
16
+ * 3. **Borrowed context.** Host-injected instruction/snapshot/catalog text is project state, not
17
+ * conversation: it is screened at this chokepoint (src/injections.ts) and replaced by a countable
18
+ * marker, so a project's own docs never travel in the shared knowledge corpus.
19
+ */
20
+ import { createHash } from 'node:crypto';
21
+ import { DEFAULT_REDACTIONS, redactText } from "./redact.js";
22
+ import { omittedMarker, screenConversation, stripReminderSpans } from "./injections.js";
23
+ /** chars/4 until the host tokenMeter is wired in. Labelled an estimate everywhere it surfaces. */
24
+ export const estimateTokens = (text) => Math.ceil(text.length / 4);
25
+ /**
26
+ * A fence strictly longer than any backtick run inside `body`, so embedded code blocks cannot close it.
27
+ * Markdown requires the closing fence to be at least as long as the opening one.
28
+ */
29
+ export function longerFence(body) {
30
+ let max = 0;
31
+ for (const match of body.matchAll(/`+/g))
32
+ max = Math.max(max, match[0].length);
33
+ return '`'.repeat(Math.max(3, max + 1));
34
+ }
35
+ /** Wrapped in a fence long enough to survive any content, including nested fences. */
36
+ export function fenced(content, info = '') {
37
+ const fence = longerFence(content);
38
+ return `${fence}${info}\n${content}\n${fence}`;
39
+ }
40
+ export const sha256 = (text) => createHash('sha256').update(text, 'utf8').digest('hex');
41
+ /** `artifacts/8f/8f3a1c9d….txt` — sharded so one directory never holds the whole subtree. */
42
+ export const artifactPath = (hash) => `artifacts/${hash.slice(0, 2)}/${hash}.txt`;
43
+ export function artifactRef(text, opts) {
44
+ const hash = sha256(text);
45
+ return {
46
+ sha256: hash,
47
+ path: artifactPath(hash),
48
+ bytes: Buffer.byteLength(text, 'utf8'),
49
+ content: text,
50
+ inlined: opts.inlined,
51
+ toolName: opts.toolName,
52
+ sourceSeq: opts.sourceSeq,
53
+ };
54
+ }
55
+ const blocksOf = (value) => Array.isArray(value) ? value : [];
56
+ const textOf = (value) => blocksOf(value)
57
+ .filter((b) => b.type === 'text')
58
+ .map((b) => b.text ?? '')
59
+ .join('\n');
60
+ /** Flatten a tool-result payload, which nests a content array inside a tool-result block. */
61
+ function toolResultText(data) {
62
+ const message = data.message;
63
+ const inner = blocksOf(message?.content ?? data.content);
64
+ const parts = [];
65
+ for (const block of inner) {
66
+ if (block.type === 'text')
67
+ parts.push(block.text ?? '');
68
+ else if (block.type === 'tool-result') {
69
+ const nested = block.content;
70
+ parts.push(typeof nested === 'string' ? nested : textOf(nested));
71
+ }
72
+ }
73
+ return parts.filter((p) => p.length > 0).join('\n');
74
+ }
75
+ /**
76
+ * Validate model-supplied ranges. Never trust them (hard rule): an overlap duplicates archived text and a
77
+ * gap silently drops history, so both are errors here rather than data that quietly goes missing.
78
+ */
79
+ export function validateRanges(chapters, archiveCeiling) {
80
+ if (chapters.length === 0)
81
+ throw new Error('no chapters supplied');
82
+ chapters.forEach((c, i) => {
83
+ if (c.startSeq > c.endSeq) {
84
+ throw new Error(`chapter ${i} ("${c.title}") has startSeq ${c.startSeq} above endSeq ${c.endSeq}`);
85
+ }
86
+ if (c.endSeq > archiveCeiling) {
87
+ throw new Error(`chapter ${i} ("${c.title}") ends at seq ${c.endSeq}, past the archive ceiling ${archiveCeiling}`);
88
+ }
89
+ if (i > 0) {
90
+ const prev = chapters[i - 1];
91
+ if (c.startSeq <= prev.startSeq) {
92
+ throw new Error(`chapter ${i} ("${c.title}") descends: starts ${c.startSeq} after ${prev.startSeq}`);
93
+ }
94
+ if (c.startSeq <= prev.endSeq) {
95
+ throw new Error(`chapter ${i} ("${c.title}") overlaps "${prev.title}": ${c.startSeq}-${prev.endSeq} in both`);
96
+ }
97
+ }
98
+ });
99
+ return chapters;
100
+ }
101
+ /** Enumerate tool results in a range so the model can decide keep/defer against real sizes. */
102
+ export function toolResultCandidates(events, range) {
103
+ const out = [];
104
+ for (const ev of events) {
105
+ if (ev.type !== 'tool/result' || ev.seq < range.startSeq || ev.seq > range.endSeq)
106
+ continue;
107
+ const text = toolResultText(ev.data ?? {});
108
+ const call = events.find((c) => c.type === 'tool/call' && c.seq < ev.seq && sameCall(c, ev));
109
+ out.push({
110
+ seq: ev.seq,
111
+ toolName: String(call?.data?.name ?? ev.data?.toolName ?? 'tool'),
112
+ bytes: Buffer.byteLength(text, 'utf8'),
113
+ estimatedTokens: estimateTokens(text),
114
+ excerpt: text.slice(0, 160),
115
+ });
116
+ }
117
+ return out;
118
+ }
119
+ function sameCall(call, result) {
120
+ const callId = call.data?.callId;
121
+ const message = result.data?.message;
122
+ const source = message?.source;
123
+ const resultCallId = source?.callId ?? result.data?.callId;
124
+ return callId !== undefined && resultCallId !== undefined && String(callId) === String(resultCallId);
125
+ }
126
+ /**
127
+ * Render one chapter. `overrides` is the model's sparse exception list; anything not named follows the
128
+ * default rule (small results inline, large ones deferred). Either way the artifact is written, so a wrong
129
+ * call costs a `read` and never loses data.
130
+ */
131
+ export function renderChapter(events, range, config, overrides = [], topics = []) {
132
+ const inline = new Map(overrides.map((o) => [o.seq, o.inline]));
133
+ // render → redact → write: the single chokepoint, so chapters AND the
134
+ // deferred artifact files (whose bodies are the redacted text, via
135
+ // ArtifactRef.content) are covered without caller changes.
136
+ const redact = (t) => redactText(t, config.redactions ?? DEFAULT_REDACTIONS).text;
137
+ const inRange = events
138
+ .filter((e) => e.seq >= range.startSeq && e.seq <= range.endSeq)
139
+ .sort((a, b) => a.seq - b.seq);
140
+ const body = [`# ${range.title}`, '', `> ${range.summary}`, ''];
141
+ const artifacts = [];
142
+ const unrenderedSeqs = [];
143
+ let toolCalls = 0;
144
+ let inlinedCount = 0;
145
+ let deferredCount = 0;
146
+ let conversationMessages = 0;
147
+ for (const ev of inRange) {
148
+ const data = (ev.data ?? {});
149
+ switch (ev.type) {
150
+ case 'user/message': {
151
+ // The one boundary (src/injections.ts): host-injected context — a
152
+ // project's own instruction files, runtime snapshots, skill catalogs,
153
+ // and THIS plugin's TOC notices — is not conversation. Whole injected
154
+ // events render as a countable marker; injected spans inside human
155
+ // messages are removed the same way. Originals stay in the session log.
156
+ const screen = screenConversation(data, textOf(data.content), 'user');
157
+ if (screen.omittedEvent) {
158
+ body.push(omittedMarker(screen.sourceKind, screen.removedChars), '');
159
+ break;
160
+ }
161
+ if (screen.removedChars > 0)
162
+ body.push(omittedMarker('<system-reminder> span(s)', screen.removedChars), '');
163
+ const text = redact(screen.text);
164
+ if (text.length === 0) {
165
+ if (screen.removedChars === 0)
166
+ unrenderedSeqs.push(ev.seq); // a marker above IS a render
167
+ break;
168
+ }
169
+ conversationMessages += 1;
170
+ body.push(`**User:**`, '', fenced(text), '');
171
+ break;
172
+ }
173
+ case 'assistant/message': {
174
+ const message = (data.message ?? data);
175
+ const asstSpans = stripReminderSpans(textOf(message.content));
176
+ if (asstSpans.removedChars > 0)
177
+ body.push(omittedMarker('<system-reminder> span(s)', asstSpans.removedChars), '');
178
+ const text = redact(asstSpans.text);
179
+ const reasoning = redact(stripReminderSpans(blocksOf(message.content)
180
+ .filter((b) => b.type === 'reasoning')
181
+ .map((b) => b.text ?? '')
182
+ .join('\n')).text);
183
+ let rendered = false;
184
+ if (reasoning.length > 0) {
185
+ body.push('<sub>_reasoning:_</sub>', '', fenced(reasoning), '');
186
+ rendered = true;
187
+ }
188
+ if (text.length > 0) {
189
+ body.push(`**Assistant:**`, '', fenced(text), '');
190
+ rendered = true;
191
+ }
192
+ if (rendered)
193
+ conversationMessages += 1;
194
+ else if (asstSpans.removedChars === 0)
195
+ unrenderedSeqs.push(ev.seq);
196
+ break;
197
+ }
198
+ case 'tool/call': {
199
+ toolCalls += 1;
200
+ const name = String(data.name ?? 'unknown');
201
+ const args = redact(typeof data.arguments === 'string' ? data.arguments : JSON.stringify(data.arguments ?? {}));
202
+ // Invocations always stay inline: this is the "what was attempted" half of the record.
203
+ body.push(`- ↳ **${name}** \`${args.slice(0, 400)}\`${args.length > 400 ? ' …' : ''} (seq ${ev.seq})`);
204
+ break;
205
+ }
206
+ case 'tool/result': {
207
+ const text = redact(toolResultText(data));
208
+ const tokens = estimateTokens(text);
209
+ const wantsInline = inline.get(ev.seq) ?? tokens <= config.toolResultDeferFloorTokens;
210
+ const priorCall = [...inRange].reverse().find((c) => c.type === 'tool/call' && c.seq < ev.seq);
211
+ const toolName = String(priorCall?.data?.name ?? 'tool');
212
+ const ref = artifactRef(text, { toolName, sourceSeq: ev.seq, inlined: wantsInline });
213
+ artifacts.push(ref);
214
+ if (wantsInline) {
215
+ inlinedCount += 1;
216
+ body.push(` - result (seq ${ev.seq}, ${ref.bytes} B, also at \`${ref.path}\`):`, '', fenced(text), '');
217
+ }
218
+ else {
219
+ deferredCount += 1;
220
+ body.push(` - result: ${ref.bytes} B ≈ ${tokens} tok, \`${ref.sha256.slice(0, 7)}…\` → \`${ref.path}\``);
221
+ }
222
+ break;
223
+ }
224
+ case 'turn/start':
225
+ case 'turn/end':
226
+ case 'step/start':
227
+ case 'step/end':
228
+ break; // lifecycle noise: intentionally absent from the body, ranges still recorded in frontmatter
229
+ default:
230
+ unrenderedSeqs.push(ev.seq);
231
+ }
232
+ }
233
+ const bodyText = body.join('\n');
234
+ const estimatedTokens = estimateTokens(bodyText);
235
+ const frontmatter = [
236
+ '---',
237
+ `title: ${JSON.stringify(range.title)}`,
238
+ `seqRange: [${range.startSeq}, ${range.endSeq}]`,
239
+ `events: ${inRange.length}`,
240
+ `tokens: ${estimatedTokens} # chars/4 estimate`,
241
+ // The declared anchor MUST hash exactly what parseChapterFile returns as
242
+ // the body (file = frontmatter + bodyText + '\n' ⇒ parsed body carries the
243
+ // trailing newline). Hashing bare bodyText made EVERY real chapter fail
244
+ // the enrich guard's self-consistency check — caught by the enrich e2e
245
+ // 2026-09-23, invisible for eras behind the host logger.
246
+ `sha256: ${sha256(`${bodyText}\n`)}`,
247
+ `topics: [${[...new Set(topics)].map((t) => JSON.stringify(t)).join(', ')}]`,
248
+ `artifacts: ${artifacts.length}`,
249
+ `unrenderedSeqs: [${unrenderedSeqs.join(', ')}]`,
250
+ '---',
251
+ '',
252
+ ].join('\n');
253
+ return {
254
+ range,
255
+ topics: [...new Set(topics)],
256
+ markdown: frontmatter + bodyText + '\n',
257
+ artifacts,
258
+ stats: {
259
+ estimatedTokens,
260
+ estimatedBytes: Buffer.byteLength(bodyText, 'utf8'),
261
+ events: inRange.length,
262
+ messages: conversationMessages,
263
+ toolCalls,
264
+ toolResultsInlined: inlinedCount,
265
+ toolResultsDeferred: deferredCount,
266
+ overTarget: estimatedTokens > config.chapterTokenTarget,
267
+ unrenderedSeqs,
268
+ },
269
+ };
270
+ }
271
+ /**
272
+ * The cumulative index that replaces compacted history. Built from registry records, never from an LLM —
273
+ * which is the entire latency argument: no summarization call, no tokens, no paraphrase.
274
+ */
275
+ export function renderIndex(entries) {
276
+ return entries
277
+ .map((e, i) => `${i + 1}. [${e.title}](${e.path}) — ${e.summary}`)
278
+ .join('\n');
279
+ }
280
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
package/lib/repo.d.ts ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Record §2.3, exact: lowercase; strip `https://` / `ssh://`; convert
3
+ * `user@host:path` → `host/path`; strip trailing `/` and `.git`; KEEP the
4
+ * host (two hosts, same path = different projects).
5
+ */
6
+ export declare function canonicalizeRemote(url: string): string;
7
+ /** UUIDv5(NAMESPACE_OID, name) per RFC 4122 — deterministic, name-derived. */
8
+ export declare const NAMESPACE_OID = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
9
+ export declare function uuidv5(namespaceUuid: string, name: string): string;
10
+ /** The project key: stable across machines for the same remote (record §2.3). */
11
+ export declare const projectKeyFromRemote: (url: string) => string;
12
+ /**
13
+ * A knowledge upstream target string (record §2.1/§3.2 + §15 amendment
14
+ * "transport is pluggable"). Shapes:
15
+ * https://host/org/repo(.git) git over HTTPS (token required, §2.1)
16
+ * /some/dir.git | ./dir | ~dir local-path git bare pool (no credentials)
17
+ * treedx+http(s)://host[:port]/repo-name TreeDX managed repository
18
+ * The `treedx+` prefix is the ONLY kind discriminator — identity, credentials,
19
+ * and the mirror layout are provider-independent beyond it.
20
+ */
21
+ export interface KnowledgeRemoteTarget {
22
+ kind: 'git' | 'treedx';
23
+ /** The raw string as the user typed it (stored as `ProjectRecord.remote`). */
24
+ raw: string;
25
+ /** TreeDX: REST base URL + the managed repository name. */
26
+ treedx?: {
27
+ baseUrl: string;
28
+ repoName: string;
29
+ };
30
+ }
31
+ /** Parse an upstream target. Throws (never guesses) on a treedx+ URL without a
32
+ * repository name — a wrong identity means syncing into the wrong pool. */
33
+ export declare function parseKnowledgeRemote(target: string): KnowledgeRemoteTarget;
34
+ /**
35
+ * Project identity for a parsed target (§2.3 unchanged in spirit): for git,
36
+ * the canonical remote; for TreeDX, `treedx/` + the canonical form of
37
+ * `baseUrl/repoName`. The prefix keeps transports distinct on purpose —
38
+ * a server that happens to mirror the same host/port/path as a git remote
39
+ * is a DIFFERENT pool, and two linked records must never fight over one key.
40
+ */
41
+ export declare function projectKeyForTarget(t: KnowledgeRemoteTarget): string;
42
+ /**
43
+ * Parse the minimum of a git config we need: the first remote's url,
44
+ * preferring `origin`. Comments (# ;), sections, and whitespace handled;
45
+ * anything exotic returns null rather than guessing.
46
+ */
47
+ export declare function parseGitConfigRemoteUrl(text: string): string | null;
48
+ export interface ProjectRemote {
49
+ remote: string;
50
+ /** `remote` = from a git remote; `path-derived` = non-git workspace fallback. */
51
+ source: 'remote' | 'path-derived';
52
+ }
53
+ /**
54
+ * Discover the project's remote WITHOUT a git binary: read `.git` as a file
55
+ * or directory, parse its `config` (following a `gitdir:` pointer for
56
+ * worktrees/submodules, relative to `cwd`). Non-git workspace → the
57
+ * path-derived fallback is the CALLER's choice (this returns null), so the
58
+ * key's `source` flag stays an explicit decision.
59
+ */
60
+ export declare function readProjectRemote(cwd: string): ProjectRemote | null;
61
+ /** Resolve the project identity: override > discovered remote > path-derived. */
62
+ export declare function resolveProject(cwd: string, overrideKey?: string, overrideRemote?: string): {
63
+ projectKey: string;
64
+ projectKeySource: 'override' | 'remote' | 'path-derived';
65
+ remote: string | null;
66
+ };
67
+ /** Repo layout (record §2.2) — every path the sync/index/search layers touch. */
68
+ export declare function repoPaths(projectKey: string, harnessId: string, sessionId?: string): {
69
+ projectYml: string;
70
+ chaptersDir: string;
71
+ artifactsDir: string;
72
+ collectionsFile: (sid: string) => string;
73
+ indexDir: string;
74
+ indexManifest: string;
75
+ curationFile: string;
76
+ rulesDir: string;
77
+ vocabularyJson: string;
78
+ };
package/lib/repo.js ADDED
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Project identity and knowledge-repo layout (knowledge-repo.md §2.2–2.3).
3
+ *
4
+ * Pure module: no cordis, no network, no git binary. `canonicalizeRemote` +
5
+ * UUIDv5 give every machine the SAME key for the same project (local paths
6
+ * differ; the remote is the identity). `.git/config` is parsed as a plain
7
+ * file — the no-git-executable rule (record §3.2) applies to discovery too.
8
+ */
9
+ import { createHash } from 'node:crypto';
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ /**
13
+ * Record §2.3, exact: lowercase; strip `https://` / `ssh://`; convert
14
+ * `user@host:path` → `host/path`; strip trailing `/` and `.git`; KEEP the
15
+ * host (two hosts, same path = different projects).
16
+ */
17
+ export function canonicalizeRemote(url) {
18
+ let s = url.trim().toLowerCase();
19
+ s = s.replace(/^(?:https?|ssh|git|file):\/\//, '');
20
+ // file:// urls carry no host, only a path
21
+ s = s.replace(/^\/+/, '');
22
+ // embedded credentials (https://user:token@host/…) must NOT fork identity:
23
+ // the same repo pasted with and without creds is the same pool. Colon-bearing
24
+ // userinfo only ever appears in scheme-form URLs (scp usernames have none).
25
+ s = s.replace(/^[^@/]*:[^@/]*@/, '');
26
+ // [user@]host:path (scp-style) and [user@]host/path (scheme-form, after the
27
+ // scheme strip above)
28
+ s = s.replace(/^([a-z0-9._-]+)@([a-z0-9.-]+)[:\/](.+)$/, (_m, _user, host, p) => `${host}/${p}`);
29
+ s = s.replace(/^([a-z0-9.-]+):(.+)$/, (_m, host, p) => `${host}/${p}`);
30
+ s = s.replace(/\/+$/, '');
31
+ s = s.replace(/\.git$/, '');
32
+ return s;
33
+ }
34
+ /** UUIDv5(NAMESPACE_OID, name) per RFC 4122 — deterministic, name-derived. */
35
+ export const NAMESPACE_OID = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
36
+ export function uuidv5(namespaceUuid, name) {
37
+ const ns = namespaceUuid.replace(/-/g, '');
38
+ if (ns.length !== 32)
39
+ throw new Error(`uuidv5: namespace is not a UUID: ${namespaceUuid}`);
40
+ const b = createHash('sha1').update(Buffer.concat([Buffer.from(ns, 'hex'), Buffer.from(name, 'utf8')])).digest();
41
+ b[6] = (b[6] & 0x0f) | 0x50; // version 5
42
+ b[8] = (b[8] & 0x3f) | 0x80; // variant 10xx
43
+ // RFC 4122: the first 16 OCTETS of the SHA-1 (not all 20)
44
+ const h = b.subarray(0, 16).toString('hex');
45
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
46
+ }
47
+ /** The project key: stable across machines for the same remote (record §2.3). */
48
+ export const projectKeyFromRemote = (url) => uuidv5(NAMESPACE_OID, canonicalizeRemote(url));
49
+ const TREEDX_FORM = /^treedx\+(https?:\/\/\S+)$/i;
50
+ /** Parse an upstream target. Throws (never guesses) on a treedx+ URL without a
51
+ * repository name — a wrong identity means syncing into the wrong pool. */
52
+ export function parseKnowledgeRemote(target) {
53
+ const raw = target.trim();
54
+ const m = TREEDX_FORM.exec(raw);
55
+ if (m === null)
56
+ return { kind: 'git', raw };
57
+ let u;
58
+ try {
59
+ u = new URL(m[1]);
60
+ }
61
+ catch {
62
+ throw new Error(`treedx+ remote is not a valid URL: ${raw}`);
63
+ }
64
+ const segs = u.pathname.split('/').filter((s) => s !== '' && !(s === 'api' && u.pathname.includes('/api/v1')) && s !== 'v1');
65
+ if (segs.length === 0)
66
+ throw new Error(`treedx+ remote needs a repository name: treedx+<url>/repo — got ${raw}`);
67
+ const repoName = segs[segs.length - 1].toLowerCase(); // TreeDX names are canonical lowercase
68
+ const trimmed = segs.slice(0, -1);
69
+ const baseUrl = u.origin + (trimmed.length > 0 ? `/${trimmed.join('/')}` : '');
70
+ return { kind: 'treedx', raw, treedx: { baseUrl, repoName } };
71
+ }
72
+ /**
73
+ * Project identity for a parsed target (§2.3 unchanged in spirit): for git,
74
+ * the canonical remote; for TreeDX, `treedx/` + the canonical form of
75
+ * `baseUrl/repoName`. The prefix keeps transports distinct on purpose —
76
+ * a server that happens to mirror the same host/port/path as a git remote
77
+ * is a DIFFERENT pool, and two linked records must never fight over one key.
78
+ */
79
+ export function projectKeyForTarget(t) {
80
+ if (t.treedx !== undefined)
81
+ return uuidv5(NAMESPACE_OID, `treedx/${canonicalizeRemote(`${t.treedx.baseUrl}/${t.treedx.repoName}`)}`);
82
+ return projectKeyFromRemote(t.raw);
83
+ }
84
+ /**
85
+ * Parse the minimum of a git config we need: the first remote's url,
86
+ * preferring `origin`. Comments (# ;), sections, and whitespace handled;
87
+ * anything exotic returns null rather than guessing.
88
+ */
89
+ export function parseGitConfigRemoteUrl(text) {
90
+ let section = null;
91
+ let originUrl = null;
92
+ let firstRemoteUrl = null;
93
+ for (const raw of text.split(/\r?\n/)) {
94
+ const line = raw.trim();
95
+ if (line === '' || line.startsWith('#') || line.startsWith(';'))
96
+ continue;
97
+ const sectionMatch = /^\[([^\]]+)\]$/.exec(line);
98
+ if (sectionMatch !== null) {
99
+ section = sectionMatch[1];
100
+ continue;
101
+ }
102
+ const kv = /^([A-Za-z0-9.]+)\s*=\s*(.+)$/.exec(line);
103
+ if (kv === null || section === null)
104
+ continue;
105
+ if (kv[1].toLowerCase() !== 'url')
106
+ continue;
107
+ const value = kv[2].trim().replace(/^["']|["']$/g, '');
108
+ const sectionName = section.replace(/^remote\s+"?([^"]+)"?$/i, '$1').toLowerCase();
109
+ if (sectionName === 'origin' && originUrl === null)
110
+ originUrl = value;
111
+ else if (/^remote/.test(section) && firstRemoteUrl === null)
112
+ firstRemoteUrl = value;
113
+ }
114
+ return originUrl ?? firstRemoteUrl;
115
+ }
116
+ /**
117
+ * Discover the project's remote WITHOUT a git binary: read `.git` as a file
118
+ * or directory, parse its `config` (following a `gitdir:` pointer for
119
+ * worktrees/submodules, relative to `cwd`). Non-git workspace → the
120
+ * path-derived fallback is the CALLER's choice (this returns null), so the
121
+ * key's `source` flag stays an explicit decision.
122
+ */
123
+ export function readProjectRemote(cwd) {
124
+ const dotGit = path.join(cwd, '.git');
125
+ let configPath = null;
126
+ try {
127
+ const st = fs.statSync(dotGit);
128
+ if (st.isDirectory())
129
+ configPath = path.join(dotGit, 'config');
130
+ else if (st.isFile()) {
131
+ const pointer = fs.readFileSync(dotGit, 'utf8').trim();
132
+ const m = /^gitdir:\s*(.+)$/m.exec(pointer);
133
+ if (m !== null) {
134
+ const target = m[1].trim();
135
+ configPath = path.isAbsolute(target) ? path.join(target, 'config') : path.join(cwd, target, 'config');
136
+ }
137
+ }
138
+ }
139
+ catch {
140
+ return null; // no .git, unreadable, or not git
141
+ }
142
+ if (configPath === null)
143
+ return null;
144
+ try {
145
+ const url = parseGitConfigRemoteUrl(fs.readFileSync(configPath, 'utf8'));
146
+ if (url === null)
147
+ return null;
148
+ return { remote: url, source: 'remote' };
149
+ }
150
+ catch {
151
+ return null;
152
+ }
153
+ }
154
+ /** Resolve the project identity: override > discovered remote > path-derived. */
155
+ export function resolveProject(cwd, overrideKey, overrideRemote) {
156
+ if (overrideKey !== undefined && overrideKey !== '') {
157
+ return { projectKey: overrideKey, projectKeySource: 'override', remote: overrideRemote ?? null };
158
+ }
159
+ const discovered = overrideRemote !== undefined && overrideRemote !== ''
160
+ ? { remote: overrideRemote, source: 'remote' }
161
+ : readProjectRemote(cwd);
162
+ if (discovered !== null) {
163
+ return { projectKey: projectKeyFromRemote(discovered.remote), projectKeySource: discovered.source, remote: discovered.remote };
164
+ }
165
+ return { projectKey: uuidv5(NAMESPACE_OID, cwd), projectKeySource: 'path-derived', remote: null };
166
+ }
167
+ /** Repo layout (record §2.2) — every path the sync/index/search layers touch. */
168
+ export function repoPaths(projectKey, harnessId, sessionId) {
169
+ const base = '';
170
+ return {
171
+ projectYml: path.join(base, 'project.yml'),
172
+ chaptersDir: sessionId === undefined
173
+ ? path.join(base, 'chapters', projectKey)
174
+ : path.join(base, 'chapters', projectKey, sessionId),
175
+ artifactsDir: path.join(base, 'artifacts', projectKey),
176
+ collectionsFile: (sid) => path.join(base, 'collections', projectKey, `${sid}.jsonl`),
177
+ indexDir: path.join(base, 'index'),
178
+ indexManifest: path.join(base, 'index', 'manifest.json'),
179
+ curationFile: path.join(base, 'edits', harnessId, 'curation.jsonl'),
180
+ rulesDir: path.join(base, 'rules', projectKey),
181
+ vocabularyJson: path.join(base, 'topics', 'vocabulary.json'),
182
+ };
183
+ }
184
+ //# sourceMappingURL=repo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.js","sourceRoot":"","sources":["../src/repo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type RuleRecord } from './rules.ts';
2
+ import type { ProjectRecord } from './sync.ts';
3
+ export interface RulesIo {
4
+ cwd(): string;
5
+ storeRoot: string;
6
+ harnessId: string;
7
+ store: {
8
+ rules(): IterableIterator<[string, RuleRecord]>;
9
+ getRule(id: string): RuleRecord | undefined;
10
+ putRule(id: string, value: RuleRecord): Promise<void>;
11
+ };
12
+ projectFor(cwd: string): ProjectRecord | undefined;
13
+ mirrorDir(cwd: string): string;
14
+ /** arm an immediate sync so facts and files travel (never throws) */
15
+ syncNow(cwd: string): Promise<void>;
16
+ /** provenance of the ADD path: 'manual' for commands, the session id for the tool */
17
+ sourceSession?: string;
18
+ now: () => Date;
19
+ }
20
+ export interface RulesReply {
21
+ kind: 'success' | 'error';
22
+ text: string;
23
+ }
24
+ export declare function rulesCommand(io: RulesIo, args: string): Promise<RulesReply>;