@goondocks/myco 0.5.1 → 0.6.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 (138) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +4 -0
  4. package/dist/{chunk-JJL6AMDA.js → chunk-24DOZEUJ.js} +255 -6
  5. package/dist/chunk-24DOZEUJ.js.map +1 -0
  6. package/dist/{chunk-ZWUFTOG3.js → chunk-2GSX3BK2.js} +4 -4
  7. package/dist/{chunk-FIRMTYFH.js → chunk-2YBUL3IL.js} +4 -37
  8. package/dist/chunk-2YBUL3IL.js.map +1 -0
  9. package/dist/{chunk-HL2S5QZG.js → chunk-2ZBB3MQT.js} +319 -40
  10. package/dist/chunk-2ZBB3MQT.js.map +1 -0
  11. package/dist/{chunk-HJG7Z6SJ.js → chunk-3EM23DMD.js} +2 -2
  12. package/dist/{chunk-XQXXF6MU.js → chunk-4RMSHZE4.js} +12 -1
  13. package/dist/{chunk-XQXXF6MU.js.map → chunk-4RMSHZE4.js.map} +1 -1
  14. package/dist/{chunk-T7OC6GH5.js → chunk-5FNZ7AMX.js} +2 -2
  15. package/dist/{chunk-X6TKHO22.js → chunk-5QWZT4AB.js} +2 -2
  16. package/dist/{chunk-B6WVNDA5.js → chunk-6BSDCZ5Q.js} +8 -2
  17. package/dist/{chunk-B6WVNDA5.js.map → chunk-6BSDCZ5Q.js.map} +1 -1
  18. package/dist/{chunk-R6LQT3U7.js → chunk-B5UZSHQV.js} +8 -12
  19. package/dist/{chunk-R6LQT3U7.js.map → chunk-B5UZSHQV.js.map} +1 -1
  20. package/dist/{chunk-7KQB22DP.js → chunk-E7OBRBCQ.js} +2 -2
  21. package/dist/{chunk-RCV2I4AI.js → chunk-GDYYJTTT.js} +5 -3
  22. package/dist/{chunk-RCV2I4AI.js.map → chunk-GDYYJTTT.js.map} +1 -1
  23. package/dist/{chunk-MIU3DKLN.js → chunk-GNR3QAER.js} +2 -2
  24. package/dist/{chunk-BMJX2IDQ.js → chunk-H7PRCVGQ.js} +2 -2
  25. package/dist/{chunk-6LTNFMXO.js → chunk-KC7ENQTN.js} +2 -2
  26. package/dist/chunk-KUMVJIJW.js +117 -0
  27. package/dist/chunk-KUMVJIJW.js.map +1 -0
  28. package/dist/{chunk-ND4VK6C7.js → chunk-L25U7PIG.js} +2 -2
  29. package/dist/{chunk-6UJWI4IW.js → chunk-MQSYSQ6T.js} +7 -5
  30. package/dist/{chunk-6UJWI4IW.js.map → chunk-MQSYSQ6T.js.map} +1 -1
  31. package/dist/{chunk-TBRZAJ7W.js → chunk-P3WO3N3I.js} +11 -3
  32. package/dist/chunk-P3WO3N3I.js.map +1 -0
  33. package/dist/{chunk-JI6M2L2W.js → chunk-QGJ2ZIUZ.js} +7 -4
  34. package/dist/chunk-QGJ2ZIUZ.js.map +1 -0
  35. package/dist/{chunk-5EZ7QF6J.js → chunk-QLUE3BUL.js} +66 -1
  36. package/dist/chunk-QLUE3BUL.js.map +1 -0
  37. package/dist/{chunk-AK6GNLPV.js → chunk-TWSTAVLO.js} +17 -1
  38. package/dist/{chunk-AK6GNLPV.js.map → chunk-TWSTAVLO.js.map} +1 -1
  39. package/dist/{chunk-FIA5NTRH.js → chunk-UVGAVYWZ.js} +11 -13
  40. package/dist/chunk-UVGAVYWZ.js.map +1 -0
  41. package/dist/{chunk-UKWO26VI.js → chunk-YTANWAGE.js} +2 -2
  42. package/dist/chunk-ZMYNRTTD.js +64 -0
  43. package/dist/chunk-ZMYNRTTD.js.map +1 -0
  44. package/dist/{cli-BLYNNKGJ.js → cli-K7SUTP7A.js} +22 -22
  45. package/dist/{client-5GB4WVXE.js → client-YJMNTITQ.js} +5 -5
  46. package/dist/{config-5FGLQGCW.js → config-G5GGT5A6.js} +3 -3
  47. package/dist/curate-6T5NKVXK.js +80 -0
  48. package/dist/curate-6T5NKVXK.js.map +1 -0
  49. package/dist/{detect-providers-BIHYFK5M.js → detect-providers-S3M5TAMW.js} +3 -3
  50. package/dist/{digest-7NKYXM6G.js → digest-O35VHYFP.js} +31 -40
  51. package/dist/digest-O35VHYFP.js.map +1 -0
  52. package/dist/{init-HPQ77WWF.js → init-TFLSATB3.js} +9 -11
  53. package/dist/init-TFLSATB3.js.map +1 -0
  54. package/dist/{logs-BSTBZHDR.js → logs-IENORIYR.js} +3 -3
  55. package/dist/{main-NFQ4II75.js → main-JEUQS3BY.js} +1218 -294
  56. package/dist/main-JEUQS3BY.js.map +1 -0
  57. package/dist/rebuild-7SH5GSNX.js +66 -0
  58. package/dist/rebuild-7SH5GSNX.js.map +1 -0
  59. package/dist/{reprocess-ZL4HKTSC.js → reprocess-Q4YH2ZBK.js} +20 -22
  60. package/dist/{reprocess-ZL4HKTSC.js.map → reprocess-Q4YH2ZBK.js.map} +1 -1
  61. package/dist/{restart-FYW662DR.js → restart-NLJLB52D.js} +7 -6
  62. package/dist/{restart-FYW662DR.js.map → restart-NLJLB52D.js.map} +1 -1
  63. package/dist/{search-E5JQMTXV.js → search-2BVRF54H.js} +10 -10
  64. package/dist/{server-TV3D35HZ.js → server-4AMZNP4F.js} +51 -97
  65. package/dist/{server-TV3D35HZ.js.map → server-4AMZNP4F.js.map} +1 -1
  66. package/dist/{session-QF6MILAC.js → session-F326AWCH.js} +2 -2
  67. package/dist/{session-start-5MFEOVQ5.js → session-start-AZAF3DTE.js} +10 -10
  68. package/dist/setup-digest-YLZZGSSR.js +15 -0
  69. package/dist/setup-llm-JOXBSLXC.js +15 -0
  70. package/dist/src/cli.js +4 -4
  71. package/dist/src/daemon/main.js +4 -4
  72. package/dist/src/hooks/post-tool-use.js +5 -5
  73. package/dist/src/hooks/session-end.js +5 -5
  74. package/dist/src/hooks/session-start.js +4 -4
  75. package/dist/src/hooks/stop.js +7 -7
  76. package/dist/src/hooks/user-prompt-submit.js +5 -5
  77. package/dist/src/mcp/server.js +4 -4
  78. package/dist/src/prompts/consolidation.md +46 -0
  79. package/dist/src/templates/portal.md +5 -0
  80. package/dist/stats-MKDIZFIQ.js +58 -0
  81. package/dist/stats-MKDIZFIQ.js.map +1 -0
  82. package/dist/templates-XPRBOWCE.js +38 -0
  83. package/dist/templates-XPRBOWCE.js.map +1 -0
  84. package/dist/ui/assets/index-D37IoDXS.css +1 -0
  85. package/dist/ui/assets/index-DA61Ial2.js +289 -0
  86. package/dist/ui/favicon.svg +11 -0
  87. package/dist/ui/fonts/GeistMono-LICENSE.txt +92 -0
  88. package/dist/ui/fonts/GeistMono-Variable.woff2 +0 -0
  89. package/dist/ui/index.html +14 -0
  90. package/dist/{verify-RACBFT2P.js → verify-7DW7LAND.js} +6 -6
  91. package/dist/{version-HJTVNPOO.js → version-RQLD7VBP.js} +4 -4
  92. package/package.json +3 -2
  93. package/dist/chunk-2AMAOSRF.js +0 -105
  94. package/dist/chunk-2AMAOSRF.js.map +0 -1
  95. package/dist/chunk-5EZ7QF6J.js.map +0 -1
  96. package/dist/chunk-FIA5NTRH.js.map +0 -1
  97. package/dist/chunk-FIRMTYFH.js.map +0 -1
  98. package/dist/chunk-HL2S5QZG.js.map +0 -1
  99. package/dist/chunk-IURC35BF.js +0 -49
  100. package/dist/chunk-IURC35BF.js.map +0 -1
  101. package/dist/chunk-JI6M2L2W.js.map +0 -1
  102. package/dist/chunk-JJL6AMDA.js.map +0 -1
  103. package/dist/chunk-KYL67SKZ.js +0 -150
  104. package/dist/chunk-KYL67SKZ.js.map +0 -1
  105. package/dist/chunk-TBRZAJ7W.js.map +0 -1
  106. package/dist/curate-S4HOYWXA.js +0 -231
  107. package/dist/curate-S4HOYWXA.js.map +0 -1
  108. package/dist/digest-7NKYXM6G.js.map +0 -1
  109. package/dist/init-HPQ77WWF.js.map +0 -1
  110. package/dist/main-NFQ4II75.js.map +0 -1
  111. package/dist/rebuild-KQ6G2GZM.js +0 -86
  112. package/dist/rebuild-KQ6G2GZM.js.map +0 -1
  113. package/dist/setup-digest-DZAFIBEF.js +0 -15
  114. package/dist/setup-llm-4BZM33YT.js +0 -15
  115. package/dist/stats-ZIIJ2GB3.js +0 -77
  116. package/dist/stats-ZIIJ2GB3.js.map +0 -1
  117. /package/dist/{chunk-ZWUFTOG3.js.map → chunk-2GSX3BK2.js.map} +0 -0
  118. /package/dist/{chunk-HJG7Z6SJ.js.map → chunk-3EM23DMD.js.map} +0 -0
  119. /package/dist/{chunk-T7OC6GH5.js.map → chunk-5FNZ7AMX.js.map} +0 -0
  120. /package/dist/{chunk-X6TKHO22.js.map → chunk-5QWZT4AB.js.map} +0 -0
  121. /package/dist/{chunk-7KQB22DP.js.map → chunk-E7OBRBCQ.js.map} +0 -0
  122. /package/dist/{chunk-MIU3DKLN.js.map → chunk-GNR3QAER.js.map} +0 -0
  123. /package/dist/{chunk-BMJX2IDQ.js.map → chunk-H7PRCVGQ.js.map} +0 -0
  124. /package/dist/{chunk-6LTNFMXO.js.map → chunk-KC7ENQTN.js.map} +0 -0
  125. /package/dist/{chunk-ND4VK6C7.js.map → chunk-L25U7PIG.js.map} +0 -0
  126. /package/dist/{chunk-UKWO26VI.js.map → chunk-YTANWAGE.js.map} +0 -0
  127. /package/dist/{cli-BLYNNKGJ.js.map → cli-K7SUTP7A.js.map} +0 -0
  128. /package/dist/{client-5GB4WVXE.js.map → client-YJMNTITQ.js.map} +0 -0
  129. /package/dist/{config-5FGLQGCW.js.map → config-G5GGT5A6.js.map} +0 -0
  130. /package/dist/{detect-providers-BIHYFK5M.js.map → detect-providers-S3M5TAMW.js.map} +0 -0
  131. /package/dist/{logs-BSTBZHDR.js.map → logs-IENORIYR.js.map} +0 -0
  132. /package/dist/{search-E5JQMTXV.js.map → search-2BVRF54H.js.map} +0 -0
  133. /package/dist/{session-QF6MILAC.js.map → session-F326AWCH.js.map} +0 -0
  134. /package/dist/{session-start-5MFEOVQ5.js.map → session-start-AZAF3DTE.js.map} +0 -0
  135. /package/dist/{setup-digest-DZAFIBEF.js.map → setup-digest-YLZZGSSR.js.map} +0 -0
  136. /package/dist/{setup-llm-4BZM33YT.js.map → setup-llm-JOXBSLXC.js.map} +0 -0
  137. /package/dist/{verify-RACBFT2P.js.map → verify-7DW7LAND.js.map} +0 -0
  138. /package/dist/{version-HJTVNPOO.js.map → version-RQLD7VBP.js.map} +0 -0
