@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.
Files changed (96) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +5 -1
  4. package/dist/chunk-2AMAOSRF.js +105 -0
  5. package/dist/chunk-2AMAOSRF.js.map +1 -0
  6. package/dist/{chunk-I7PNZEBO.js → chunk-6LTNFMXO.js} +12 -1
  7. package/dist/{chunk-I7PNZEBO.js.map → chunk-6LTNFMXO.js.map} +1 -1
  8. package/dist/{chunk-2GJFTIWX.js → chunk-7KQB22DP.js} +2 -2
  9. package/dist/{chunk-JBD5KP5G.js → chunk-B6WVNDA5.js} +14 -2
  10. package/dist/chunk-B6WVNDA5.js.map +1 -0
  11. package/dist/chunk-FIA5NTRH.js +159 -0
  12. package/dist/chunk-FIA5NTRH.js.map +1 -0
  13. package/dist/{chunk-GFBG73P4.js → chunk-FIRMTYFH.js} +3 -3
  14. package/dist/{chunk-XCPQHC4X.js → chunk-HJG7Z6SJ.js} +2 -2
  15. package/dist/chunk-HL2S5QZG.js +385 -0
  16. package/dist/chunk-HL2S5QZG.js.map +1 -0
  17. package/dist/{chunk-WBT5DWGC.js → chunk-IURC35BF.js} +2 -2
  18. package/dist/{chunk-67R6EMYD.js → chunk-JI6M2L2W.js} +31 -52
  19. package/dist/chunk-JI6M2L2W.js.map +1 -0
  20. package/dist/{chunk-FPEDTLQ6.js → chunk-JJL6AMDA.js} +3 -101
  21. package/dist/chunk-JJL6AMDA.js.map +1 -0
  22. package/dist/chunk-KYL67SKZ.js +150 -0
  23. package/dist/chunk-KYL67SKZ.js.map +1 -0
  24. package/dist/{chunk-ZCBL5HER.js → chunk-ND4VK6C7.js} +2 -2
  25. package/dist/{chunk-V2OWD2VV.js → chunk-R6LQT3U7.js} +24 -146
  26. package/dist/chunk-R6LQT3U7.js.map +1 -0
  27. package/dist/{chunk-IYFKPSRP.js → chunk-RCV2I4AI.js} +3 -3
  28. package/dist/{chunk-BNIYWCST.js → chunk-X6TKHO22.js} +2 -2
  29. package/dist/{chunk-OUFSLZTX.js → chunk-ZWUFTOG3.js} +21 -9
  30. package/dist/chunk-ZWUFTOG3.js.map +1 -0
  31. package/dist/{cli-PMOFCZQL.js → cli-BLYNNKGJ.js} +24 -18
  32. package/dist/cli-BLYNNKGJ.js.map +1 -0
  33. package/dist/{client-5SUO2UYH.js → client-5GB4WVXE.js} +5 -5
  34. package/dist/curate-S4HOYWXA.js +231 -0
  35. package/dist/curate-S4HOYWXA.js.map +1 -0
  36. package/dist/{detect-providers-IRL2TTLK.js → detect-providers-BIHYFK5M.js} +3 -3
  37. package/dist/digest-7NKYXM6G.js +96 -0
  38. package/dist/digest-7NKYXM6G.js.map +1 -0
  39. package/dist/{init-NUF5UBUJ.js → init-HPQ77WWF.js} +5 -5
  40. package/dist/{main-2XEBVUR6.js → main-NFQ4II75.js} +253 -576
  41. package/dist/main-NFQ4II75.js.map +1 -0
  42. package/dist/{rebuild-E6YFIRYZ.js → rebuild-KQ6G2GZM.js} +8 -7
  43. package/dist/{rebuild-E6YFIRYZ.js.map → rebuild-KQ6G2GZM.js.map} +1 -1
  44. package/dist/{reprocess-7G7KQWCN.js → reprocess-ZL4HKTSC.js} +95 -24
  45. package/dist/reprocess-ZL4HKTSC.js.map +1 -0
  46. package/dist/{restart-ABW4ZK3P.js → restart-FYW662DR.js} +6 -6
  47. package/dist/{search-MPD7SFK6.js → search-E5JQMTXV.js} +6 -6
  48. package/dist/{server-NZLZRITH.js → server-TV3D35HZ.js} +38 -15
  49. package/dist/{server-NZLZRITH.js.map → server-TV3D35HZ.js.map} +1 -1
  50. package/dist/{session-start-YB4A4PZB.js → session-start-5MFEOVQ5.js} +6 -6
  51. package/dist/{setup-digest-K732MGOJ.js → setup-digest-DZAFIBEF.js} +5 -5
  52. package/dist/{setup-llm-XCCH5LYD.js → setup-llm-4BZM33YT.js} +5 -5
  53. package/dist/src/cli.js +4 -4
  54. package/dist/src/daemon/main.js +4 -4
  55. package/dist/src/hooks/post-tool-use.js +5 -5
  56. package/dist/src/hooks/session-end.js +5 -5
  57. package/dist/src/hooks/session-start.js +4 -4
  58. package/dist/src/hooks/stop.js +6 -6
  59. package/dist/src/hooks/stop.js.map +1 -1
  60. package/dist/src/hooks/user-prompt-submit.js +5 -5
  61. package/dist/src/mcp/server.js +4 -4
  62. package/dist/src/prompts/extraction.md +1 -1
  63. package/dist/src/prompts/summary.md +1 -11
  64. package/dist/src/prompts/supersession.md +32 -0
  65. package/dist/{stats-6G7SN5YZ.js → stats-ZIIJ2GB3.js} +5 -5
  66. package/dist/{verify-JFHQH55Z.js → verify-RACBFT2P.js} +4 -4
  67. package/dist/{version-5B2TWXQJ.js → version-HJTVNPOO.js} +4 -4
  68. package/package.json +1 -1
  69. package/skills/setup/SKILL.md +56 -28
  70. package/skills/setup/references/model-recommendations.md +49 -43
  71. package/dist/chunk-67R6EMYD.js.map +0 -1
  72. package/dist/chunk-FPEDTLQ6.js.map +0 -1
  73. package/dist/chunk-JBD5KP5G.js.map +0 -1
  74. package/dist/chunk-OUFSLZTX.js.map +0 -1
  75. package/dist/chunk-V2OWD2VV.js.map +0 -1
  76. package/dist/cli-PMOFCZQL.js.map +0 -1
  77. package/dist/main-2XEBVUR6.js.map +0 -1
  78. package/dist/reprocess-7G7KQWCN.js.map +0 -1
  79. /package/dist/{chunk-2GJFTIWX.js.map → chunk-7KQB22DP.js.map} +0 -0
  80. /package/dist/{chunk-GFBG73P4.js.map → chunk-FIRMTYFH.js.map} +0 -0
  81. /package/dist/{chunk-XCPQHC4X.js.map → chunk-HJG7Z6SJ.js.map} +0 -0
  82. /package/dist/{chunk-WBT5DWGC.js.map → chunk-IURC35BF.js.map} +0 -0
  83. /package/dist/{chunk-ZCBL5HER.js.map → chunk-ND4VK6C7.js.map} +0 -0
  84. /package/dist/{chunk-IYFKPSRP.js.map → chunk-RCV2I4AI.js.map} +0 -0
  85. /package/dist/{chunk-BNIYWCST.js.map → chunk-X6TKHO22.js.map} +0 -0
  86. /package/dist/{client-5SUO2UYH.js.map → client-5GB4WVXE.js.map} +0 -0
  87. /package/dist/{detect-providers-IRL2TTLK.js.map → detect-providers-BIHYFK5M.js.map} +0 -0
  88. /package/dist/{init-NUF5UBUJ.js.map → init-HPQ77WWF.js.map} +0 -0
  89. /package/dist/{restart-ABW4ZK3P.js.map → restart-FYW662DR.js.map} +0 -0
  90. /package/dist/{search-MPD7SFK6.js.map → search-E5JQMTXV.js.map} +0 -0
  91. /package/dist/{session-start-YB4A4PZB.js.map → session-start-5MFEOVQ5.js.map} +0 -0
  92. /package/dist/{setup-digest-K732MGOJ.js.map → setup-digest-DZAFIBEF.js.map} +0 -0
  93. /package/dist/{setup-llm-XCCH5LYD.js.map → setup-llm-4BZM33YT.js.map} +0 -0
  94. /package/dist/{stats-6G7SN5YZ.js.map → stats-ZIIJ2GB3.js.map} +0 -0
  95. /package/dist/{verify-JFHQH55Z.js.map → verify-RACBFT2P.js.map} +0 -0
  96. /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-JBD5KP5G.js";
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-ZCBL5HER.js.map
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-I7PNZEBO.js";
5
+ } from "./chunk-6LTNFMXO.js";
6
6
  import {
7
- ARTIFACT_TYPES,
8
7
  indexNote
9
- } from "./chunk-FPEDTLQ6.js";
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-BNIYWCST.js";
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-JBD5KP5G.js";
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 summarization failed: ${error.message}`;
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
- loadPrompt,
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-V2OWD2VV.js.map
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-67R6EMYD.js";
5
+ } from "./chunk-JI6M2L2W.js";
6
6
  import {
7
7
  LLM_REQUEST_TIMEOUT_MS
8
- } from "./chunk-JBD5KP5G.js";
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-IYFKPSRP.js.map
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-JBD5KP5G.js";
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-BNIYWCST.js.map
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-2GJFTIWX.js";
4
+ } from "./chunk-7KQB22DP.js";
5
5
  import {
6
6
  AgentRegistry
7
- } from "./chunk-BNIYWCST.js";
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
- } from "./chunk-JBD5KP5G.js";
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 the current version. Spawns it if unhealthy
105
- * or restarts it if the version is stale. Returns true if healthy after this call.
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
- if (await this.isStale()) {
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-OUFSLZTX.js.map
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":[]}