@fingerskier/augment 0.4.0 → 0.5.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 (54) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +494 -450
  3. package/dist/cli.js +5 -4
  4. package/dist/cli.js.map +1 -1
  5. package/dist/daemon/client.d.ts +6 -0
  6. package/dist/daemon/client.js +19 -0
  7. package/dist/daemon/client.js.map +1 -1
  8. package/dist/daemon/http.js +79 -9
  9. package/dist/daemon/http.js.map +1 -1
  10. package/dist/daemon/lifetime-lock.js +12 -12
  11. package/dist/db.js +107 -107
  12. package/dist/decoction.d.ts +51 -0
  13. package/dist/decoction.js +184 -0
  14. package/dist/decoction.js.map +1 -0
  15. package/dist/hooks.d.ts +8 -5
  16. package/dist/hooks.js +49 -20
  17. package/dist/hooks.js.map +1 -1
  18. package/dist/install.d.ts +14 -2
  19. package/dist/install.js +189 -1
  20. package/dist/install.js.map +1 -1
  21. package/dist/mcp/apps.js +480 -480
  22. package/dist/mcp/server.js +83 -0
  23. package/dist/mcp/server.js.map +1 -1
  24. package/dist/memory/files.js +21 -2
  25. package/dist/memory/files.js.map +1 -1
  26. package/dist/memory/project.d.ts +3 -0
  27. package/dist/memory/project.js +20 -0
  28. package/dist/memory/project.js.map +1 -1
  29. package/dist/pi/extension.d.ts +6 -0
  30. package/dist/pi/extension.js +74 -20
  31. package/dist/pi/extension.js.map +1 -1
  32. package/dist/proposals.js +16 -15
  33. package/dist/proposals.js.map +1 -1
  34. package/dist/service.d.ts +134 -2
  35. package/dist/service.js +578 -51
  36. package/dist/service.js.map +1 -1
  37. package/docs/FEATURES.md +771 -615
  38. package/docs/verify-memory-flow.md +4 -3
  39. package/integrations/claude/skills/augment-context/SKILL.md +74 -74
  40. package/integrations/claude/skills/augment-decoction/SKILL.md +36 -0
  41. package/integrations/claude/skills/augment-dream/SKILL.md +34 -0
  42. package/integrations/claude/skills/augment-dream/agents/openai.yaml +4 -0
  43. package/integrations/claude/skills/augment-sleep/SKILL.md +26 -26
  44. package/integrations/codex/SKILL.md +107 -74
  45. package/integrations/examples/claude-mcp-config.json +17 -17
  46. package/integrations/examples/codex-mcp-config.json +17 -17
  47. package/integrations/examples/grok-mcp-config.toml +18 -0
  48. package/integrations/grok/AGENTS.md +61 -0
  49. package/integrations/pi/skills/augment-decoction/SKILL.md +37 -0
  50. package/integrations/pi/skills/augment-dream/SKILL.md +36 -0
  51. package/package.json +85 -84
  52. package/dist/tally.d.ts +0 -46
  53. package/dist/tally.js +0 -94
  54. package/dist/tally.js.map +0 -1
