@goondocks/myco 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +5 -1
- package/dist/chunk-2AMAOSRF.js +105 -0
- package/dist/chunk-2AMAOSRF.js.map +1 -0
- package/dist/{chunk-I7PNZEBO.js → chunk-6LTNFMXO.js} +12 -1
- package/dist/{chunk-I7PNZEBO.js.map → chunk-6LTNFMXO.js.map} +1 -1
- package/dist/{chunk-2GJFTIWX.js → chunk-7KQB22DP.js} +2 -2
- package/dist/{chunk-JBD5KP5G.js → chunk-B6WVNDA5.js} +14 -2
- package/dist/chunk-B6WVNDA5.js.map +1 -0
- package/dist/chunk-FIA5NTRH.js +159 -0
- package/dist/chunk-FIA5NTRH.js.map +1 -0
- package/dist/{chunk-GFBG73P4.js → chunk-FIRMTYFH.js} +3 -3
- package/dist/{chunk-XCPQHC4X.js → chunk-HJG7Z6SJ.js} +2 -2
- package/dist/chunk-HL2S5QZG.js +385 -0
- package/dist/chunk-HL2S5QZG.js.map +1 -0
- package/dist/{chunk-WBT5DWGC.js → chunk-IURC35BF.js} +2 -2
- package/dist/{chunk-67R6EMYD.js → chunk-JI6M2L2W.js} +31 -52
- package/dist/chunk-JI6M2L2W.js.map +1 -0
- package/dist/{chunk-FPEDTLQ6.js → chunk-JJL6AMDA.js} +3 -101
- package/dist/chunk-JJL6AMDA.js.map +1 -0
- package/dist/chunk-KYL67SKZ.js +150 -0
- package/dist/chunk-KYL67SKZ.js.map +1 -0
- package/dist/{chunk-ZCBL5HER.js → chunk-ND4VK6C7.js} +2 -2
- package/dist/{chunk-V2OWD2VV.js → chunk-R6LQT3U7.js} +24 -146
- package/dist/chunk-R6LQT3U7.js.map +1 -0
- package/dist/{chunk-IYFKPSRP.js → chunk-RCV2I4AI.js} +3 -3
- package/dist/{chunk-BNIYWCST.js → chunk-X6TKHO22.js} +2 -2
- package/dist/{chunk-OUFSLZTX.js → chunk-ZWUFTOG3.js} +21 -9
- package/dist/chunk-ZWUFTOG3.js.map +1 -0
- package/dist/{cli-PMOFCZQL.js → cli-BLYNNKGJ.js} +24 -18
- package/dist/cli-BLYNNKGJ.js.map +1 -0
- package/dist/{client-5SUO2UYH.js → client-5GB4WVXE.js} +5 -5
- package/dist/curate-S4HOYWXA.js +231 -0
- package/dist/curate-S4HOYWXA.js.map +1 -0
- package/dist/{detect-providers-IRL2TTLK.js → detect-providers-BIHYFK5M.js} +3 -3
- package/dist/digest-7NKYXM6G.js +96 -0
- package/dist/digest-7NKYXM6G.js.map +1 -0
- package/dist/{init-NUF5UBUJ.js → init-HPQ77WWF.js} +5 -5
- package/dist/{main-2XEBVUR6.js → main-NFQ4II75.js} +253 -576
- package/dist/main-NFQ4II75.js.map +1 -0
- package/dist/{rebuild-E6YFIRYZ.js → rebuild-KQ6G2GZM.js} +8 -7
- package/dist/{rebuild-E6YFIRYZ.js.map → rebuild-KQ6G2GZM.js.map} +1 -1
- package/dist/{reprocess-7G7KQWCN.js → reprocess-ZL4HKTSC.js} +95 -24
- package/dist/reprocess-ZL4HKTSC.js.map +1 -0
- package/dist/{restart-ABW4ZK3P.js → restart-FYW662DR.js} +6 -6
- package/dist/{search-MPD7SFK6.js → search-E5JQMTXV.js} +6 -6
- package/dist/{server-NZLZRITH.js → server-TV3D35HZ.js} +38 -15
- package/dist/{server-NZLZRITH.js.map → server-TV3D35HZ.js.map} +1 -1
- package/dist/{session-start-YB4A4PZB.js → session-start-5MFEOVQ5.js} +6 -6
- package/dist/{setup-digest-K732MGOJ.js → setup-digest-DZAFIBEF.js} +5 -5
- package/dist/{setup-llm-XCCH5LYD.js → setup-llm-4BZM33YT.js} +5 -5
- package/dist/src/cli.js +4 -4
- package/dist/src/daemon/main.js +4 -4
- package/dist/src/hooks/post-tool-use.js +5 -5
- package/dist/src/hooks/session-end.js +5 -5
- package/dist/src/hooks/session-start.js +4 -4
- package/dist/src/hooks/stop.js +6 -6
- package/dist/src/hooks/stop.js.map +1 -1
- package/dist/src/hooks/user-prompt-submit.js +5 -5
- package/dist/src/mcp/server.js +4 -4
- package/dist/src/prompts/extraction.md +1 -1
- package/dist/src/prompts/summary.md +1 -11
- package/dist/src/prompts/supersession.md +32 -0
- package/dist/{stats-6G7SN5YZ.js → stats-ZIIJ2GB3.js} +5 -5
- package/dist/{verify-JFHQH55Z.js → verify-RACBFT2P.js} +4 -4
- package/dist/{version-5B2TWXQJ.js → version-HJTVNPOO.js} +4 -4
- package/package.json +1 -1
- package/skills/setup/SKILL.md +56 -28
- package/skills/setup/references/model-recommendations.md +49 -43
- package/dist/chunk-67R6EMYD.js.map +0 -1
- package/dist/chunk-FPEDTLQ6.js.map +0 -1
- package/dist/chunk-JBD5KP5G.js.map +0 -1
- package/dist/chunk-OUFSLZTX.js.map +0 -1
- package/dist/chunk-V2OWD2VV.js.map +0 -1
- package/dist/cli-PMOFCZQL.js.map +0 -1
- package/dist/main-2XEBVUR6.js.map +0 -1
- package/dist/reprocess-7G7KQWCN.js.map +0 -1
- /package/dist/{chunk-2GJFTIWX.js.map → chunk-7KQB22DP.js.map} +0 -0
- /package/dist/{chunk-GFBG73P4.js.map → chunk-FIRMTYFH.js.map} +0 -0
- /package/dist/{chunk-XCPQHC4X.js.map → chunk-HJG7Z6SJ.js.map} +0 -0
- /package/dist/{chunk-WBT5DWGC.js.map → chunk-IURC35BF.js.map} +0 -0
- /package/dist/{chunk-ZCBL5HER.js.map → chunk-ND4VK6C7.js.map} +0 -0
- /package/dist/{chunk-IYFKPSRP.js.map → chunk-RCV2I4AI.js.map} +0 -0
- /package/dist/{chunk-BNIYWCST.js.map → chunk-X6TKHO22.js.map} +0 -0
- /package/dist/{client-5SUO2UYH.js.map → client-5GB4WVXE.js.map} +0 -0
- /package/dist/{detect-providers-IRL2TTLK.js.map → detect-providers-BIHYFK5M.js.map} +0 -0
- /package/dist/{init-NUF5UBUJ.js.map → init-HPQ77WWF.js.map} +0 -0
- /package/dist/{restart-ABW4ZK3P.js.map → restart-FYW662DR.js.map} +0 -0
- /package/dist/{search-MPD7SFK6.js.map → search-E5JQMTXV.js.map} +0 -0
- /package/dist/{session-start-YB4A4PZB.js.map → session-start-5MFEOVQ5.js.map} +0 -0
- /package/dist/{setup-digest-K732MGOJ.js.map → setup-digest-DZAFIBEF.js.map} +0 -0
- /package/dist/{setup-llm-XCCH5LYD.js.map → setup-llm-4BZM33YT.js.map} +0 -0
- /package/dist/{stats-6G7SN5YZ.js.map → stats-ZIIJ2GB3.js.map} +0 -0
- /package/dist/{verify-JFHQH55Z.js.map → verify-RACBFT2P.js.map} +0 -0
- /package/dist/{version-5B2TWXQJ.js.map → version-HJTVNPOO.js.map} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
STDIN_TIMEOUT_MS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B6WVNDA5.js";
|
|
5
5
|
|
|
6
6
|
// src/hooks/read-stdin.ts
|
|
7
7
|
function readStdin() {
|
|
@@ -18,4 +18,4 @@ function readStdin() {
|
|
|
18
18
|
export {
|
|
19
19
|
readStdin
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=chunk-
|
|
21
|
+
//# sourceMappingURL=chunk-ND4VK6C7.js.map
|
|
@@ -2,157 +2,38 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
|
|
|
2
2
|
import {
|
|
3
3
|
formatSporeBody,
|
|
4
4
|
sessionNoteId
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6LTNFMXO.js";
|
|
6
6
|
import {
|
|
7
|
-
ARTIFACT_TYPES,
|
|
8
7
|
indexNote
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JJL6AMDA.js";
|
|
9
|
+
import {
|
|
10
|
+
buildClassificationPrompt,
|
|
11
|
+
buildExtractionPrompt,
|
|
12
|
+
buildSummaryPrompt,
|
|
13
|
+
buildTitlePrompt,
|
|
14
|
+
extractJson,
|
|
15
|
+
stripReasoningTokens
|
|
16
|
+
} from "./chunk-KYL67SKZ.js";
|
|
17
|
+
import {
|
|
18
|
+
ARTIFACT_TYPES
|
|
19
|
+
} from "./chunk-2AMAOSRF.js";
|
|
10
20
|
import {
|
|
11
21
|
external_exports
|
|
12
22
|
} from "./chunk-6UJWI4IW.js";
|
|
13
23
|
import {
|
|
14
24
|
AgentRegistry
|
|
15
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-X6TKHO22.js";
|
|
16
26
|
import {
|
|
17
27
|
AI_RESPONSE_PREVIEW_CHARS,
|
|
18
|
-
CANDIDATE_CONTENT_PREVIEW,
|
|
19
28
|
CHARS_PER_TOKEN,
|
|
20
29
|
COMMAND_PREVIEW_CHARS,
|
|
30
|
+
LLM_REASONING_MODE,
|
|
21
31
|
PROMPT_PREVIEW_CHARS,
|
|
22
32
|
estimateTokens
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
|
|
25
|
-
// src/prompts/index.ts
|
|
26
|
-
import fs from "fs";
|
|
27
|
-
import path from "path";
|
|
28
|
-
import { fileURLToPath } from "url";
|
|
29
|
-
function resolvePromptsDir() {
|
|
30
|
-
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
31
|
-
for (let i = 0; i < 5; i++) {
|
|
32
|
-
if (fs.existsSync(path.join(dir, "package.json"))) {
|
|
33
|
-
return path.join(dir, "dist", "src", "prompts");
|
|
34
|
-
}
|
|
35
|
-
if (fs.existsSync(path.join(dir, "extraction.md"))) {
|
|
36
|
-
return dir;
|
|
37
|
-
}
|
|
38
|
-
dir = path.dirname(dir);
|
|
39
|
-
}
|
|
40
|
-
return path.dirname(fileURLToPath(import.meta.url));
|
|
41
|
-
}
|
|
42
|
-
var PROMPTS_DIR = resolvePromptsDir();
|
|
43
|
-
var promptCache = /* @__PURE__ */ new Map();
|
|
44
|
-
function loadPrompt(name) {
|
|
45
|
-
let cached = promptCache.get(name);
|
|
46
|
-
if (!cached) {
|
|
47
|
-
cached = fs.readFileSync(path.join(PROMPTS_DIR, `${name}.md`), "utf-8").trim();
|
|
48
|
-
promptCache.set(name, cached);
|
|
49
|
-
}
|
|
50
|
-
return cached;
|
|
51
|
-
}
|
|
52
|
-
function interpolate(template, vars) {
|
|
53
|
-
let result = template;
|
|
54
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
55
|
-
result = result.replaceAll(`{{${key}}}`, value);
|
|
56
|
-
}
|
|
57
|
-
return result;
|
|
58
|
-
}
|
|
59
|
-
function buildExtractionPrompt(sessionId, eventCount, toolSummary, maxTokens) {
|
|
60
|
-
return interpolate(loadPrompt("extraction"), {
|
|
61
|
-
sessionId,
|
|
62
|
-
eventCount: String(eventCount),
|
|
63
|
-
toolSummary,
|
|
64
|
-
maxTokens: String(maxTokens ?? 2048)
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
function buildSummaryPrompt(sessionId, user, content, maxTokens) {
|
|
68
|
-
return interpolate(loadPrompt("summary"), {
|
|
69
|
-
sessionId,
|
|
70
|
-
user,
|
|
71
|
-
content,
|
|
72
|
-
maxTokens: String(maxTokens ?? 1024)
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
function buildTitlePrompt(summary, sessionId) {
|
|
76
|
-
return interpolate(loadPrompt("title"), {
|
|
77
|
-
summary,
|
|
78
|
-
sessionId
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
var ARTIFACT_TYPE_DESCRIPTIONS = [
|
|
82
|
-
'"spec" \u2014 Design specifications, architecture documents',
|
|
83
|
-
'"plan" \u2014 Implementation plans, roadmaps',
|
|
84
|
-
'"rfc" \u2014 Requests for comment, proposals',
|
|
85
|
-
'"doc" \u2014 Documentation, guides, READMEs',
|
|
86
|
-
'"other" \u2014 Other substantive documents'
|
|
87
|
-
];
|
|
88
|
-
function buildSimilarityPrompt(currentSummary, candidateSummary) {
|
|
89
|
-
return interpolate(loadPrompt("session-similarity"), {
|
|
90
|
-
currentSummary,
|
|
91
|
-
candidateSummary
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
function buildClassificationPrompt(sessionId, candidates, maxTokens) {
|
|
95
|
-
const fileList = candidates.map((c) => {
|
|
96
|
-
const truncated = c.content.slice(0, CANDIDATE_CONTENT_PREVIEW);
|
|
97
|
-
return `### ${c.path}
|
|
98
|
-
\`\`\`
|
|
99
|
-
${truncated}
|
|
100
|
-
\`\`\``;
|
|
101
|
-
}).join("\n\n");
|
|
102
|
-
return interpolate(loadPrompt("classification"), {
|
|
103
|
-
sessionId,
|
|
104
|
-
fileList,
|
|
105
|
-
artifactTypes: ARTIFACT_TYPE_DESCRIPTIONS.map((d) => `- ${d}`).join("\n"),
|
|
106
|
-
validTypes: ARTIFACT_TYPES.join("|"),
|
|
107
|
-
maxTokens: String(maxTokens ?? 1024)
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// src/intelligence/response.ts
|
|
112
|
-
var REASONING_PATTERNS = [
|
|
113
|
-
// <think>...</think>answer (DeepSeek, Qwen, GLM, many others)
|
|
114
|
-
/<think>[\s\S]*?<\/think>\s*/gi,
|
|
115
|
-
// Implicit opening: reasoning...</think>answer (GLM-4.7 observed)
|
|
116
|
-
/^[\s\S]*?<\/think>\s*/i,
|
|
117
|
-
// <reasoning>...</reasoning>answer
|
|
118
|
-
/<reasoning>[\s\S]*?<\/reasoning>\s*/gi,
|
|
119
|
-
// <|thinking|>...<|/thinking|>answer
|
|
120
|
-
/<\|thinking\|>[\s\S]*?<\|\/thinking\|>\s*/gi,
|
|
121
|
-
// Plain-text "Thinking Process:" block followed by actual content
|
|
122
|
-
// (Qwen 3.5 via LM Studio without native thinking mode)
|
|
123
|
-
// Matches from "Thinking Process:" up to the last numbered step, then the synthesis follows
|
|
124
|
-
/^Thinking Process:[\s\S]*?(?=\n(?:## |# |\*\*[A-Z]))/i
|
|
125
|
-
];
|
|
126
|
-
function stripReasoningTokens(text) {
|
|
127
|
-
if (!text) return text;
|
|
128
|
-
for (const pattern of REASONING_PATTERNS) {
|
|
129
|
-
const stripped = text.replace(pattern, "").trim();
|
|
130
|
-
if (stripped && stripped !== text.trim()) {
|
|
131
|
-
return stripped;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return text;
|
|
135
|
-
}
|
|
136
|
-
function extractJson(text) {
|
|
137
|
-
const cleaned = stripReasoningTokens(text);
|
|
138
|
-
const fenceMatch = cleaned.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
139
|
-
if (fenceMatch) {
|
|
140
|
-
return JSON.parse(fenceMatch[1].trim());
|
|
141
|
-
}
|
|
142
|
-
const objectMatch = cleaned.match(/\{[\s\S]*\}/);
|
|
143
|
-
if (objectMatch) {
|
|
144
|
-
return JSON.parse(objectMatch[0]);
|
|
145
|
-
}
|
|
146
|
-
return JSON.parse(cleaned);
|
|
147
|
-
}
|
|
148
|
-
function extractNumber(text) {
|
|
149
|
-
const cleaned = stripReasoningTokens(text).trim();
|
|
150
|
-
const match = cleaned.match(/(\d+\.?\d*)/);
|
|
151
|
-
if (match) return parseFloat(match[1]);
|
|
152
|
-
return parseFloat(cleaned);
|
|
153
|
-
}
|
|
33
|
+
} from "./chunk-B6WVNDA5.js";
|
|
154
34
|
|
|
155
35
|
// src/daemon/processor.ts
|
|
36
|
+
var SUMMARIZATION_FAILED_MARKER = "summarization failed";
|
|
156
37
|
var ClassificationResponseSchema = external_exports.object({
|
|
157
38
|
artifacts: external_exports.array(external_exports.object({
|
|
158
39
|
source_path: external_exports.string(),
|
|
@@ -185,7 +66,7 @@ var BufferProcessor = class {
|
|
|
185
66
|
const rawPrompt = this.buildPromptForExtraction(events, sessionId);
|
|
186
67
|
const prompt = this.truncateForContext(rawPrompt, this.extractionMaxTokens);
|
|
187
68
|
try {
|
|
188
|
-
const response = await this.backend.summarize(prompt, { maxTokens: this.extractionMaxTokens });
|
|
69
|
+
const response = await this.backend.summarize(prompt, { maxTokens: this.extractionMaxTokens, reasoning: LLM_REASONING_MODE });
|
|
189
70
|
const parsed = extractJson(response.text);
|
|
190
71
|
return {
|
|
191
72
|
summary: parsed.summary,
|
|
@@ -209,15 +90,15 @@ var BufferProcessor = class {
|
|
|
209
90
|
const summaryPrompt = buildSummaryPrompt(sessionId, user ?? "unknown", truncatedContent, this.summaryMaxTokens);
|
|
210
91
|
let summaryText;
|
|
211
92
|
try {
|
|
212
|
-
const response = await this.backend.summarize(summaryPrompt, { maxTokens: this.summaryMaxTokens });
|
|
93
|
+
const response = await this.backend.summarize(summaryPrompt, { maxTokens: this.summaryMaxTokens, reasoning: LLM_REASONING_MODE });
|
|
213
94
|
summaryText = stripReasoningTokens(response.text);
|
|
214
95
|
} catch (error) {
|
|
215
|
-
summaryText = `Session ${sessionId} \u2014
|
|
96
|
+
summaryText = `Session ${sessionId} \u2014 ${SUMMARIZATION_FAILED_MARKER}: ${error.message}`;
|
|
216
97
|
}
|
|
217
98
|
const titlePrompt = buildTitlePrompt(summaryText, sessionId);
|
|
218
99
|
let title;
|
|
219
100
|
try {
|
|
220
|
-
const response = await this.backend.summarize(titlePrompt, { maxTokens: this.titleMaxTokens });
|
|
101
|
+
const response = await this.backend.summarize(titlePrompt, { maxTokens: this.titleMaxTokens, reasoning: LLM_REASONING_MODE });
|
|
221
102
|
title = stripReasoningTokens(response.text).trim();
|
|
222
103
|
} catch {
|
|
223
104
|
title = `Session ${sessionId}`;
|
|
@@ -227,7 +108,7 @@ var BufferProcessor = class {
|
|
|
227
108
|
async classifyArtifacts(candidates, sessionId) {
|
|
228
109
|
if (candidates.length === 0) return [];
|
|
229
110
|
const prompt = this.buildPromptForClassification(candidates, sessionId);
|
|
230
|
-
const response = await this.backend.summarize(prompt, { maxTokens: this.classificationMaxTokens });
|
|
111
|
+
const response = await this.backend.summarize(prompt, { maxTokens: this.classificationMaxTokens, reasoning: LLM_REASONING_MODE });
|
|
231
112
|
const raw = extractJson(response.text);
|
|
232
113
|
const parsed = ClassificationResponseSchema.parse(raw);
|
|
233
114
|
return parsed.artifacts;
|
|
@@ -364,13 +245,10 @@ function writeObservationNotes(observations, sessionId, writer, index, vaultDir)
|
|
|
364
245
|
}
|
|
365
246
|
|
|
366
247
|
export {
|
|
367
|
-
|
|
368
|
-
buildSimilarityPrompt,
|
|
369
|
-
stripReasoningTokens,
|
|
370
|
-
extractNumber,
|
|
248
|
+
SUMMARIZATION_FAILED_MARKER,
|
|
371
249
|
BufferProcessor,
|
|
372
250
|
TranscriptMiner,
|
|
373
251
|
extractTurnsFromBuffer,
|
|
374
252
|
writeObservationNotes
|
|
375
253
|
};
|
|
376
|
-
//# sourceMappingURL=chunk-
|
|
254
|
+
//# sourceMappingURL=chunk-R6LQT3U7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/processor.ts","../src/capture/transcript-miner.ts","../src/vault/observations.ts"],"sourcesContent":["import { z } from 'zod';\nimport type { LlmProvider } from '../intelligence/llm.js';\nimport { ARTIFACT_TYPES } from '../vault/types.js';\nimport { estimateTokens, CHARS_PER_TOKEN, PROMPT_PREVIEW_CHARS, AI_RESPONSE_PREVIEW_CHARS, COMMAND_PREVIEW_CHARS, LLM_REASONING_MODE } from '../constants.js';\nimport type { MycoConfig } from '../config/schema.js';\nimport type { ObservationType, ArtifactType } from '../vault/types.js';\nimport { buildExtractionPrompt, buildSummaryPrompt, buildTitlePrompt, buildClassificationPrompt } from '../prompts/index.js';\nimport { extractJson, stripReasoningTokens } from '../intelligence/response.js';\n\n/** Marker substring in failed summary text. Used by reprocess --failed to detect failures. */\nexport const SUMMARIZATION_FAILED_MARKER = 'summarization failed';\n\nexport interface Observation {\n type: ObservationType;\n title: string;\n content: string;\n tags: string[];\n root_cause?: string;\n fix?: string;\n rationale?: string;\n alternatives_rejected?: string;\n gained?: string;\n sacrificed?: string;\n}\n\nexport interface ProcessorResult {\n summary: string;\n observations: Observation[];\n degraded: boolean;\n}\n\nexport interface ClassifiedArtifact {\n source_path: string;\n artifact_type: ArtifactType;\n title: string;\n tags: string[];\n}\n\nconst ClassificationResponseSchema = z.object({\n artifacts: z.array(z.object({\n source_path: z.string(),\n artifact_type: z.enum(ARTIFACT_TYPES),\n title: z.string(),\n tags: z.array(z.string()).default([]),\n })).default([]),\n});\n\nexport class BufferProcessor {\n private extractionMaxTokens: number;\n private summaryMaxTokens: number;\n private titleMaxTokens: number;\n private classificationMaxTokens: number;\n\n constructor(private backend: LlmProvider, private contextWindow: number = 8192, captureConfig?: MycoConfig['capture']) {\n this.extractionMaxTokens = captureConfig?.extraction_max_tokens ?? 2048;\n this.summaryMaxTokens = captureConfig?.summary_max_tokens ?? 512;\n this.titleMaxTokens = captureConfig?.title_max_tokens ?? 32;\n this.classificationMaxTokens = captureConfig?.classification_max_tokens ?? 1024;\n }\n\n private truncateForContext(data: string, maxTokens: number): string {\n const available = this.contextWindow - maxTokens;\n const dataTokens = estimateTokens(data);\n if (dataTokens <= available) return data;\n const charBudget = available * CHARS_PER_TOKEN;\n return data.slice(0, charBudget);\n }\n\n async process(\n events: Array<Record<string, unknown>>,\n sessionId: string,\n ): Promise<ProcessorResult> {\n const rawPrompt = this.buildPromptForExtraction(events, sessionId);\n const prompt = this.truncateForContext(rawPrompt, this.extractionMaxTokens);\n\n try {\n const response = await this.backend.summarize(prompt, { maxTokens: this.extractionMaxTokens, reasoning: LLM_REASONING_MODE });\n const parsed = extractJson(response.text) as {\n summary: string;\n observations: Observation[];\n };\n\n return {\n summary: parsed.summary,\n observations: parsed.observations ?? [],\n degraded: false,\n };\n } catch (error) {\n return {\n summary: `LLM processing failed for session ${sessionId}. ${events.length} events captured. Error: ${(error as Error).message}`,\n observations: [],\n degraded: true,\n };\n }\n }\n\n private buildPromptForExtraction(\n events: Array<Record<string, unknown>>,\n sessionId: string,\n ): string {\n const toolSummary = this.summarizeEvents(events);\n return buildExtractionPrompt(sessionId, events.length, toolSummary, this.extractionMaxTokens);\n }\n\n async summarizeSession(\n conversationMarkdown: string,\n sessionId: string,\n user?: string,\n ): Promise<{ summary: string; title: string }> {\n const truncatedContent = this.truncateForContext(conversationMarkdown, this.summaryMaxTokens);\n const summaryPrompt = buildSummaryPrompt(sessionId, user ?? 'unknown', truncatedContent, this.summaryMaxTokens);\n\n let summaryText: string;\n try {\n const response = await this.backend.summarize(summaryPrompt, { maxTokens: this.summaryMaxTokens, reasoning: LLM_REASONING_MODE });\n summaryText = stripReasoningTokens(response.text);\n } catch (error) {\n summaryText = `Session ${sessionId} — ${SUMMARIZATION_FAILED_MARKER}: ${(error as Error).message}`;\n }\n\n const titlePrompt = buildTitlePrompt(summaryText, sessionId);\n let title: string;\n try {\n const response = await this.backend.summarize(titlePrompt, { maxTokens: this.titleMaxTokens, reasoning: LLM_REASONING_MODE });\n title = stripReasoningTokens(response.text).trim();\n } catch {\n title = `Session ${sessionId}`;\n }\n\n return { summary: summaryText, title };\n }\n\n async classifyArtifacts(\n candidates: Array<{ path: string; content: string }>,\n sessionId: string,\n ): Promise<ClassifiedArtifact[]> {\n if (candidates.length === 0) return [];\n\n const prompt = this.buildPromptForClassification(candidates, sessionId);\n const response = await this.backend.summarize(prompt, { maxTokens: this.classificationMaxTokens, reasoning: LLM_REASONING_MODE });\n const raw = extractJson(response.text);\n const parsed = ClassificationResponseSchema.parse(raw);\n return parsed.artifacts;\n }\n\n private buildPromptForClassification(\n candidates: Array<{ path: string; content: string }>,\n sessionId: string,\n ): string {\n return buildClassificationPrompt(sessionId, candidates, this.classificationMaxTokens);\n }\n\n private summarizeEvents(events: Array<Record<string, unknown>>): string {\n const toolCounts = new Map<string, number>();\n const filesAccessed = new Set<string>();\n const prompts: string[] = [];\n const aiResponses: string[] = [];\n\n for (const event of events) {\n if (event.type === 'user_prompt') {\n const prompt = String(event.prompt ?? '');\n if (prompt) prompts.push(prompt.slice(0, PROMPT_PREVIEW_CHARS));\n continue;\n }\n\n if (event.type === 'ai_response') {\n const content = String(event.content ?? '');\n if (content) aiResponses.push(content.slice(0, AI_RESPONSE_PREVIEW_CHARS));\n continue;\n }\n\n // Hooks send tool_name/tool_input; also support legacy tool/input\n const tool = String(event.tool_name ?? event.tool ?? 'unknown');\n toolCounts.set(tool, (toolCounts.get(tool) ?? 0) + 1);\n\n const input = (event.tool_input ?? event.input) as Record<string, unknown> | undefined;\n if (input?.path) filesAccessed.add(String(input.path));\n if (input?.file_path) filesAccessed.add(String(input.file_path));\n if (input?.command) filesAccessed.add(`[cmd] ${String(input.command).slice(0, COMMAND_PREVIEW_CHARS)}`);\n }\n\n const lines: string[] = [];\n\n if (prompts.length > 0) {\n lines.push('### User Prompts');\n for (const p of prompts) {\n lines.push(`- \"${p}\"`);\n }\n }\n\n lines.push('\\n### Tool Usage');\n for (const [tool, count] of toolCounts) {\n lines.push(`- ${tool}: ${count} calls`);\n }\n\n if (filesAccessed.size > 0) {\n lines.push('\\n### Files Accessed');\n for (const file of filesAccessed) {\n lines.push(`- ${file}`);\n }\n }\n\n if (aiResponses.length > 0) {\n lines.push('\\n### AI Responses');\n for (const r of aiResponses) {\n lines.push(`- \"${r}\"`);\n }\n }\n\n return lines.join('\\n');\n }\n}\n","import { AgentRegistry } from '../agents/registry.js';\nimport type { AgentAdapter } from '../agents/adapter.js';\nimport { PROMPT_PREVIEW_CHARS } from '../constants.js';\nimport fs from 'node:fs';\n\n// Re-export TranscriptTurn from its canonical home in agents/adapter.ts\nexport type { TranscriptTurn } from '../agents/adapter.js';\nimport type { TranscriptTurn } from '../agents/adapter.js';\n\ninterface TranscriptConfig {\n /** Additional agent adapters to register (useful for testing or custom agents) */\n additionalAdapters?: AgentAdapter[];\n}\n\nexport class TranscriptMiner {\n private registry: AgentRegistry;\n\n constructor(config?: TranscriptConfig) {\n this.registry = new AgentRegistry(config?.additionalAdapters);\n }\n\n /**\n * Extract all conversation turns for a session.\n * Convenience wrapper — delegates to getAllTurnsWithSource.\n */\n getAllTurns(sessionId: string): TranscriptTurn[] {\n return this.getAllTurnsWithSource(sessionId).turns;\n }\n\n /**\n * Extract turns using the hook-provided transcript path first (fast, no scanning),\n * then fall back to adapter registry scanning if the path isn't provided.\n */\n getAllTurnsWithSource(sessionId: string, transcriptPath?: string): { turns: TranscriptTurn[]; source: string } {\n // Primary: use the path provided by the hook (no directory scanning needed)\n if (transcriptPath) {\n const result = this.registry.parseTurnsFromPath(transcriptPath);\n if (result) return result;\n }\n\n // Fallback: scan known agent directories\n const result = this.registry.getTranscriptTurns(sessionId);\n if (result) return result;\n return { turns: [], source: 'none' };\n }\n}\n\n/**\n * Build turns from buffer events — the fallback when no agent transcript is available.\n * Buffer events come from hooks (user_prompt, tool_use) and lack AI responses.\n * Turns will have prompts and tool counts but no aiResponse.\n */\nexport function extractTurnsFromBuffer(events: Array<Record<string, unknown>>): TranscriptTurn[] {\n const turns: TranscriptTurn[] = [];\n let current: TranscriptTurn | null = null;\n\n for (const event of events) {\n const type = event.type as string;\n if (type === 'user_prompt') {\n if (current) turns.push(current);\n current = {\n prompt: String(event.prompt ?? '').slice(0, PROMPT_PREVIEW_CHARS),\n toolCount: 0,\n timestamp: String(event.timestamp ?? new Date().toISOString()),\n };\n } else if (type === 'tool_use') {\n if (current) current.toolCount++;\n }\n }\n if (current) turns.push(current);\n return turns;\n}\n","import { formatSporeBody } from '../obsidian/formatter.js';\nimport { sessionNoteId } from './session-id.js';\nimport { indexNote } from '../index/rebuild.js';\nimport type { Observation } from '../daemon/processor.js';\nimport type { VaultWriter } from './writer.js';\nimport type { MycoIndex } from '../index/sqlite.js';\n\nexport interface WrittenNote {\n id: string;\n path: string;\n observation: Observation;\n}\n\nexport function writeObservationNotes(\n observations: Observation[],\n sessionId: string,\n writer: VaultWriter,\n index: MycoIndex,\n vaultDir: string,\n): WrittenNote[] {\n const results: WrittenNote[] = [];\n\n for (const obs of observations) {\n const obsId = `${obs.type}-${sessionId.slice(-6)}-${Date.now()}`;\n const body = formatSporeBody({\n title: obs.title,\n observationType: obs.type,\n content: obs.content,\n sessionId,\n root_cause: obs.root_cause,\n fix: obs.fix,\n rationale: obs.rationale,\n alternatives_rejected: obs.alternatives_rejected,\n gained: obs.gained,\n sacrificed: obs.sacrificed,\n tags: obs.tags,\n });\n const relativePath = writer.writeSpore({\n id: obsId,\n observation_type: obs.type,\n session: sessionNoteId(sessionId),\n tags: obs.tags,\n content: body,\n });\n indexNote(index, vaultDir, relativePath);\n results.push({ id: obsId, path: relativePath, observation: obs });\n }\n\n return results;\n}\n"],"mappings":"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","names":["result"]}
|
|
@@ -2,10 +2,10 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
|
|
|
2
2
|
import {
|
|
3
3
|
LmStudioBackend,
|
|
4
4
|
OllamaBackend
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-JI6M2L2W.js";
|
|
6
6
|
import {
|
|
7
7
|
LLM_REQUEST_TIMEOUT_MS
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-B6WVNDA5.js";
|
|
9
9
|
|
|
10
10
|
// node_modules/@anthropic-ai/sdk/internal/tslib.mjs
|
|
11
11
|
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
@@ -4911,4 +4911,4 @@ export {
|
|
|
4911
4911
|
createLlmProvider,
|
|
4912
4912
|
createEmbeddingProvider
|
|
4913
4913
|
};
|
|
4914
|
-
//# sourceMappingURL=chunk-
|
|
4914
|
+
//# sourceMappingURL=chunk-RCV2I4AI.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
PROMPT_PREVIEW_CHARS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B6WVNDA5.js";
|
|
5
5
|
|
|
6
6
|
// src/agents/adapter.ts
|
|
7
7
|
import fs from "fs";
|
|
@@ -351,4 +351,4 @@ export {
|
|
|
351
351
|
claudeCodeAdapter,
|
|
352
352
|
AgentRegistry
|
|
353
353
|
};
|
|
354
|
-
//# sourceMappingURL=chunk-
|
|
354
|
+
//# sourceMappingURL=chunk-X6TKHO22.js.map
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
getPluginVersion
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7KQB22DP.js";
|
|
5
5
|
import {
|
|
6
6
|
AgentRegistry
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-X6TKHO22.js";
|
|
8
8
|
import {
|
|
9
9
|
DAEMON_CLIENT_TIMEOUT_MS,
|
|
10
10
|
DAEMON_HEALTH_CHECK_TIMEOUT_MS,
|
|
11
|
-
DAEMON_HEALTH_RETRY_DELAYS
|
|
12
|
-
|
|
11
|
+
DAEMON_HEALTH_RETRY_DELAYS,
|
|
12
|
+
DAEMON_STALE_GRACE_PERIOD_MS
|
|
13
|
+
} from "./chunk-B6WVNDA5.js";
|
|
13
14
|
|
|
14
15
|
// src/hooks/client.ts
|
|
15
16
|
import fs from "fs";
|
|
@@ -68,9 +69,16 @@ var DaemonClient = class {
|
|
|
68
69
|
/**
|
|
69
70
|
* Check if the daemon is running a stale version.
|
|
70
71
|
* Returns true if the daemon's version doesn't match the current plugin version.
|
|
72
|
+
* Skips the check if daemon.json was written recently (grace period) to prevent
|
|
73
|
+
* rapid restart loops from concurrent hooks or session reloads.
|
|
71
74
|
*/
|
|
72
75
|
async isStale() {
|
|
73
76
|
try {
|
|
77
|
+
const jsonPath = path.join(this.vaultDir, "daemon.json");
|
|
78
|
+
const stat = fs.statSync(jsonPath);
|
|
79
|
+
if (Date.now() - stat.mtimeMs < DAEMON_STALE_GRACE_PERIOD_MS) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
74
82
|
const info = this.readDaemonJson();
|
|
75
83
|
if (!info) return false;
|
|
76
84
|
const res = await fetch(`http://127.0.0.1:${info.port}/health`, {
|
|
@@ -101,11 +109,15 @@ var DaemonClient = class {
|
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
111
|
/**
|
|
104
|
-
* Ensure the daemon is running
|
|
105
|
-
*
|
|
112
|
+
* Ensure the daemon is running. Spawns it if unhealthy.
|
|
113
|
+
* When checkStale is true (default), also restarts a healthy daemon if its
|
|
114
|
+
* version doesn't match the current plugin version. Use checkStale: false
|
|
115
|
+
* for hooks that just need the daemon alive (e.g., stop) without triggering
|
|
116
|
+
* version-driven restarts.
|
|
106
117
|
*/
|
|
107
|
-
async ensureRunning() {
|
|
108
|
-
|
|
118
|
+
async ensureRunning(opts) {
|
|
119
|
+
const checkStale = opts?.checkStale ?? true;
|
|
120
|
+
if (checkStale && await this.isStale()) {
|
|
109
121
|
this.killDaemon();
|
|
110
122
|
await new Promise((r) => setTimeout(r, 200));
|
|
111
123
|
} else if (await this.isHealthy()) {
|
|
@@ -166,4 +178,4 @@ var DaemonClient = class {
|
|
|
166
178
|
export {
|
|
167
179
|
DaemonClient
|
|
168
180
|
};
|
|
169
|
-
//# sourceMappingURL=chunk-
|
|
181
|
+
//# sourceMappingURL=chunk-ZWUFTOG3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/client.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { DAEMON_CLIENT_TIMEOUT_MS, DAEMON_HEALTH_CHECK_TIMEOUT_MS, DAEMON_HEALTH_RETRY_DELAYS, DAEMON_STALE_GRACE_PERIOD_MS } from '../constants.js';\nimport { AgentRegistry } from '../agents/registry.js';\nimport { getPluginVersion } from '../version.js';\n\ninterface DaemonInfo {\n pid: number;\n port: number;\n}\n\ninterface HealthResponse {\n myco: boolean;\n version?: string;\n}\n\ninterface ClientResult {\n ok: boolean;\n data?: any;\n}\n\nexport class DaemonClient {\n private vaultDir: string;\n\n constructor(vaultDir: string) {\n this.vaultDir = vaultDir;\n }\n\n async post(endpoint: string, body: unknown): Promise<ClientResult> {\n try {\n const info = this.readDaemonJson();\n if (!info) return { ok: false };\n\n const res = await fetch(`http://127.0.0.1:${info.port}${endpoint}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n\n if (!res.ok) return { ok: false };\n const data = await res.json();\n return { ok: true, data };\n } catch {\n return { ok: false };\n }\n }\n\n async get(endpoint: string): Promise<ClientResult> {\n try {\n const info = this.readDaemonJson();\n if (!info) return { ok: false };\n\n const res = await fetch(`http://127.0.0.1:${info.port}${endpoint}`, {\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n\n if (!res.ok) return { ok: false };\n const data = await res.json();\n return { ok: true, data };\n } catch {\n return { ok: false };\n }\n }\n\n async isHealthy(): Promise<boolean> {\n try {\n const info = this.readDaemonJson();\n if (!info) return false;\n\n const res = await fetch(`http://127.0.0.1:${info.port}/health`, {\n signal: AbortSignal.timeout(DAEMON_HEALTH_CHECK_TIMEOUT_MS),\n });\n if (!res.ok) return false;\n const data = await res.json() as HealthResponse;\n return data.myco === true;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if the daemon is running a stale version.\n * Returns true if the daemon's version doesn't match the current plugin version.\n * Skips the check if daemon.json was written recently (grace period) to prevent\n * rapid restart loops from concurrent hooks or session reloads.\n */\n private async isStale(): Promise<boolean> {\n try {\n const jsonPath = path.join(this.vaultDir, 'daemon.json');\n const stat = fs.statSync(jsonPath);\n if (Date.now() - stat.mtimeMs < DAEMON_STALE_GRACE_PERIOD_MS) {\n return false;\n }\n\n const info = this.readDaemonJson();\n if (!info) return false;\n\n const res = await fetch(`http://127.0.0.1:${info.port}/health`, {\n signal: AbortSignal.timeout(DAEMON_HEALTH_CHECK_TIMEOUT_MS),\n });\n if (!res.ok) return false;\n const data = await res.json() as HealthResponse;\n if (!data.myco) return false;\n\n // No version in response = old daemon that predates this check\n if (!data.version) return true;\n\n return data.version !== getPluginVersion();\n } catch {\n return false;\n }\n }\n\n /**\n * Kill the running daemon process.\n */\n private killDaemon(): void {\n try {\n const info = this.readDaemonJson();\n if (!info) return;\n process.kill(info.pid, 'SIGTERM');\n } catch { /* already dead */ }\n try {\n fs.unlinkSync(path.join(this.vaultDir, 'daemon.json'));\n } catch { /* already gone */ }\n }\n\n /**\n * Ensure the daemon is running. Spawns it if unhealthy.\n * When checkStale is true (default), also restarts a healthy daemon if its\n * version doesn't match the current plugin version. Use checkStale: false\n * for hooks that just need the daemon alive (e.g., stop) without triggering\n * version-driven restarts.\n */\n async ensureRunning(opts?: { checkStale?: boolean }): Promise<boolean> {\n const checkStale = opts?.checkStale ?? true;\n\n if (checkStale && await this.isStale()) {\n this.killDaemon();\n // Brief pause for port release\n await new Promise((r) => setTimeout(r, 200));\n } else if (await this.isHealthy()) {\n return true;\n }\n\n this.spawnDaemon();\n\n for (const delay of DAEMON_HEALTH_RETRY_DELAYS) {\n await new Promise((r) => setTimeout(r, delay));\n if (await this.isHealthy()) return true;\n }\n return false;\n }\n\n spawnDaemon(): void {\n const daemonScript = this.resolveDaemonScript();\n if (!daemonScript || !fs.existsSync(daemonScript)) return;\n\n const child = spawn('node', [daemonScript, '--vault', this.vaultDir], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n }\n\n /**\n * Resolve the daemon entry script path.\n * Priority:\n * 1. Plugin root env var (set by the agent host) → dist/src/daemon/main.js\n * 2. Walk up from the current file to find the dist/ directory containing\n * the daemon entry. This handles both chunk files (dist/chunk-*.js) and\n * thin entry points (dist/src/hooks/*.js) after bundling.\n */\n private resolveDaemonScript(): string | undefined {\n const pluginRoot = new AgentRegistry().resolvePluginRoot();\n if (pluginRoot) {\n return path.join(pluginRoot, 'dist', 'src', 'daemon', 'main.js');\n }\n\n // Walk up from import.meta.dirname looking for the daemon entry\n let dir = import.meta.dirname;\n for (let i = 0; i < 5; i++) {\n const candidate = path.join(dir, 'dist', 'src', 'daemon', 'main.js');\n if (fs.existsSync(candidate)) return candidate;\n // Also check if we're already inside dist/\n const inDist = path.join(dir, 'src', 'daemon', 'main.js');\n if (fs.existsSync(inDist)) return inDist;\n dir = path.dirname(dir);\n }\n return undefined;\n }\n\n private readDaemonJson(): DaemonInfo | null {\n try {\n const jsonPath = path.join(this.vaultDir, 'daemon.json');\n const content = fs.readFileSync(jsonPath, 'utf-8');\n const info = JSON.parse(content);\n if (typeof info.port !== 'number') return null;\n return info as DaemonInfo;\n } catch {\n return null;\n }\n }\n}\n"],"mappings":"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","names":[]}
|