@@ -1,150 +0,0 @@
1
- import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
- import {
3
- ARTIFACT_TYPES
4
- } from "./chunk-2AMAOSRF.js";
5
- import {
6
- CANDIDATE_CONTENT_PREVIEW
7
- } from "./chunk-B6WVNDA5.js";
8
-
9
- // src/intelligence/response.ts
10
- var REASONING_PATTERNS = [
11
- // <think>...</think>answer (DeepSeek, Qwen, GLM, many others)
12
- /<think>[\s\S]*?<\/think>\s*/gi,
13
- // Implicit opening: reasoning...</think>answer (GLM-4.7 observed)
14
- /^[\s\S]*?<\/think>\s*/i,
15
- // <reasoning>...</reasoning>answer
16
- /<reasoning>[\s\S]*?<\/reasoning>\s*/gi,
17
- // <|thinking|>...<|/thinking|>answer
18
- /<\|thinking\|>[\s\S]*?<\|\/thinking\|>\s*/gi,
19
- // Plain-text "Thinking Process:" block followed by actual content
20
- // (Qwen 3.5 via LM Studio without native thinking mode)
21
- // Matches from "Thinking Process:" up to the last numbered step, then the synthesis follows
22
- /^Thinking Process:[\s\S]*?(?=\n(?:## |# |\*\*[A-Z]))/i
23
- ];
24
- function stripReasoningTokens(text) {
25
- if (!text) return text;
26
- for (const pattern of REASONING_PATTERNS) {
27
- const stripped = text.replace(pattern, "").trim();
28
- if (stripped && stripped !== text.trim()) {
29
- return stripped;
30
- }
31
- }
32
- return text;
33
- }
34
- function extractJson(text) {
35
- const cleaned = stripReasoningTokens(text);
36
- const fenceMatch = cleaned.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
37
- if (fenceMatch) {
38
- return JSON.parse(fenceMatch[1].trim());
39
- }
40
- const objectMatch = cleaned.match(/\{[\s\S]*\}/);
41
- if (objectMatch) {
42
- return JSON.parse(objectMatch[0]);
43
- }
44
- return JSON.parse(cleaned);
45
- }
46
- function extractNumber(text) {
47
- const cleaned = stripReasoningTokens(text).trim();
48
- const match = cleaned.match(/(\d+\.?\d*)/);
49
- if (match) return parseFloat(match[1]);
50
- return parseFloat(cleaned);
51
- }
52
-
53
- // src/prompts/index.ts
54
- import fs from "fs";
55
- import path from "path";
56
- import { fileURLToPath } from "url";
57
- function resolvePromptsDir() {
58
- let dir = path.dirname(fileURLToPath(import.meta.url));
59
- for (let i = 0; i < 5; i++) {
60
- if (fs.existsSync(path.join(dir, "package.json"))) {
61
- return path.join(dir, "dist", "src", "prompts");
62
- }
63
- if (fs.existsSync(path.join(dir, "extraction.md"))) {
64
- return dir;
65
- }
66
- dir = path.dirname(dir);
67
- }
68
- return path.dirname(fileURLToPath(import.meta.url));
69
- }
70
- var PROMPTS_DIR = resolvePromptsDir();
71
- var promptCache = /* @__PURE__ */ new Map();
72
- function loadPrompt(name) {
73
- let cached = promptCache.get(name);
74
- if (!cached) {
75
- cached = fs.readFileSync(path.join(PROMPTS_DIR, `${name}.md`), "utf-8").trim();
76
- promptCache.set(name, cached);
77
- }
78
- return cached;
79
- }
80
- function interpolate(template, vars) {
81
- let result = template;
82
- for (const [key, value] of Object.entries(vars)) {
83
- result = result.replaceAll(`{{${key}}}`, value);
84
- }
85
- return result;
86
- }
87
- function buildExtractionPrompt(sessionId, eventCount, toolSummary, maxTokens) {
88
- return interpolate(loadPrompt("extraction"), {
89
- sessionId,
90
- eventCount: String(eventCount),
91
- toolSummary,
92
- maxTokens: String(maxTokens ?? 2048)
93
- });
94
- }
95
- function buildSummaryPrompt(sessionId, user, content, maxTokens) {
96
- return interpolate(loadPrompt("summary"), {
97
- sessionId,
98
- user,
99
- content,
100
- maxTokens: String(maxTokens ?? 1024)
101
- });
102
- }
103
- function buildTitlePrompt(summary, sessionId) {
104
- return interpolate(loadPrompt("title"), {
105
- summary,
106
- sessionId
107
- });
108
- }
109
- var ARTIFACT_TYPE_DESCRIPTIONS = [
110
- '"spec" \u2014 Design specifications, architecture documents',
111
- '"plan" \u2014 Implementation plans, roadmaps',
112
- '"rfc" \u2014 Requests for comment, proposals',
113
- '"doc" \u2014 Documentation, guides, READMEs',
114
- '"other" \u2014 Other substantive documents'
115
- ];
116
- function buildSimilarityPrompt(currentSummary, candidateSummary) {
117
- return interpolate(loadPrompt("session-similarity"), {
118
- currentSummary,
119
- candidateSummary
120
- });
121
- }
122
- function buildClassificationPrompt(sessionId, candidates, maxTokens) {
123
- const fileList = candidates.map((c) => {
124
- const truncated = c.content.slice(0, CANDIDATE_CONTENT_PREVIEW);
125
- return `### ${c.path}
126
- \`\`\`
127
- ${truncated}
128
- \`\`\``;
129
- }).join("\n\n");
130
- return interpolate(loadPrompt("classification"), {
131
- sessionId,
132
- fileList,
133
- artifactTypes: ARTIFACT_TYPE_DESCRIPTIONS.map((d) => `- ${d}`).join("\n"),
134
- validTypes: ARTIFACT_TYPES.join("|"),
135
- maxTokens: String(maxTokens ?? 1024)
136
- });
137
- }
138
-
139
- export {
140
- stripReasoningTokens,
141
- extractJson,
142
- extractNumber,
143
- loadPrompt,
144
- buildExtractionPrompt,
145
- buildSummaryPrompt,
146
- buildTitlePrompt,
147
- buildSimilarityPrompt,
148
- buildClassificationPrompt
149
- };
150
- //# sourceMappingURL=chunk-KYL67SKZ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/intelligence/response.ts","../src/prompts/index.ts"],"sourcesContent":["/**\n * Clean LLM response text before parsing.\n *\n * Reasoning models (DeepSeek, Qwen, GLM, etc.) embed chain-of-thought\n * in the response using special tags. These must be stripped before\n * JSON parsing or value extraction.\n */\n\n// Patterns for reasoning model chain-of-thought tokens.\n// Order matters: most specific patterns first.\nconst REASONING_PATTERNS = [\n // <think>...</think>answer (DeepSeek, Qwen, GLM, many others)\n /<think>[\\s\\S]*?<\\/think>\\s*/gi,\n // Implicit opening: reasoning...</think>answer (GLM-4.7 observed)\n /^[\\s\\S]*?<\\/think>\\s*/i,\n // <reasoning>...</reasoning>answer\n /<reasoning>[\\s\\S]*?<\\/reasoning>\\s*/gi,\n // <|thinking|>...<|/thinking|>answer\n /<\\|thinking\\|>[\\s\\S]*?<\\|\\/thinking\\|>\\s*/gi,\n // Plain-text \"Thinking Process:\" block followed by actual content\n // (Qwen 3.5 via LM Studio without native thinking mode)\n // Matches from \"Thinking Process:\" up to the last numbered step, then the synthesis follows\n /^Thinking Process:[\\s\\S]*?(?=\\n(?:## |# |\\*\\*[A-Z]))/i,\n];\n\n/**\n * Strip reasoning/chain-of-thought tokens from LLM response text.\n * Returns the final answer without the thinking process.\n */\nexport function stripReasoningTokens(text: string): string {\n if (!text) return text;\n\n for (const pattern of REASONING_PATTERNS) {\n const stripped = text.replace(pattern, '').trim();\n if (stripped && stripped !== text.trim()) {\n return stripped;\n }\n }\n\n return text;\n}\n\n/**\n * Extract JSON from an LLM response that may contain markdown fences,\n * reasoning tokens, or other wrapper text.\n *\n * Tries in order:\n * 1. Strip reasoning tokens\n * 2. Extract from ```json ... ``` code fences\n * 3. Find bare {...} JSON object\n * 4. Parse the cleaned text directly\n */\nexport function extractJson(text: string): unknown {\n const cleaned = stripReasoningTokens(text);\n\n // Try code fence extraction\n const fenceMatch = cleaned.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?```/);\n if (fenceMatch) {\n return JSON.parse(fenceMatch[1].trim());\n }\n\n // Try bare JSON object\n const objectMatch = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (objectMatch) {\n return JSON.parse(objectMatch[0]);\n }\n\n // Try direct parse\n return JSON.parse(cleaned);\n}\n\n/**\n * Extract a numeric value from an LLM response that may contain\n * reasoning tokens or extra text around the number.\n */\nexport function extractNumber(text: string): number {\n const cleaned = stripReasoningTokens(text).trim();\n const match = cleaned.match(/(\\d+\\.?\\d*)/);\n if (match) return parseFloat(match[1]);\n return parseFloat(cleaned);\n}\n","/**\n * Prompt loader — reads .md templates from disk and interpolates variables.\n * Prompts are markdown files in this directory, not TypeScript strings.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { ARTIFACT_TYPES } from '../vault/types.js';\nimport { CANDIDATE_CONTENT_PREVIEW } from '../constants.js';\n\n/**\n * Resolve the prompts directory. With tsup code-splitting, import.meta.url\n * points to a chunk file (dist/chunk-XXXX.js), not dist/src/prompts/.\n * Walk up from the current file to find package.json, then use dist/src/prompts/.\n */\nfunction resolvePromptsDir(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n if (fs.existsSync(path.join(dir, 'package.json'))) {\n return path.join(dir, 'dist', 'src', 'prompts');\n }\n // Also check if we're already in the right place (tsc output or dev mode)\n if (fs.existsSync(path.join(dir, 'extraction.md'))) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n // Final fallback: adjacent to current file (works with tsc)\n return path.dirname(fileURLToPath(import.meta.url));\n}\n\nconst PROMPTS_DIR = resolvePromptsDir();\n\nconst promptCache = new Map<string, string>();\n\nexport function loadPrompt(name: string): string {\n let cached = promptCache.get(name);\n if (!cached) {\n cached = fs.readFileSync(path.join(PROMPTS_DIR, `${name}.md`), 'utf-8').trim();\n promptCache.set(name, cached);\n }\n return cached;\n}\n\nfunction interpolate(template: string, vars: Record<string, string>): string {\n let result = template;\n for (const [key, value] of Object.entries(vars)) {\n result = result.replaceAll(`{{${key}}}`, value);\n }\n return result;\n}\n\n// --- Prompt builders ---\n\nexport function buildExtractionPrompt(\n sessionId: string,\n eventCount: number,\n toolSummary: string,\n maxTokens?: number,\n): string {\n return interpolate(loadPrompt('extraction'), {\n sessionId,\n eventCount: String(eventCount),\n toolSummary,\n maxTokens: String(maxTokens ?? 2048),\n });\n}\n\nexport function buildSummaryPrompt(\n sessionId: string,\n user: string,\n content: string,\n maxTokens?: number,\n): string {\n return interpolate(loadPrompt('summary'), {\n sessionId,\n user,\n content,\n maxTokens: String(maxTokens ?? 1024),\n });\n}\n\nexport function buildTitlePrompt(\n summary: string,\n sessionId: string,\n): string {\n return interpolate(loadPrompt('title'), {\n summary,\n sessionId,\n });\n}\n\nconst ARTIFACT_TYPE_DESCRIPTIONS = [\n '\"spec\" — Design specifications, architecture documents',\n '\"plan\" — Implementation plans, roadmaps',\n '\"rfc\" — Requests for comment, proposals',\n '\"doc\" — Documentation, guides, READMEs',\n '\"other\" — Other substantive documents',\n];\n\nexport function buildSimilarityPrompt(\n currentSummary: string,\n candidateSummary: string,\n): string {\n return interpolate(loadPrompt('session-similarity'), {\n currentSummary,\n candidateSummary,\n });\n}\n\nexport function buildClassificationPrompt(\n sessionId: string,\n candidates: Array<{ path: string; content: string }>,\n maxTokens?: number,\n): string {\n const fileList = candidates\n .map((c) => {\n const truncated = c.content.slice(0, CANDIDATE_CONTENT_PREVIEW);\n return `### ${c.path}\\n\\`\\`\\`\\n${truncated}\\n\\`\\`\\``;\n })\n .join('\\n\\n');\n\n return interpolate(loadPrompt('classification'), {\n sessionId,\n fileList,\n artifactTypes: ARTIFACT_TYPE_DESCRIPTIONS.map((d) => `- ${d}`).join('\\n'),\n validTypes: ARTIFACT_TYPES.join('|'),\n maxTokens: String(maxTokens ?? 1024),\n });\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/loader.ts","../src/config/migrations.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport { MycoConfigSchema, type MycoConfig } from './schema.js';\nimport { runMigrations, CURRENT_MIGRATION_VERSION } from './migrations.js';\n\nconst CONFIG_FILENAME = 'myco.yaml';\n\nexport function loadConfig(vaultDir: string): MycoConfig {\n const configPath = path.join(vaultDir, CONFIG_FILENAME);\n\n if (!fs.existsSync(configPath)) {\n throw new Error(`myco.yaml not found in ${vaultDir}`);\n }\n\n const raw = fs.readFileSync(configPath, 'utf-8');\n const parsed = YAML.parse(raw) as Record<string, unknown>;\n\n // Detect v1 config and guide migration\n if (parsed.version === 1 || (parsed.intelligence as Record<string, unknown>)?.backend) {\n throw new Error(\n 'Myco config uses v1 format. Run /myco:setup-llm to reconfigure for v2.',\n );\n }\n\n // Auto-map legacy 'haiku' provider name to 'anthropic'\n const intel = parsed.intelligence as Record<string, unknown> | undefined;\n const llm = intel?.llm as Record<string, unknown> | undefined;\n if (llm?.provider === 'haiku') {\n llm.provider = 'anthropic';\n }\n\n // Run numbered migrations\n const migrationsRan = runMigrations(parsed, vaultDir, (msg) => {\n // Log to stderr since this runs during config loading (before logger is available)\n process.stderr.write(`[myco migration] ${msg}\\n`);\n });\n\n // Parse with Zod to fill in defaults for new config sections\n const config = MycoConfigSchema.parse(parsed);\n\n // Write back if migrations ran or new defaults were added\n const needsWrite = migrationsRan\n || (parsed.config_version as number ?? 0) < CURRENT_MIGRATION_VERSION\n || !('digest' in parsed);\n\n if (needsWrite) {\n const fullConfig = JSON.parse(JSON.stringify(config)) as Record<string, unknown>;\n fs.writeFileSync(configPath, YAML.stringify(fullConfig), 'utf-8');\n }\n\n return config;\n}\n\nexport function saveConfig(vaultDir: string, config: MycoConfig): void {\n // Validate before writing — OAK lesson: validate on write, not just read\n const validated = MycoConfigSchema.parse(config);\n\n const configPath = path.join(vaultDir, CONFIG_FILENAME);\n fs.mkdirSync(vaultDir, { recursive: true });\n fs.writeFileSync(configPath, YAML.stringify(validated), 'utf-8');\n}\n","/**\n * Config and vault migrations — run once per version, tracked by config_version.\n *\n * Each migration has a version number, a name, and a function that receives\n * the raw parsed YAML doc and the vault directory. Migrations run in order\n * and are skipped if config_version is already past them.\n *\n * To add a new migration:\n * 1. Add an entry to MIGRATIONS with the next version number\n * 2. Write the migrate function — it receives the mutable doc and vaultDir\n * 3. The framework handles version tracking and writing the config back\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface Migration {\n version: number;\n name: string;\n migrate: (doc: Record<string, unknown>, vaultDir: string) => void;\n}\n\n/** Regex matching both quoted and unquoted YAML: type: memory, type: \"memory\", type: 'memory' */\nconst MEMORY_TYPE_PATTERN = /type:\\s*[\"']?memory[\"']?/g;\n\nexport const MIGRATIONS: Migration[] = [\n {\n version: 1,\n name: 'rename-memories-to-spores',\n migrate: (doc, vaultDir) => {\n // Config: rename context.layers.memories → context.layers.spores\n const context = doc.context as Record<string, unknown> | undefined;\n const layers = context?.layers as Record<string, unknown> | undefined;\n if (layers && 'memories' in layers && !('spores' in layers)) {\n layers.spores = layers.memories;\n delete layers.memories;\n }\n\n // Vault: rename memories/ directory → spores/\n const memoriesDir = path.join(vaultDir, 'memories');\n const sporesDir = path.join(vaultDir, 'spores');\n\n if (!fs.existsSync(memoriesDir)) return;\n\n if (fs.existsSync(sporesDir)) {\n // Both exist (interrupted migration) — merge remaining files\n const moveRemaining = (srcDir: string, destDir: string): void => {\n for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {\n const srcPath = path.join(srcDir, entry.name);\n const destPath = path.join(destDir, entry.name);\n if (entry.isDirectory()) {\n if (!fs.existsSync(destPath)) fs.mkdirSync(destPath, { recursive: true });\n moveRemaining(srcPath, destPath);\n } else if (!fs.existsSync(destPath)) {\n fs.renameSync(srcPath, destPath);\n }\n }\n };\n moveRemaining(memoriesDir, sporesDir);\n fs.rmSync(memoriesDir, { recursive: true, force: true });\n } else {\n fs.renameSync(memoriesDir, sporesDir);\n }\n\n // Update frontmatter type: memory → type: spore (handles quoted and unquoted)\n const walkUpdate = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) { walkUpdate(fullPath); continue; }\n if (!entry.name.endsWith('.md')) continue;\n const content = fs.readFileSync(fullPath, 'utf-8');\n MEMORY_TYPE_PATTERN.lastIndex = 0;\n if (MEMORY_TYPE_PATTERN.test(content)) {\n MEMORY_TYPE_PATTERN.lastIndex = 0;\n fs.writeFileSync(fullPath, content.replace(MEMORY_TYPE_PATTERN, 'type: spore'));\n }\n }\n };\n walkUpdate(sporesDir);\n\n // Update wikilinks in ALL vault files: [[memories/...]] → [[spores/...]]\n const walkLinks = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) { walkLinks(fullPath); continue; }\n if (!entry.name.endsWith('.md')) continue;\n const content = fs.readFileSync(fullPath, 'utf-8');\n if (content.includes('memories/')) {\n fs.writeFileSync(fullPath, content.replace(/memories\\//g, 'spores/'));\n }\n }\n };\n walkLinks(vaultDir);\n },\n },\n];\n\n/** Current migration version — the highest version in MIGRATIONS. */\nexport const CURRENT_MIGRATION_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;\n\n/**\n * Run all pending migrations on the raw config doc.\n * Returns true if any migrations ran (caller should reindex).\n */\nexport function runMigrations(\n doc: Record<string, unknown>,\n vaultDir: string,\n log?: (message: string) => void,\n): boolean {\n const currentVersion = (doc.config_version as number) ?? 0;\n let ran = false;\n\n for (const migration of MIGRATIONS) {\n if (migration.version <= currentVersion) continue;\n\n log?.(`Running migration ${migration.version}: ${migration.name}`);\n migration.migrate(doc, vaultDir);\n doc.config_version = migration.version;\n ran = true;\n }\n\n return ran;\n}\n"],"mappings":"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","names":["fs","path","path","fs","YAML"]}
@@ -1,231 +0,0 @@
1
- import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
- import {
3
- isActiveSpore,
4
- supersedeSpore,
5
- supersededIdsSchema
6
- } from "./chunk-FIA5NTRH.js";
7
- import "./chunk-JJL6AMDA.js";
8
- import {
9
- generateEmbedding
10
- } from "./chunk-RGVBGTD6.js";
11
- import {
12
- VectorIndex
13
- } from "./chunk-XQXXF6MU.js";
14
- import {
15
- loadPrompt,
16
- stripReasoningTokens
17
- } from "./chunk-KYL67SKZ.js";
18
- import "./chunk-2AMAOSRF.js";
19
- import {
20
- createEmbeddingProvider,
21
- createLlmProvider
22
- } from "./chunk-RCV2I4AI.js";
23
- import {
24
- MycoIndex
25
- } from "./chunk-AK6GNLPV.js";
26
- import "./chunk-JI6M2L2W.js";
27
- import {
28
- loadConfig
29
- } from "./chunk-TBRZAJ7W.js";
30
- import "./chunk-6UJWI4IW.js";
31
- import {
32
- CURATION_CLUSTER_SIMILARITY,
33
- EMBEDDING_INPUT_LIMIT,
34
- LLM_REASONING_MODE,
35
- SUPERSESSION_MAX_TOKENS
36
- } from "./chunk-B6WVNDA5.js";
37
- import "./chunk-PZUWP5VK.js";
38
-
39
- // src/cli/curate.ts
40
- import path from "path";
41
- var EMBEDDING_BATCH_SIZE = 10;
42
- function cosineSimilarity(a, b) {
43
- let dot = 0, normA = 0, normB = 0;
44
- for (let i = 0; i < a.length; i++) {
45
- dot += a[i] * b[i];
46
- normA += a[i] * a[i];
47
- normB += b[i] * b[i];
48
- }
49
- return dot / (Math.sqrt(normA) * Math.sqrt(normB));
50
- }
51
- function updateCentroid(spores) {
52
- if (spores.length === 0) return [];
53
- const dim = spores[0].embedding.length;
54
- const centroid = new Array(dim).fill(0);
55
- for (const s of spores) {
56
- for (let i = 0; i < dim; i++) {
57
- centroid[i] += s.embedding[i];
58
- }
59
- }
60
- for (let i = 0; i < dim; i++) {
61
- centroid[i] /= spores.length;
62
- }
63
- return centroid;
64
- }
65
- function clusterSpores(spores) {
66
- const clusters = [];
67
- for (const spore of spores) {
68
- let bestCluster = null;
69
- let bestSimilarity = -1;
70
- for (const cluster of clusters) {
71
- const sim = cosineSimilarity(spore.embedding, cluster.centroid);
72
- if (sim > bestSimilarity) {
73
- bestSimilarity = sim;
74
- bestCluster = cluster;
75
- }
76
- }
77
- if (bestCluster !== null && bestSimilarity >= CURATION_CLUSTER_SIMILARITY) {
78
- bestCluster.spores.push(spore);
79
- bestCluster.centroid = updateCentroid(bestCluster.spores);
80
- } else {
81
- clusters.push({ spores: [spore], centroid: [...spore.embedding] });
82
- }
83
- }
84
- return clusters;
85
- }
86
- async function run(args, vaultDir) {
87
- const isDryRun = args.includes("--dry-run");
88
- const config = loadConfig(vaultDir);
89
- const index = new MycoIndex(path.join(vaultDir, "index.db"));
90
- const llmProvider = createLlmProvider(config.intelligence.llm);
91
- const embeddingProvider = createEmbeddingProvider(config.intelligence.embedding);
92
- let vectorIndex = null;
93
- try {
94
- const testEmbed = await embeddingProvider.embed("test");
95
- vectorIndex = new VectorIndex(path.join(vaultDir, "vectors.db"), testEmbed.dimensions);
96
- } catch (e) {
97
- console.error(`Vector index unavailable: ${e.message}`);
98
- console.error("Curate requires a working embedding provider.");
99
- index.close();
100
- process.exit(1);
101
- }
102
- try {
103
- if (isDryRun) {
104
- console.log("Dry run \u2014 no changes will be written.\n");
105
- }
106
- const allSpores = index.query({ type: "spore" });
107
- const activeSpores = allSpores.filter((n) => isActiveSpore(n.frontmatter));
108
- console.log(`Scanning ${activeSpores.length} active spores...`);
109
- if (activeSpores.length === 0) {
110
- console.log("No active spores found.");
111
- return;
112
- }
113
- const sporesWithEmbeddings = [];
114
- let embedFailures = 0;
115
- for (let i = 0; i < activeSpores.length; i += EMBEDDING_BATCH_SIZE) {
116
- const batch = activeSpores.slice(i, i + EMBEDDING_BATCH_SIZE);
117
- const results = await Promise.allSettled(
118
- batch.map(async (spore) => {
119
- const text = spore.content.slice(0, EMBEDDING_INPUT_LIMIT);
120
- const result = await generateEmbedding(embeddingProvider, text);
121
- return { spore, embedding: result.embedding };
122
- })
123
- );
124
- for (const result of results) {
125
- if (result.status === "fulfilled") {
126
- const { spore, embedding } = result.value;
127
- sporesWithEmbeddings.push({
128
- id: spore.id,
129
- path: spore.path,
130
- title: spore.title,
131
- content: spore.content,
132
- created: spore.created,
133
- frontmatter: spore.frontmatter,
134
- embedding
135
- });
136
- } else {
137
- embedFailures++;
138
- }
139
- }
140
- }
141
- if (embedFailures > 0) {
142
- console.log(`Warning: ${embedFailures} spore(s) could not be embedded and were skipped.`);
143
- }
144
- const byType = /* @__PURE__ */ new Map();
145
- for (const spore of sporesWithEmbeddings) {
146
- const obsType = spore.frontmatter["observation_type"] ?? "unknown";
147
- if (!byType.has(obsType)) byType.set(obsType, []);
148
- byType.get(obsType).push(spore);
149
- }
150
- const template = loadPrompt("supersession");
151
- let totalClusters = 0;
152
- let totalSuperseded = 0;
153
- for (const [obsType, typeSpores] of byType) {
154
- const clusters = clusterSpores(typeSpores);
155
- const multiSpore = clusters.filter((c) => c.spores.length >= 2);
156
- if (multiSpore.length === 0) continue;
157
- console.log(`
158
- Type: ${obsType} \u2014 ${typeSpores.length} spores, ${multiSpore.length} cluster(s) to evaluate`);
159
- totalClusters += multiSpore.length;
160
- for (const cluster of multiSpore) {
161
- const sorted = [...cluster.spores].sort((a, b) => a.created.localeCompare(b.created));
162
- const newest = sorted[sorted.length - 1];
163
- const candidates = sorted.slice(0, sorted.length - 1);
164
- const newSporeText = `[${newest.id}] ${newest.title}
165
- ${newest.content}`;
166
- const candidatesText = candidates.map((c) => `[${c.id}] ${c.title}
167
- ${c.content}`).join("\n\n");
168
- const prompt = template.replace("{{new_spore}}", newSporeText).replace("{{candidates}}", candidatesText);
169
- let responseText;
170
- try {
171
- const response = await llmProvider.summarize(prompt, {
172
- maxTokens: SUPERSESSION_MAX_TOKENS,
173
- reasoning: LLM_REASONING_MODE
174
- });
175
- responseText = stripReasoningTokens(response.text);
176
- } catch (err) {
177
- console.log(` Warning: LLM call failed for cluster in ${obsType}: ${String(err)}`);
178
- continue;
179
- }
180
- let rawIds;
181
- try {
182
- rawIds = JSON.parse(responseText);
183
- } catch {
184
- console.log(` Warning: Could not parse LLM response for cluster in ${obsType}`);
185
- continue;
186
- }
187
- const parsed = supersededIdsSchema.safeParse(rawIds);
188
- if (!parsed.success) {
189
- console.log(` Warning: LLM response schema invalid for cluster in ${obsType}`);
190
- continue;
191
- }
192
- const candidateMap = new Map(candidates.map((c) => [c.id, c]));
193
- const validIds = parsed.data.filter((id) => candidateMap.has(id));
194
- if (validIds.length === 0) continue;
195
- for (const id of validIds) {
196
- const candidate = candidateMap.get(id);
197
- if (isDryRun) {
198
- console.log(` [dry-run] Would supersede: ${candidate.title} (${id})`);
199
- console.log(` Superseded by: ${newest.title} (${newest.id})`);
200
- totalSuperseded++;
201
- continue;
202
- }
203
- const wrote = supersedeSpore(id, newest.id, candidate.path, { index, vectorIndex, vaultDir });
204
- if (!wrote) {
205
- console.log(` Warning: file not found for ${id}, skipping write`);
206
- continue;
207
- }
208
- console.log(` Superseded: ${candidate.title} (${id})`);
209
- console.log(` By: ${newest.title} (${newest.id})`);
210
- totalSuperseded++;
211
- }
212
- }
213
- }
214
- console.log(`
215
- Curation complete:`);
216
- console.log(` Scanned: ${activeSpores.length} active spores`);
217
- console.log(` Clusters evaluated: ${totalClusters}`);
218
- if (isDryRun) {
219
- console.log(` Would supersede: ${totalSuperseded}`);
220
- } else {
221
- console.log(` Superseded: ${totalSuperseded}`);
222
- }
223
- } finally {
224
- index.close();
225
- vectorIndex?.close();
226
- }
227
- }
228
- export {
229
- run
230
- };
231
- //# sourceMappingURL=curate-S4HOYWXA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/curate.ts"],"sourcesContent":["/**\n * myco curate — scan the vault for stale spores and supersede them.\n *\n * Usage:\n * myco curate Scan and supersede stale spores\n * myco curate --dry-run Show what would be superseded without writing\n *\n * Algorithm:\n * 1. Load all active spores from the index\n * 2. Group by observation_type\n * 3. Within each group, embed spores and cluster by cosine similarity\n * 4. For each cluster with 2+ members, ask the LLM which are outdated\n * 5. Mark superseded: update frontmatter, append notice, re-index, remove vector\n */\nimport path from 'node:path';\nimport { loadConfig } from '../config/loader.js';\nimport { MycoIndex } from '../index/sqlite.js';\nimport { VectorIndex } from '../index/vectors.js';\nimport { createLlmProvider, createEmbeddingProvider } from '../intelligence/llm.js';\nimport { generateEmbedding } from '../intelligence/embeddings.js';\nimport { stripReasoningTokens } from '../intelligence/response.js';\nimport { loadPrompt } from '../prompts/index.js';\nimport { supersedeSpore, supersededIdsSchema, isActiveSpore } from '../vault/curation.js';\nimport {\n CURATION_CLUSTER_SIMILARITY,\n EMBEDDING_INPUT_LIMIT,\n SUPERSESSION_MAX_TOKENS,\n LLM_REASONING_MODE,\n} from '../constants.js';\n\n/** Max concurrent embedding requests to avoid overwhelming the provider. */\nconst EMBEDDING_BATCH_SIZE = 10;\n\nfunction cosineSimilarity(a: number[], b: number[]): number {\n let dot = 0, normA = 0, normB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\n}\n\ninterface SporeWithEmbedding {\n id: string;\n path: string;\n title: string;\n content: string;\n created: string;\n frontmatter: Record<string, unknown>;\n embedding: number[];\n}\n\ninterface Cluster {\n spores: SporeWithEmbedding[];\n centroid: number[];\n}\n\nfunction updateCentroid(spores: SporeWithEmbedding[]): number[] {\n if (spores.length === 0) return [];\n const dim = spores[0].embedding.length;\n const centroid = new Array<number>(dim).fill(0);\n for (const s of spores) {\n for (let i = 0; i < dim; i++) {\n centroid[i] += s.embedding[i];\n }\n }\n for (let i = 0; i < dim; i++) {\n centroid[i] /= spores.length;\n }\n return centroid;\n}\n\nfunction clusterSpores(spores: SporeWithEmbedding[]): Cluster[] {\n const clusters: Cluster[] = [];\n\n for (const spore of spores) {\n let bestCluster: Cluster | null = null;\n let bestSimilarity = -1;\n\n for (const cluster of clusters) {\n const sim = cosineSimilarity(spore.embedding, cluster.centroid);\n if (sim > bestSimilarity) {\n bestSimilarity = sim;\n bestCluster = cluster;\n }\n }\n\n if (bestCluster !== null && bestSimilarity >= CURATION_CLUSTER_SIMILARITY) {\n bestCluster.spores.push(spore);\n bestCluster.centroid = updateCentroid(bestCluster.spores);\n } else {\n clusters.push({ spores: [spore], centroid: [...spore.embedding] });\n }\n }\n\n return clusters;\n}\n\nexport async function run(args: string[], vaultDir: string): Promise<void> {\n const isDryRun = args.includes('--dry-run');\n\n const config = loadConfig(vaultDir);\n const index = new MycoIndex(path.join(vaultDir, 'index.db'));\n\n const llmProvider = createLlmProvider(config.intelligence.llm);\n const embeddingProvider = createEmbeddingProvider(config.intelligence.embedding);\n\n let vectorIndex: VectorIndex | null = null;\n try {\n const testEmbed = await embeddingProvider.embed('test');\n vectorIndex = new VectorIndex(path.join(vaultDir, 'vectors.db'), testEmbed.dimensions);\n } catch (e) {\n console.error(`Vector index unavailable: ${(e as Error).message}`);\n console.error('Curate requires a working embedding provider.');\n index.close();\n process.exit(1);\n }\n\n try {\n if (isDryRun) {\n console.log('Dry run — no changes will be written.\\n');\n }\n\n // 1. Query all spores and filter for active ones\n const allSpores = index.query({ type: 'spore' });\n const activeSpores = allSpores.filter((n) => isActiveSpore(n.frontmatter));\n\n console.log(`Scanning ${activeSpores.length} active spores...`);\n\n if (activeSpores.length === 0) {\n console.log('No active spores found.');\n return;\n }\n\n // 2. Embed all active spores (batched for concurrency)\n const sporesWithEmbeddings: SporeWithEmbedding[] = [];\n let embedFailures = 0;\n\n for (let i = 0; i < activeSpores.length; i += EMBEDDING_BATCH_SIZE) {\n const batch = activeSpores.slice(i, i + EMBEDDING_BATCH_SIZE);\n const results = await Promise.allSettled(\n batch.map(async (spore) => {\n const text = spore.content.slice(0, EMBEDDING_INPUT_LIMIT);\n const result = await generateEmbedding(embeddingProvider, text);\n return { spore, embedding: result.embedding };\n }),\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n const { spore, embedding } = result.value;\n sporesWithEmbeddings.push({\n id: spore.id,\n path: spore.path,\n title: spore.title,\n content: spore.content,\n created: spore.created,\n frontmatter: spore.frontmatter,\n embedding,\n });\n } else {\n embedFailures++;\n }\n }\n }\n\n if (embedFailures > 0) {\n console.log(`Warning: ${embedFailures} spore(s) could not be embedded and were skipped.`);\n }\n\n // 3. Group by observation_type\n const byType = new Map<string, SporeWithEmbedding[]>();\n for (const spore of sporesWithEmbeddings) {\n const obsType = (spore.frontmatter['observation_type'] as string | undefined) ?? 'unknown';\n if (!byType.has(obsType)) byType.set(obsType, []);\n byType.get(obsType)!.push(spore);\n }\n\n // 4. Cluster within each type group\n const template = loadPrompt('supersession');\n let totalClusters = 0;\n let totalSuperseded = 0;\n\n for (const [obsType, typeSpores] of byType) {\n const clusters = clusterSpores(typeSpores);\n const multiSpore = clusters.filter((c) => c.spores.length >= 2);\n\n if (multiSpore.length === 0) continue;\n\n console.log(`\\nType: ${obsType} — ${typeSpores.length} spores, ${multiSpore.length} cluster(s) to evaluate`);\n totalClusters += multiSpore.length;\n\n for (const cluster of multiSpore) {\n // Sort by created date ascending; newest is last\n const sorted = [...cluster.spores].sort((a, b) => a.created.localeCompare(b.created));\n const newest = sorted[sorted.length - 1];\n const candidates = sorted.slice(0, sorted.length - 1);\n\n // 5. Build supersession prompt\n const newSporeText = `[${newest.id}] ${newest.title}\\n${newest.content}`;\n const candidatesText = candidates\n .map((c) => `[${c.id}] ${c.title}\\n${c.content}`)\n .join('\\n\\n');\n\n const prompt = template\n .replace('{{new_spore}}', newSporeText)\n .replace('{{candidates}}', candidatesText);\n\n // 6. Ask LLM which candidates are outdated\n let responseText: string;\n try {\n const response = await llmProvider.summarize(prompt, {\n maxTokens: SUPERSESSION_MAX_TOKENS,\n reasoning: LLM_REASONING_MODE,\n });\n responseText = stripReasoningTokens(response.text);\n } catch (err) {\n console.log(` Warning: LLM call failed for cluster in ${obsType}: ${String(err)}`);\n continue;\n }\n\n // Parse response\n let rawIds: unknown;\n try {\n rawIds = JSON.parse(responseText);\n } catch {\n console.log(` Warning: Could not parse LLM response for cluster in ${obsType}`);\n continue;\n }\n\n const parsed = supersededIdsSchema.safeParse(rawIds);\n if (!parsed.success) {\n console.log(` Warning: LLM response schema invalid for cluster in ${obsType}`);\n continue;\n }\n\n // Validate IDs against actual candidates\n const candidateMap = new Map(candidates.map((c) => [c.id, c]));\n const validIds = parsed.data.filter((id) => candidateMap.has(id));\n\n if (validIds.length === 0) continue;\n\n for (const id of validIds) {\n const candidate = candidateMap.get(id)!;\n\n if (isDryRun) {\n console.log(` [dry-run] Would supersede: ${candidate.title} (${id})`);\n console.log(` Superseded by: ${newest.title} (${newest.id})`);\n totalSuperseded++;\n continue;\n }\n\n const wrote = supersedeSpore(id, newest.id, candidate.path, { index, vectorIndex, vaultDir });\n\n if (!wrote) {\n console.log(` Warning: file not found for ${id}, skipping write`);\n continue;\n }\n\n console.log(` Superseded: ${candidate.title} (${id})`);\n console.log(` By: ${newest.title} (${newest.id})`);\n totalSuperseded++;\n }\n }\n }\n\n // 8. Summary\n console.log(`\\nCuration complete:`);\n console.log(` Scanned: ${activeSpores.length} active spores`);\n console.log(` Clusters evaluated: ${totalClusters}`);\n if (isDryRun) {\n console.log(` Would supersede: ${totalSuperseded}`);\n } else {\n console.log(` Superseded: ${totalSuperseded}`);\n }\n } finally {\n index.close();\n vectorIndex?.close();\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/digest.ts"],"sourcesContent":["/**\n * myco digest — run a digest cycle from the CLI.\n *\n * Usage:\n * myco digest Incremental cycle (only new substrate)\n * myco digest --full Full reprocess of all tiers from clean slate\n * myco digest --tier 3000 Reprocess a specific tier from clean slate\n *\n * When --tier or --full is used, the cycle processes ALL vault notes (not just\n * new ones) and ignores previous extracts, producing a clean synthesis.\n */\nimport { loadConfig } from '../config/loader.js';\nimport { MycoIndex } from '../index/sqlite.js';\nimport { createLlmProvider } from '../intelligence/llm.js';\nimport { DigestEngine } from '../daemon/digest.js';\nimport type { DigestCycleOptions } from '../daemon/digest.js';\nimport { parseIntFlag } from './shared.js';\nimport path from 'node:path';\n\nexport async function run(args: string[], vaultDir: string): Promise<void> {\n const config = loadConfig(vaultDir);\n\n if (!config.digest.enabled) {\n console.error('Digest is not enabled. Set digest.enabled: true in myco.yaml.');\n process.exit(1);\n }\n\n const tierArg = parseIntFlag(args, '--tier');\n const isFull = args.includes('--full');\n const isReprocess = isFull || tierArg !== undefined;\n\n // Resolve the digest LLM provider\n const digestLlmConfig = {\n provider: config.digest.intelligence.provider ?? config.intelligence.llm.provider,\n model: config.digest.intelligence.model ?? config.intelligence.llm.model,\n base_url: config.digest.intelligence.base_url ?? config.intelligence.llm.base_url,\n context_window: config.digest.intelligence.context_window,\n };\n const llmProvider = createLlmProvider(digestLlmConfig);\n\n const index = new MycoIndex(path.join(vaultDir, 'index.db'));\n\n const engine = new DigestEngine({\n vaultDir,\n index,\n llmProvider,\n config,\n log: (level, message, data) => {\n const prefix = level === 'warn' ? '⚠' : level === 'info' ? '→' : ' ';\n const suffix = data ? ` ${JSON.stringify(data)}` : '';\n console.log(`${prefix} ${message}${suffix}`);\n },\n });\n\n const opts: DigestCycleOptions = {};\n if (isReprocess) {\n opts.fullReprocess = true;\n opts.cleanSlate = true;\n }\n if (tierArg !== undefined) {\n const eligible = engine.getEligibleTiers();\n if (!eligible.includes(tierArg)) {\n console.error(`Tier ${tierArg} is not eligible. Eligible tiers: [${eligible.join(', ')}]`);\n index.close();\n process.exit(1);\n }\n opts.tiers = [tierArg];\n }\n\n if (isReprocess) {\n const tierLabel = tierArg ? `tier ${tierArg}` : 'all tiers';\n console.log(`Full reprocess of ${tierLabel} — clean slate, all substrate`);\n } else {\n console.log('Running incremental digest cycle');\n }\n\n try {\n const result = await engine.runCycle(opts);\n\n if (!result) {\n console.log('No substrate found — nothing to digest.');\n return;\n }\n\n console.log(`\\nDigest cycle complete:`);\n console.log(` Tiers generated: [${result.tiersGenerated.join(', ')}]`);\n console.log(` Substrate: ${Object.values(result.substrate).flat().length} notes`);\n console.log(` Duration: ${(result.durationMs / 1000).toFixed(1)}s`);\n console.log(` Model: ${result.model}`);\n } finally {\n index.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,OAAO,UAAU;AAEjB,eAAsB,IAAI,MAAgB,UAAiC;AACzE,QAAM,SAAS,WAAW,QAAQ;AAElC,MAAI,CAAC,OAAO,OAAO,SAAS;AAC1B,YAAQ,MAAM,+DAA+D;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,aAAa,MAAM,QAAQ;AAC3C,QAAM,SAAS,KAAK,SAAS,QAAQ;AACrC,QAAM,cAAc,UAAU,YAAY;AAG1C,QAAM,kBAAkB;AAAA,IACtB,UAAU,OAAO,OAAO,aAAa,YAAY,OAAO,aAAa,IAAI;AAAA,IACzE,OAAO,OAAO,OAAO,aAAa,SAAS,OAAO,aAAa,IAAI;AAAA,IACnE,UAAU,OAAO,OAAO,aAAa,YAAY,OAAO,aAAa,IAAI;AAAA,IACzE,gBAAgB,OAAO,OAAO,aAAa;AAAA,EAC7C;AACA,QAAM,cAAc,kBAAkB,eAAe;AAErD,QAAM,QAAQ,IAAI,UAAU,KAAK,KAAK,UAAU,UAAU,CAAC;AAE3D,QAAM,SAAS,IAAI,aAAa;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,CAAC,OAAO,SAAS,SAAS;AAC7B,YAAM,SAAS,UAAU,SAAS,WAAM,UAAU,SAAS,WAAM;AACjE,YAAM,SAAS,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AACnD,cAAQ,IAAI,GAAG,MAAM,IAAI,OAAO,GAAG,MAAM,EAAE;AAAA,IAC7C;AAAA,EACF,CAAC;AAED,QAAM,OAA2B,CAAC;AAClC,MAAI,aAAa;AACf,SAAK,gBAAgB;AACrB,SAAK,aAAa;AAAA,EACpB;AACA,MAAI,YAAY,QAAW;AACzB,UAAM,WAAW,OAAO,iBAAiB;AACzC,QAAI,CAAC,SAAS,SAAS,OAAO,GAAG;AAC/B,cAAQ,MAAM,QAAQ,OAAO,sCAAsC,SAAS,KAAK,IAAI,CAAC,GAAG;AACzF,YAAM,MAAM;AACZ,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,SAAK,QAAQ,CAAC,OAAO;AAAA,EACvB;AAEA,MAAI,aAAa;AACf,UAAM,YAAY,UAAU,QAAQ,OAAO,KAAK;AAChD,YAAQ,IAAI,qBAAqB,SAAS,oCAA+B;AAAA,EAC3E,OAAO;AACL,YAAQ,IAAI,kCAAkC;AAAA,EAChD;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,SAAS,IAAI;AAEzC,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI,8CAAyC;AACrD;AAAA,IACF;AAEA,YAAQ,IAAI;AAAA,uBAA0B;AACtC,YAAQ,IAAI,uBAAuB,OAAO,eAAe,KAAK,IAAI,CAAC,GAAG;AACtE,YAAQ,IAAI,gBAAgB,OAAO,OAAO,OAAO,SAAS,EAAE,KAAK,EAAE,MAAM,QAAQ;AACjF,YAAQ,IAAI,gBAAgB,OAAO,aAAa,KAAM,QAAQ,CAAC,CAAC,GAAG;AACnE,YAAQ,IAAI,YAAY,OAAO,KAAK,EAAE;AAAA,EACxC,UAAE;AACA,UAAM,MAAM;AAAA,EACd;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/init.ts"],"sourcesContent":["import { MycoIndex } from '../index/sqlite.js';\nimport { initFts } from '../index/fts.js';\nimport { resolveVaultDir } from '../vault/resolve.js';\nimport {\n parseStringFlag,\n DASHBOARD_CONTENT,\n VAULT_GITIGNORE,\n configureVaultEnv,\n} from './shared.js';\nimport { MycoConfigSchema } from '../config/schema.js';\nimport { run as setupLlm } from './setup-llm.js';\nimport { run as setupDigest } from './setup-digest.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport YAML from 'yaml';\n\nexport async function run(args: string[]): Promise<void> {\n const vaultPath = parseStringFlag(args, '--vault');\n const user = parseStringFlag(args, '--user') ?? '';\n const teamEnabled = args.includes('--team');\n\n // Resolve vault directory\n const vaultDir = vaultPath\n ? (vaultPath.startsWith('~/') ? path.join(os.homedir(), vaultPath.slice(2)) : path.resolve(vaultPath))\n : path.join(resolveVaultDir());\n\n // Check if already initialized\n if (fs.existsSync(path.join(vaultDir, 'myco.yaml'))) {\n console.log(`Vault already initialized at ${vaultDir}`);\n return;\n }\n\n console.log(`Initializing Myco vault at ${vaultDir}`);\n\n // Create directory structure\n const dirs = ['sessions', 'plans', 'spores', 'artifacts', 'team', 'buffer', 'logs'];\n for (const dir of dirs) {\n fs.mkdirSync(path.join(vaultDir, dir), { recursive: true });\n }\n\n // Write myco.yaml — only version is truly required, everything else has Zod defaults\n const config = MycoConfigSchema.parse({\n version: 2,\n team: { user, enabled: teamEnabled },\n });\n\n fs.writeFileSync(\n path.join(vaultDir, 'myco.yaml'),\n YAML.stringify(config),\n 'utf-8',\n );\n\n // Write .gitignore\n fs.writeFileSync(path.join(vaultDir, '.gitignore'), VAULT_GITIGNORE, 'utf-8');\n\n // Write Obsidian dashboard\n fs.writeFileSync(path.join(vaultDir, '_dashboard.md'), DASHBOARD_CONTENT, 'utf-8');\n\n // Initialize FTS index\n const index = new MycoIndex(path.join(vaultDir, 'index.db'));\n initFts(index);\n index.close();\n\n // Apply LLM provider settings from flags (if any were passed)\n const llmFlags: string[] = [];\n const llmProvider = parseStringFlag(args, '--llm-provider');\n const llmModel = parseStringFlag(args, '--llm-model');\n const llmUrl = parseStringFlag(args, '--llm-url');\n if (llmProvider) llmFlags.push('--llm-provider', llmProvider);\n if (llmModel) llmFlags.push('--llm-model', llmModel);\n if (llmUrl) llmFlags.push('--llm-url', llmUrl);\n const embeddingProvider = parseStringFlag(args, '--embedding-provider');\n const embeddingModel = parseStringFlag(args, '--embedding-model');\n const embeddingUrl = parseStringFlag(args, '--embedding-url');\n if (embeddingProvider) llmFlags.push('--embedding-provider', embeddingProvider);\n if (embeddingModel) llmFlags.push('--embedding-model', embeddingModel);\n if (embeddingUrl) llmFlags.push('--embedding-url', embeddingUrl);\n\n if (llmFlags.length > 0) {\n await setupLlm(llmFlags, vaultDir);\n }\n\n // Apply digest settings from flags (if any were passed)\n const digestFlags: string[] = [];\n const tiers = parseStringFlag(args, '--tiers');\n const injectTier = parseStringFlag(args, '--inject-tier');\n const contextWindow = parseStringFlag(args, '--context-window');\n if (tiers) digestFlags.push('--tiers', tiers);\n if (injectTier) digestFlags.push('--inject-tier', injectTier);\n if (contextWindow) digestFlags.push('--context-window', contextWindow);\n\n if (digestFlags.length > 0) {\n await setupDigest(digestFlags, vaultDir);\n }\n\n // Summary\n console.log('');\n console.log('=== Myco Vault Initialized ===');\n console.log(`Path: ${vaultDir}`);\n console.log(`Team mode: ${teamEnabled ? 'enabled' : 'disabled'}`);\n if (user) console.log(`User: ${user}`);\n console.log('');\n\n // If vault is outside the project, configure MYCO_VAULT_DIR for the current agent\n const projectRoot = path.resolve('.');\n const isProjectLocal = vaultDir.startsWith(projectRoot);\n if (!isProjectLocal) {\n configureVaultEnv(projectRoot, vaultDir);\n }\n\n console.log('Next: start a coding session — Myco will begin capturing automatically.');\n}\n"],"mappings":"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","names":["run","YAML"]}