package/dist/db.js CHANGED
@@ -31,51 +31,51 @@ export class AugmentDatabase {
31
31
  await this.execute(`PRAGMA busy_timeout = 5000`);
32
32
  await this.execute(`PRAGMA foreign_keys = ON`);
33
33
  await this.batch([
34
- `CREATE TABLE IF NOT EXISTS projects (
35
- id INTEGER PRIMARY KEY AUTOINCREMENT,
36
- name TEXT NOT NULL UNIQUE,
37
- org TEXT NOT NULL,
38
- repo TEXT NOT NULL,
39
- source TEXT NOT NULL,
40
- created_at TEXT NOT NULL,
41
- updated_at TEXT NOT NULL
34
+ `CREATE TABLE IF NOT EXISTS projects (
35
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
36
+ name TEXT NOT NULL UNIQUE,
37
+ org TEXT NOT NULL,
38
+ repo TEXT NOT NULL,
39
+ source TEXT NOT NULL,
40
+ created_at TEXT NOT NULL,
41
+ updated_at TEXT NOT NULL
42
42
  )`,
43
- `CREATE TABLE IF NOT EXISTS memories (
44
- id INTEGER PRIMARY KEY AUTOINCREMENT,
45
- project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
46
- kind TEXT NOT NULL,
47
- name TEXT NOT NULL,
48
- relative_path TEXT NOT NULL UNIQUE,
49
- content_hash TEXT NOT NULL,
50
- frontmatter_hash TEXT NOT NULL,
51
- size INTEGER NOT NULL,
52
- density REAL NOT NULL,
53
- mtime REAL NOT NULL,
54
- tags_json TEXT NOT NULL,
55
- links_json TEXT NOT NULL,
56
- body TEXT NOT NULL,
57
- created_at TEXT NOT NULL,
58
- updated_at TEXT NOT NULL,
59
- archived INTEGER NOT NULL DEFAULT 0,
60
- consolidated_into TEXT,
61
- derived_from_json TEXT
43
+ `CREATE TABLE IF NOT EXISTS memories (
44
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
45
+ project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
46
+ kind TEXT NOT NULL,
47
+ name TEXT NOT NULL,
48
+ relative_path TEXT NOT NULL UNIQUE,
49
+ content_hash TEXT NOT NULL,
50
+ frontmatter_hash TEXT NOT NULL,
51
+ size INTEGER NOT NULL,
52
+ density REAL NOT NULL,
53
+ mtime REAL NOT NULL,
54
+ tags_json TEXT NOT NULL,
55
+ links_json TEXT NOT NULL,
56
+ body TEXT NOT NULL,
57
+ created_at TEXT NOT NULL,
58
+ updated_at TEXT NOT NULL,
59
+ archived INTEGER NOT NULL DEFAULT 0,
60
+ consolidated_into TEXT,
61
+ derived_from_json TEXT
62
62
  )`,
63
- `CREATE TABLE IF NOT EXISTS memory_embeddings (
64
- memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
65
- model_id TEXT NOT NULL,
66
- model_version TEXT NOT NULL,
67
- dimensions INTEGER NOT NULL,
68
- vector_json TEXT NOT NULL,
69
- embedded_hash TEXT NOT NULL,
70
- embedded_at TEXT NOT NULL
63
+ `CREATE TABLE IF NOT EXISTS memory_embeddings (
64
+ memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
65
+ model_id TEXT NOT NULL,
66
+ model_version TEXT NOT NULL,
67
+ dimensions INTEGER NOT NULL,
68
+ vector_json TEXT NOT NULL,
69
+ embedded_hash TEXT NOT NULL,
70
+ embedded_at TEXT NOT NULL
71
71
  )`,
72
- `CREATE TABLE IF NOT EXISTS links (
73
- id INTEGER PRIMARY KEY AUTOINCREMENT,
74
- from_memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
75
- to_memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
76
- type TEXT NOT NULL,
77
- created_at TEXT NOT NULL,
78
- UNIQUE(from_memory_id, to_memory_id, type)
72
+ `CREATE TABLE IF NOT EXISTS links (
73
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
74
+ from_memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
75
+ to_memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
76
+ type TEXT NOT NULL,
77
+ created_at TEXT NOT NULL,
78
+ UNIQUE(from_memory_id, to_memory_id, type)
79
79
  )`,
80
80
  `CREATE INDEX IF NOT EXISTS idx_memories_project_id ON memories(project_id)`,
81
81
  `CREATE INDEX IF NOT EXISTS idx_memories_kind ON memories(kind)`,
@@ -118,12 +118,12 @@ export class AugmentDatabase {
118
118
  async upsertProject(name, source = "git") {
119
119
  const parts = projectParts(name);
120
120
  const now = new Date().toISOString();
121
- await this.execute(`INSERT INTO projects (name, org, repo, source, created_at, updated_at)
122
- VALUES (?, ?, ?, ?, ?, ?)
123
- ON CONFLICT(name) DO UPDATE SET
124
- org = excluded.org,
125
- repo = excluded.repo,
126
- source = excluded.source,
121
+ await this.execute(`INSERT INTO projects (name, org, repo, source, created_at, updated_at)
122
+ VALUES (?, ?, ?, ?, ?, ?)
123
+ ON CONFLICT(name) DO UPDATE SET
124
+ org = excluded.org,
125
+ repo = excluded.repo,
126
+ source = excluded.source,
127
127
  updated_at = excluded.updated_at`, [name, parts.org, parts.repo, source, now, now]);
128
128
  const project = await this.getProjectByName(name);
129
129
  if (!project) {
@@ -148,27 +148,27 @@ export class AugmentDatabase {
148
148
  async upsertMemory(memory) {
149
149
  const project = await this.upsertProject(memory.frontmatter.project);
150
150
  const now = new Date().toISOString();
151
- await this.execute(`INSERT INTO memories (
152
- project_id, kind, name, relative_path, content_hash, frontmatter_hash,
153
- size, density, mtime, tags_json, links_json, body, created_at, updated_at,
154
- archived, consolidated_into, derived_from_json
155
- )
156
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
157
- ON CONFLICT(relative_path) DO UPDATE SET
158
- project_id = excluded.project_id,
159
- kind = excluded.kind,
160
- name = excluded.name,
161
- content_hash = excluded.content_hash,
162
- frontmatter_hash = excluded.frontmatter_hash,
163
- size = excluded.size,
164
- density = excluded.density,
165
- mtime = excluded.mtime,
166
- tags_json = excluded.tags_json,
167
- links_json = excluded.links_json,
168
- body = excluded.body,
169
- updated_at = excluded.updated_at,
170
- archived = excluded.archived,
171
- consolidated_into = excluded.consolidated_into,
151
+ await this.execute(`INSERT INTO memories (
152
+ project_id, kind, name, relative_path, content_hash, frontmatter_hash,
153
+ size, density, mtime, tags_json, links_json, body, created_at, updated_at,
154
+ archived, consolidated_into, derived_from_json
155
+ )
156
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
157
+ ON CONFLICT(relative_path) DO UPDATE SET
158
+ project_id = excluded.project_id,
159
+ kind = excluded.kind,
160
+ name = excluded.name,
161
+ content_hash = excluded.content_hash,
162
+ frontmatter_hash = excluded.frontmatter_hash,
163
+ size = excluded.size,
164
+ density = excluded.density,
165
+ mtime = excluded.mtime,
166
+ tags_json = excluded.tags_json,
167
+ links_json = excluded.links_json,
168
+ body = excluded.body,
169
+ updated_at = excluded.updated_at,
170
+ archived = excluded.archived,
171
+ consolidated_into = excluded.consolidated_into,
172
172
  derived_from_json = excluded.derived_from_json`, [
173
173
  project.id,
174
174
  memory.frontmatter.kind,
@@ -230,7 +230,7 @@ export class AugmentDatabase {
230
230
  continue;
231
231
  }
232
232
  statements.push({
233
- sql: `INSERT OR IGNORE INTO links (from_memory_id, to_memory_id, type, created_at)
233
+ sql: `INSERT OR IGNORE INTO links (from_memory_id, to_memory_id, type, created_at)
234
234
  VALUES (?, ?, ?, ?)`,
235
235
  args: [memory.id, target.id, link.type, now],
236
236
  });
@@ -257,36 +257,36 @@ export class AugmentDatabase {
257
257
  return result.rows.map((row) => memoryFromRow(row));
258
258
  }
259
259
  async listLinks(memoryId) {
260
- const result = await this.execute(`SELECT
261
- links.*,
262
- from_memory.relative_path AS from_relative_path,
263
- to_memory.relative_path AS to_relative_path
264
- FROM links
265
- JOIN memories AS from_memory ON from_memory.id = links.from_memory_id
266
- JOIN memories AS to_memory ON to_memory.id = links.to_memory_id
267
- WHERE links.from_memory_id = ? OR links.to_memory_id = ?
260
+ const result = await this.execute(`SELECT
261
+ links.*,
262
+ from_memory.relative_path AS from_relative_path,
263
+ to_memory.relative_path AS to_relative_path
264
+ FROM links
265
+ JOIN memories AS from_memory ON from_memory.id = links.from_memory_id
266
+ JOIN memories AS to_memory ON to_memory.id = links.to_memory_id
267
+ WHERE links.from_memory_id = ? OR links.to_memory_id = ?
268
268
  ORDER BY links.id`, [memoryId, memoryId]);
269
269
  return result.rows.map((row) => linkFromRow(row));
270
270
  }
271
271
  async getLinkById(id) {
272
- const result = await this.execute(`SELECT
273
- links.*,
274
- from_memory.relative_path AS from_relative_path,
275
- to_memory.relative_path AS to_relative_path
276
- FROM links
277
- JOIN memories AS from_memory ON from_memory.id = links.from_memory_id
278
- JOIN memories AS to_memory ON to_memory.id = links.to_memory_id
272
+ const result = await this.execute(`SELECT
273
+ links.*,
274
+ from_memory.relative_path AS from_relative_path,
275
+ to_memory.relative_path AS to_relative_path
276
+ FROM links
277
+ JOIN memories AS from_memory ON from_memory.id = links.from_memory_id
278
+ JOIN memories AS to_memory ON to_memory.id = links.to_memory_id
279
279
  WHERE links.id = ?`, [id]);
280
280
  return result.rows[0] ? linkFromRow(result.rows[0]) : undefined;
281
281
  }
282
282
  async getAllLinks() {
283
- const result = await this.execute(`SELECT
284
- links.*,
285
- from_memory.relative_path AS from_relative_path,
286
- to_memory.relative_path AS to_relative_path
287
- FROM links
288
- JOIN memories AS from_memory ON from_memory.id = links.from_memory_id
289
- JOIN memories AS to_memory ON to_memory.id = links.to_memory_id
283
+ const result = await this.execute(`SELECT
284
+ links.*,
285
+ from_memory.relative_path AS from_relative_path,
286
+ to_memory.relative_path AS to_relative_path
287
+ FROM links
288
+ JOIN memories AS from_memory ON from_memory.id = links.from_memory_id
289
+ JOIN memories AS to_memory ON to_memory.id = links.to_memory_id
290
290
  ORDER BY links.id`);
291
291
  return result.rows.map((row) => linkFromRow(row));
292
292
  }
@@ -312,16 +312,16 @@ export class AugmentDatabase {
312
312
  if (!record) {
313
313
  throw new Error(`Cannot store embedding for missing memory`);
314
314
  }
315
- await this.execute(`INSERT INTO memory_embeddings (
316
- memory_id, model_id, model_version, dimensions, vector_json, embedded_hash, embedded_at
317
- )
318
- VALUES (?, ?, ?, ?, ?, ?, ?)
319
- ON CONFLICT(memory_id) DO UPDATE SET
320
- model_id = excluded.model_id,
321
- model_version = excluded.model_version,
322
- dimensions = excluded.dimensions,
323
- vector_json = excluded.vector_json,
324
- embedded_hash = excluded.embedded_hash,
315
+ await this.execute(`INSERT INTO memory_embeddings (
316
+ memory_id, model_id, model_version, dimensions, vector_json, embedded_hash, embedded_at
317
+ )
318
+ VALUES (?, ?, ?, ?, ?, ?, ?)
319
+ ON CONFLICT(memory_id) DO UPDATE SET
320
+ model_id = excluded.model_id,
321
+ model_version = excluded.model_version,
322
+ dimensions = excluded.dimensions,
323
+ vector_json = excluded.vector_json,
324
+ embedded_hash = excluded.embedded_hash,
325
325
  embedded_at = excluded.embedded_at`, [
326
326
  record.id,
327
327
  provider.modelId,
@@ -405,11 +405,11 @@ export class AugmentDatabase {
405
405
  }
406
406
  }
407
407
  function memorySelectSql(suffix) {
408
- return `SELECT
409
- memories.*,
410
- projects.name AS project_name
411
- FROM memories
412
- JOIN projects ON projects.id = memories.project_id
408
+ return `SELECT
409
+ memories.*,
410
+ projects.name AS project_name
411
+ FROM memories
412
+ JOIN projects ON projects.id = memories.project_id
413
413
  ${suffix}`;
414
414
  }
415
415
  function projectFromRow(row) {
@@ -0,0 +1,51 @@
1
+ import type { MemoryKind } from "./constants.js";
2
+ import type { MemoryRecord } from "./types.js";
3
+ export declare const DECOCTION_DEFAULT_MIN_COSINE = 0.75;
4
+ export declare const DECOCTION_MIN_PROJECTS = 4;
5
+ export declare const DECOCTION_DEFAULT_SNIPPET_CHARS = 280;
6
+ /** The corpus fields needed by read-only decoction discovery. */
7
+ export type DecoctionDiscoveryMemory = Pick<MemoryRecord, "id" | "project_name" | "kind" | "relative_path" | "content_hash" | "frontmatter_hash" | "tags" | "body" | "archived">;
8
+ /** Matches the memories + embeddings portion of AugmentService's corpus snapshot. */
9
+ export interface DecoctionDiscoveryInput {
10
+ memories: readonly DecoctionDiscoveryMemory[];
11
+ embeddings: ReadonlyMap<number, number[]>;
12
+ /** Raw cosine floor used for every pair in a cluster. */
13
+ minCosine?: number;
14
+ /** Maximum normalized body characters returned per source. */
15
+ snippetChars?: number;
16
+ }
17
+ export interface DecoctionDiscoverySource {
18
+ id: number;
19
+ /** Durable identity; numeric ids can churn during an index rebuild. */
20
+ relative_path: string;
21
+ project_name: string;
22
+ kind: MemoryKind;
23
+ content_hash: string;
24
+ frontmatter_hash: string;
25
+ snippet: string;
26
+ }
27
+ export interface DecoctionDiscoveryCluster {
28
+ sources: DecoctionDiscoverySource[];
29
+ distinct_projects: number;
30
+ project_names: string[];
31
+ /** Lowest raw cosine among every pair of sources in this complete-link cluster. */
32
+ min_pairwise_cosine: number;
33
+ }
34
+ export interface DecoctionDiscoveryOutput {
35
+ /** Policy-eligible memories, including any that currently lack an embedding. */
36
+ eligible: number;
37
+ /** Eligible memories that have an embedding and therefore entered clustering. */
38
+ embedded: number;
39
+ min_cosine: number;
40
+ min_projects: typeof DECOCTION_MIN_PROJECTS;
41
+ clusters: DecoctionDiscoveryCluster[];
42
+ }
43
+ /**
44
+ * Find cross-project evidence groups without mutating the corpus.
45
+ *
46
+ * This is agglomerative complete-link clustering: two groups can merge only
47
+ * when every cross-group source pair meets the raw cosine floor. In contrast
48
+ * to connected components, a chain of individually similar neighbors cannot
49
+ * pull dissimilar endpoints into the same generic-memory candidate.
50
+ */
51
+ export declare function discoverDecoctionCandidates(input: DecoctionDiscoveryInput): DecoctionDiscoveryOutput;
@@ -0,0 +1,184 @@
1
+ import { cosineSimilarity } from "./util/text.js";
2
+ export const DECOCTION_DEFAULT_MIN_COSINE = 0.75;
3
+ export const DECOCTION_MIN_PROJECTS = 4;
4
+ export const DECOCTION_DEFAULT_SNIPPET_CHARS = 280;
5
+ const EVIDENCE_KINDS = new Set(["WORK", "ISSUE", "SPEC", "ARCH"]);
6
+ const EXCLUDED_TAGS = new Set(["private", "no-generalize"]);
7
+ /**
8
+ * Find cross-project evidence groups without mutating the corpus.
9
+ *
10
+ * This is agglomerative complete-link clustering: two groups can merge only
11
+ * when every cross-group source pair meets the raw cosine floor. In contrast
12
+ * to connected components, a chain of individually similar neighbors cannot
13
+ * pull dissimilar endpoints into the same generic-memory candidate.
14
+ */
15
+ export function discoverDecoctionCandidates(input) {
16
+ const minCosine = input.minCosine ?? DECOCTION_DEFAULT_MIN_COSINE;
17
+ const snippetChars = input.snippetChars ?? DECOCTION_DEFAULT_SNIPPET_CHARS;
18
+ validateOptions(minCosine, snippetChars);
19
+ const eligible = input.memories.filter(isEligibleEvidence).sort(compareMemories);
20
+ const candidates = eligible.filter((memory) => input.embeddings.has(memory.id));
21
+ const similarities = similarityMatrix(candidates, input.embeddings);
22
+ const working = completeLinkClusters(candidates, similarities, minCosine);
23
+ const clusters = working
24
+ .map((cluster) => toDiscoveryCluster(cluster, candidates, similarities, snippetChars))
25
+ .filter((cluster) => cluster.distinct_projects >= DECOCTION_MIN_PROJECTS)
26
+ .sort(compareDiscoveryClusters);
27
+ return {
28
+ eligible: eligible.length,
29
+ embedded: candidates.length,
30
+ min_cosine: minCosine,
31
+ min_projects: DECOCTION_MIN_PROJECTS,
32
+ clusters,
33
+ };
34
+ }
35
+ function isEligibleEvidence(memory) {
36
+ if (!EVIDENCE_KINDS.has(memory.kind) || memory.archived || isGeneric(memory)) {
37
+ return false;
38
+ }
39
+ return !memory.tags.some((tag) => EXCLUDED_TAGS.has(tag.trim().toLowerCase()));
40
+ }
41
+ function isGeneric(memory) {
42
+ if (memory.project_name === ".generic") {
43
+ return true;
44
+ }
45
+ const normalizedPath = memory.relative_path.replaceAll("\\", "/").replace(/^\.\//, "");
46
+ return normalizedPath.startsWith(".generic/");
47
+ }
48
+ function similarityMatrix(candidates, embeddings) {
49
+ const matrix = candidates.map(() => new Array(candidates.length).fill(1));
50
+ for (let left = 0; left < candidates.length; left += 1) {
51
+ const leftVector = embeddings.get(candidates[left].id);
52
+ for (let right = left + 1; right < candidates.length; right += 1) {
53
+ const rightVector = embeddings.get(candidates[right].id);
54
+ const similarity = leftVector && rightVector ? boundedCosine(leftVector, rightVector) : 0;
55
+ matrix[left][right] = similarity;
56
+ matrix[right][left] = similarity;
57
+ }
58
+ }
59
+ return matrix;
60
+ }
61
+ function completeLinkClusters(candidates, similarities, minCosine) {
62
+ let clusters = candidates.map((memory, index) => ({
63
+ members: [index],
64
+ key: memoryKey(memory),
65
+ }));
66
+ for (;;) {
67
+ let best;
68
+ for (let left = 0; left < clusters.length; left += 1) {
69
+ for (let right = left + 1; right < clusters.length; right += 1) {
70
+ const similarity = completeLinkSimilarity(clusters[left], clusters[right], similarities, minCosine);
71
+ if (similarity < minCosine) {
72
+ continue;
73
+ }
74
+ const key = mergeKey(clusters[left], clusters[right]);
75
+ if (!best ||
76
+ similarity > best.similarity ||
77
+ (similarity === best.similarity && compareText(key, best.key) < 0)) {
78
+ best = { left, right, similarity, key };
79
+ }
80
+ }
81
+ }
82
+ if (!best) {
83
+ return clusters;
84
+ }
85
+ const members = [...clusters[best.left].members, ...clusters[best.right].members].sort((a, b) => a - b);
86
+ const merged = { members, key: best.key };
87
+ clusters = clusters
88
+ .filter((_, index) => index !== best.left && index !== best.right)
89
+ .concat(merged)
90
+ .sort((a, b) => compareText(a.key, b.key));
91
+ }
92
+ }
93
+ function completeLinkSimilarity(left, right, similarities, floor) {
94
+ let minimum = 1;
95
+ for (const leftIndex of left.members) {
96
+ for (const rightIndex of right.members) {
97
+ minimum = Math.min(minimum, similarities[leftIndex][rightIndex]);
98
+ // This pair can never merge. Avoid scanning the remaining cross pairs.
99
+ if (minimum < floor) {
100
+ return minimum;
101
+ }
102
+ }
103
+ }
104
+ return minimum;
105
+ }
106
+ function toDiscoveryCluster(cluster, candidates, similarities, snippetChars) {
107
+ const members = cluster.members.map((index) => candidates[index]);
108
+ const projectNames = [...new Set(members.map((memory) => memory.project_name))].sort(compareText);
109
+ const sources = members
110
+ .map((memory) => ({
111
+ id: memory.id,
112
+ relative_path: memory.relative_path,
113
+ project_name: memory.project_name,
114
+ kind: memory.kind,
115
+ content_hash: memory.content_hash,
116
+ frontmatter_hash: memory.frontmatter_hash,
117
+ snippet: snippet(memory.body, snippetChars),
118
+ }))
119
+ .sort((a, b) => compareText(a.relative_path, b.relative_path) || a.id - b.id);
120
+ return {
121
+ sources,
122
+ distinct_projects: projectNames.length,
123
+ project_names: projectNames,
124
+ min_pairwise_cosine: minimumPairwise(cluster.members, similarities),
125
+ };
126
+ }
127
+ function minimumPairwise(members, similarities) {
128
+ if (members.length < 2) {
129
+ return 1;
130
+ }
131
+ let minimum = 1;
132
+ for (let left = 0; left < members.length; left += 1) {
133
+ for (let right = left + 1; right < members.length; right += 1) {
134
+ minimum = Math.min(minimum, similarities[members[left]][members[right]]);
135
+ }
136
+ }
137
+ return minimum;
138
+ }
139
+ function compareDiscoveryClusters(left, right) {
140
+ return (right.distinct_projects - left.distinct_projects ||
141
+ right.sources.length - left.sources.length ||
142
+ right.min_pairwise_cosine - left.min_pairwise_cosine ||
143
+ compareText(clusterPathKey(left), clusterPathKey(right)));
144
+ }
145
+ function snippet(body, maxChars) {
146
+ const normalized = body.replace(/\s+/g, " ").trim();
147
+ if (normalized.length <= maxChars) {
148
+ return normalized;
149
+ }
150
+ if (maxChars === 0) {
151
+ return "";
152
+ }
153
+ if (maxChars === 1) {
154
+ return "…";
155
+ }
156
+ return `${normalized.slice(0, maxChars - 1).trimEnd()}…`;
157
+ }
158
+ function boundedCosine(left, right) {
159
+ return Math.max(-1, Math.min(1, cosineSimilarity(left, right)));
160
+ }
161
+ function compareMemories(left, right) {
162
+ return compareText(left.relative_path, right.relative_path) || left.id - right.id;
163
+ }
164
+ function memoryKey(memory) {
165
+ return `${memory.relative_path}\u0000${memory.id}`;
166
+ }
167
+ function mergeKey(left, right) {
168
+ return compareText(left.key, right.key) <= 0 ? `${left.key}\u0001${right.key}` : `${right.key}\u0001${left.key}`;
169
+ }
170
+ function clusterPathKey(cluster) {
171
+ return cluster.sources.map((source) => `${source.relative_path}\u0000${source.id}`).join("\u0001");
172
+ }
173
+ function compareText(left, right) {
174
+ return left < right ? -1 : left > right ? 1 : 0;
175
+ }
176
+ function validateOptions(minCosine, snippetChars) {
177
+ if (!Number.isFinite(minCosine) || minCosine < 0 || minCosine > 1) {
178
+ throw new RangeError("minCosine must be a finite number between 0 and 1");
179
+ }
180
+ if (!Number.isInteger(snippetChars) || snippetChars < 0) {
181
+ throw new RangeError("snippetChars must be a non-negative integer");
182
+ }
183
+ }
184
+ //# sourceMappingURL=decoction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decoction.js","sourceRoot":"","sources":["../src/decoction.ts"],"names":[],"mappings":"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"}
package/dist/hooks.d.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import { type RecallOptions } from "./recall.js";
2
2
  /**
3
- * Host-agnostic hook handlers. Claude Code and Codex CLI share the same
4
- * lifecycle-hook stdin/stdout JSON contract (Codex copied the event names and
5
- * envelopes), so one set of handlers serves both. The only host difference this
6
- * file cares about is the field naming the prompt: Claude sends `user_input`,
7
- * Codex sends `prompt` `readPrompt` accepts either.
3
+ * Host-agnostic hook handlers. Claude Code, Codex CLI, and Grok Build share the
4
+ * same lifecycle-hook event names; Codex and Grok largely copied Claude's
5
+ * envelopes. Field-name differences are normalized below:
6
+ * - prompt: Claude `user_input`, Codex `prompt` (Grok may send either)
7
+ * - tool fields: Claude/Codex snake_case (`tool_name`, `tool_input`, `session_id`);
8
+ * Grok camelCase (`toolName`, `toolInput`, `sessionId`, `workspaceRoot`)
9
+ * - tool names: Grok's real names (`run_terminal_command`, `search_replace`, …)
10
+ * plus Claude aliases already accepted by Grok matchers
8
11
  *
9
12
  * Every handler is **fail-open**: a hook runs on every prompt and every turn
10
13
  * boundary, so any failure must degrade to "do nothing" (empty stdout, exit 0)