@goondocks/myco 0.6.0 → 0.6.2
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 +10 -11
- package/dist/{chunk-2YBUL3IL.js → chunk-25DJSF2K.js} +3 -3
- package/dist/{chunk-24DOZEUJ.js → chunk-ALBVNGCF.js} +591 -27
- package/dist/chunk-ALBVNGCF.js.map +1 -0
- package/dist/{chunk-E7OBRBCQ.js → chunk-CK24O5YQ.js} +12 -2
- package/dist/chunk-CK24O5YQ.js.map +1 -0
- package/dist/{chunk-2GSX3BK2.js → chunk-CPVXNRGW.js} +4 -4
- package/dist/{chunk-L25U7PIG.js → chunk-CQ4RKK67.js} +2 -2
- package/dist/{chunk-ZMYNRTTD.js → chunk-HRGHDMYI.js} +4 -3
- package/dist/chunk-HRGHDMYI.js.map +1 -0
- package/dist/{chunk-5FNZ7AMX.js → chunk-IWBWZQK6.js} +2 -2
- package/dist/{chunk-MQSYSQ6T.js → chunk-JSK7L46L.js} +11 -6
- package/dist/{chunk-MQSYSQ6T.js.map → chunk-JSK7L46L.js.map} +1 -1
- package/dist/{chunk-KUMVJIJW.js → chunk-LDKXXKF6.js} +6 -10
- package/dist/{chunk-KUMVJIJW.js.map → chunk-LDKXXKF6.js.map} +1 -1
- package/dist/{chunk-QGJ2ZIUZ.js → chunk-MWW62YZP.js} +37 -5
- package/dist/chunk-MWW62YZP.js.map +1 -0
- package/dist/{chunk-2ZBB3MQT.js → chunk-PQWQC3RF.js} +444 -21
- package/dist/chunk-PQWQC3RF.js.map +1 -0
- package/dist/{chunk-5QWZT4AB.js → chunk-RNWALAFP.js} +2 -2
- package/dist/{chunk-3EM23DMD.js → chunk-RXJHB7W4.js} +2 -2
- package/dist/{chunk-GNR3QAER.js → chunk-RY76WEN3.js} +2 -2
- package/dist/{chunk-GDYYJTTT.js → chunk-V5R6O6RP.js} +3 -3
- package/dist/{chunk-6BSDCZ5Q.js → chunk-WBLTISAK.js} +8 -3
- package/dist/chunk-WBLTISAK.js.map +1 -0
- package/dist/{chunk-YTANWAGE.js → chunk-XNAM6Z4O.js} +2 -2
- package/dist/{chunk-P3WO3N3I.js → chunk-YG6MLLGL.js} +19 -3
- package/dist/{chunk-P3WO3N3I.js.map → chunk-YG6MLLGL.js.map} +1 -1
- package/dist/{cli-K7SUTP7A.js → cli-LMBBPV2D.js} +20 -20
- package/dist/{client-YJMNTITQ.js → client-FDKJ4BY7.js} +5 -5
- package/dist/{config-G5GGT5A6.js → config-HDUFDOQN.js} +3 -3
- package/dist/{curate-6T5NKVXK.js → curate-DYE4VCBJ.js} +10 -11
- package/dist/{curate-6T5NKVXK.js.map → curate-DYE4VCBJ.js.map} +1 -1
- package/dist/{detect-providers-S3M5TAMW.js → detect-providers-I2QQFDJW.js} +3 -3
- package/dist/{digest-O35VHYFP.js → digest-PNHFM7JJ.js} +11 -13
- package/dist/{digest-O35VHYFP.js.map → digest-PNHFM7JJ.js.map} +1 -1
- package/dist/{init-TFLSATB3.js → init-7N7F6W6U.js} +8 -8
- package/dist/{main-JEUQS3BY.js → main-3JZDUJLU.js} +177 -40
- package/dist/main-3JZDUJLU.js.map +1 -0
- package/dist/{rebuild-7SH5GSNX.js → rebuild-WXKQ5HZO.js} +10 -11
- package/dist/{rebuild-7SH5GSNX.js.map → rebuild-WXKQ5HZO.js.map} +1 -1
- package/dist/reprocess-PKRDV67L.js +79 -0
- package/dist/reprocess-PKRDV67L.js.map +1 -0
- package/dist/{restart-NLJLB52D.js → restart-WSJRHRHI.js} +6 -6
- package/dist/{search-2BVRF54H.js → search-SWMJ4MZ3.js} +6 -6
- package/dist/{server-4AMZNP4F.js → server-NTRVB5ZM.js} +14 -18
- package/dist/{server-4AMZNP4F.js.map → server-NTRVB5ZM.js.map} +1 -1
- package/dist/{session-start-AZAF3DTE.js → session-start-KQ4KCQMZ.js} +9 -9
- package/dist/setup-digest-BOYOSM4B.js +15 -0
- package/dist/setup-llm-PCZ64ALK.js +15 -0
- 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 +7 -7
- package/dist/src/hooks/user-prompt-submit.js +5 -5
- package/dist/src/mcp/server.js +4 -4
- package/dist/src/prompts/consolidation.md +2 -0
- package/dist/src/prompts/digest-7500.md +68 -0
- package/dist/{stats-MKDIZFIQ.js → stats-2OUQSEZO.js} +6 -6
- package/dist/ui/assets/index-Bk4X_8-Z.css +1 -0
- package/dist/ui/assets/index-D3SY7ZHY.js +299 -0
- package/dist/ui/index.html +2 -2
- package/dist/{verify-7DW7LAND.js → verify-MG5O7SBU.js} +6 -6
- package/dist/{version-RQLD7VBP.js → version-NKOECSVH.js} +4 -4
- package/package.json +3 -3
- package/dist/chunk-24DOZEUJ.js.map +0 -1
- package/dist/chunk-2ZBB3MQT.js.map +0 -1
- package/dist/chunk-3JCXYLHD.js +0 -33
- package/dist/chunk-3JCXYLHD.js.map +0 -1
- package/dist/chunk-6BSDCZ5Q.js.map +0 -1
- package/dist/chunk-B5UZSHQV.js +0 -250
- package/dist/chunk-B5UZSHQV.js.map +0 -1
- package/dist/chunk-E7OBRBCQ.js.map +0 -1
- package/dist/chunk-KC7ENQTN.js +0 -436
- package/dist/chunk-KC7ENQTN.js.map +0 -1
- package/dist/chunk-QGJ2ZIUZ.js.map +0 -1
- package/dist/chunk-UVGAVYWZ.js +0 -157
- package/dist/chunk-UVGAVYWZ.js.map +0 -1
- package/dist/chunk-ZMYNRTTD.js.map +0 -1
- package/dist/main-JEUQS3BY.js.map +0 -1
- package/dist/reprocess-Q4YH2ZBK.js +0 -268
- package/dist/reprocess-Q4YH2ZBK.js.map +0 -1
- package/dist/setup-digest-YLZZGSSR.js +0 -15
- package/dist/setup-llm-JOXBSLXC.js +0 -15
- package/dist/ui/assets/index-D37IoDXS.css +0 -1
- package/dist/ui/assets/index-DA61Ial2.js +0 -289
- /package/dist/{chunk-2YBUL3IL.js.map → chunk-25DJSF2K.js.map} +0 -0
- /package/dist/{chunk-2GSX3BK2.js.map → chunk-CPVXNRGW.js.map} +0 -0
- /package/dist/{chunk-L25U7PIG.js.map → chunk-CQ4RKK67.js.map} +0 -0
- /package/dist/{chunk-5FNZ7AMX.js.map → chunk-IWBWZQK6.js.map} +0 -0
- /package/dist/{chunk-5QWZT4AB.js.map → chunk-RNWALAFP.js.map} +0 -0
- /package/dist/{chunk-3EM23DMD.js.map → chunk-RXJHB7W4.js.map} +0 -0
- /package/dist/{chunk-GNR3QAER.js.map → chunk-RY76WEN3.js.map} +0 -0
- /package/dist/{chunk-GDYYJTTT.js.map → chunk-V5R6O6RP.js.map} +0 -0
- /package/dist/{chunk-YTANWAGE.js.map → chunk-XNAM6Z4O.js.map} +0 -0
- /package/dist/{cli-K7SUTP7A.js.map → cli-LMBBPV2D.js.map} +0 -0
- /package/dist/{client-YJMNTITQ.js.map → client-FDKJ4BY7.js.map} +0 -0
- /package/dist/{config-G5GGT5A6.js.map → config-HDUFDOQN.js.map} +0 -0
- /package/dist/{detect-providers-S3M5TAMW.js.map → detect-providers-I2QQFDJW.js.map} +0 -0
- /package/dist/{init-TFLSATB3.js.map → init-7N7F6W6U.js.map} +0 -0
- /package/dist/{restart-NLJLB52D.js.map → restart-WSJRHRHI.js.map} +0 -0
- /package/dist/{search-2BVRF54H.js.map → search-SWMJ4MZ3.js.map} +0 -0
- /package/dist/{session-start-AZAF3DTE.js.map → session-start-KQ4KCQMZ.js.map} +0 -0
- /package/dist/{setup-digest-YLZZGSSR.js.map → setup-digest-BOYOSM4B.js.map} +0 -0
- /package/dist/{setup-llm-JOXBSLXC.js.map → setup-llm-PCZ64ALK.js.map} +0 -0
- /package/dist/{stats-MKDIZFIQ.js.map → stats-2OUQSEZO.js.map} +0 -0
- /package/dist/{verify-7DW7LAND.js.map → verify-MG5O7SBU.js.map} +0 -0
- /package/dist/{version-RQLD7VBP.js.map → version-NKOECSVH.js.map} +0 -0
package/dist/chunk-KC7ENQTN.js
DELETED
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
require_dist
|
|
4
|
-
} from "./chunk-MQSYSQ6T.js";
|
|
5
|
-
import {
|
|
6
|
-
__toESM
|
|
7
|
-
} from "./chunk-PZUWP5VK.js";
|
|
8
|
-
|
|
9
|
-
// src/vault/session-id.ts
|
|
10
|
-
var SESSION_PREFIX = "session-";
|
|
11
|
-
function sessionNoteId(bareId) {
|
|
12
|
-
if (bareId.startsWith(SESSION_PREFIX)) return bareId;
|
|
13
|
-
return `${SESSION_PREFIX}${bareId}`;
|
|
14
|
-
}
|
|
15
|
-
function bareSessionId(noteId) {
|
|
16
|
-
if (noteId.startsWith(SESSION_PREFIX)) return noteId.slice(SESSION_PREFIX.length);
|
|
17
|
-
return noteId;
|
|
18
|
-
}
|
|
19
|
-
function sessionWikilink(bareId) {
|
|
20
|
-
return `[[${sessionNoteId(bareId)}]]`;
|
|
21
|
-
}
|
|
22
|
-
function sessionRelativePath(bareId, date) {
|
|
23
|
-
return `sessions/${date}/${sessionNoteId(bareId)}.md`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// src/obsidian/formatter.ts
|
|
27
|
-
var CALLOUT_MAP = {
|
|
28
|
-
gotcha: "warning",
|
|
29
|
-
bug_fix: "bug",
|
|
30
|
-
decision: "info",
|
|
31
|
-
discovery: "tip",
|
|
32
|
-
trade_off: "question"
|
|
33
|
-
};
|
|
34
|
-
function observationCalloutType(observationType) {
|
|
35
|
-
return CALLOUT_MAP[observationType] ?? "note";
|
|
36
|
-
}
|
|
37
|
-
function escapeHtmlTags(text) {
|
|
38
|
-
return text.replace(/<(?=[a-zA-Z/!])/g, "\\<");
|
|
39
|
-
}
|
|
40
|
-
function callout(type, title, content) {
|
|
41
|
-
const safe = escapeHtmlTags(content);
|
|
42
|
-
const indented = safe.split("\n").map((line) => `> ${line}`).join("\n");
|
|
43
|
-
return `> [!${type}] ${title}
|
|
44
|
-
${indented}`;
|
|
45
|
-
}
|
|
46
|
-
function inlineField(key, value) {
|
|
47
|
-
return `${key}:: ${value}`;
|
|
48
|
-
}
|
|
49
|
-
function wikilink(target, display) {
|
|
50
|
-
return display ? `[[${target}|${display}]]` : `[[${target}]]`;
|
|
51
|
-
}
|
|
52
|
-
function tagNormalize(s) {
|
|
53
|
-
return s.replace(/_/g, "-");
|
|
54
|
-
}
|
|
55
|
-
function sanitizeTag(raw) {
|
|
56
|
-
const stripped = raw.startsWith("#") ? raw.slice(1) : raw;
|
|
57
|
-
return stripped.replace(/\s+/g, "/");
|
|
58
|
-
}
|
|
59
|
-
function buildTags(type, subtype, extraTags = []) {
|
|
60
|
-
const tags = [`type/${type}`];
|
|
61
|
-
if (subtype) {
|
|
62
|
-
tags.push(`${type}/${tagNormalize(subtype)}`);
|
|
63
|
-
}
|
|
64
|
-
for (const tag of extraTags) {
|
|
65
|
-
const normalized = sanitizeTag(tag);
|
|
66
|
-
if (normalized && !tags.includes(normalized)) {
|
|
67
|
-
tags.push(normalized);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return tags;
|
|
71
|
-
}
|
|
72
|
-
function footerTags(tags) {
|
|
73
|
-
return tags.map((t) => t.startsWith("#") ? t : `#${t}`).join(" ");
|
|
74
|
-
}
|
|
75
|
-
function formatSessionBody(input) {
|
|
76
|
-
const sections = [];
|
|
77
|
-
sections.push(`# ${input.title}`);
|
|
78
|
-
if (input.narrative) {
|
|
79
|
-
sections.push(callout("abstract", "Summary", input.narrative));
|
|
80
|
-
}
|
|
81
|
-
const fields = [];
|
|
82
|
-
fields.push(inlineField("Session", wikilink(sessionNoteId(input.sessionId))));
|
|
83
|
-
if (input.user) fields.push(inlineField("User", input.user));
|
|
84
|
-
if (input.started && input.ended) {
|
|
85
|
-
const duration = formatDuration(input.started, input.ended);
|
|
86
|
-
if (duration) fields.push(inlineField("Duration", duration));
|
|
87
|
-
}
|
|
88
|
-
if (input.branch) fields.push(inlineField("Branch", `\`${input.branch}\``));
|
|
89
|
-
sections.push(fields.join("\n"));
|
|
90
|
-
if (input.relatedMemories?.length) {
|
|
91
|
-
const links = input.relatedMemories.map((m) => `- ${wikilink(m.id, m.title)}`);
|
|
92
|
-
sections.push(`## Related Spores
|
|
93
|
-
${links.join("\n")}`);
|
|
94
|
-
}
|
|
95
|
-
if (input.turns.length > 0) {
|
|
96
|
-
const turnLines = [];
|
|
97
|
-
for (let i = 0; i < input.turns.length; i++) {
|
|
98
|
-
const turn = input.turns[i];
|
|
99
|
-
const turnNum = i + 1;
|
|
100
|
-
turnLines.push(`### Turn ${turnNum}`);
|
|
101
|
-
if (turn.prompt || turn.images?.length) {
|
|
102
|
-
const parts = [];
|
|
103
|
-
if (turn.prompt) parts.push(turn.prompt);
|
|
104
|
-
if (turn.images?.length) {
|
|
105
|
-
parts.push(turn.images.map((f) => `![[${f}]]`).join("\n"));
|
|
106
|
-
}
|
|
107
|
-
if (turn.toolCount > 0) parts.push(`*${turn.toolCount} tool calls*`);
|
|
108
|
-
turnLines.push(callout("user", "Prompt", parts.join("\n\n")));
|
|
109
|
-
} else if (turn.toolCount > 0) {
|
|
110
|
-
turnLines.push(callout("user", "Prompt", `*${turn.toolCount} tool calls*`));
|
|
111
|
-
}
|
|
112
|
-
if (turn.aiResponse) {
|
|
113
|
-
turnLines.push(callout("assistant", "Response", turn.aiResponse));
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
sections.push(`## Conversation
|
|
117
|
-
|
|
118
|
-
${turnLines.join("\n\n")}`);
|
|
119
|
-
}
|
|
120
|
-
const allTags = buildTags("session", "ended", [
|
|
121
|
-
...input.user ? [`user/${input.user}`] : [],
|
|
122
|
-
...input.tags ?? []
|
|
123
|
-
]);
|
|
124
|
-
sections.push(footerTags(allTags));
|
|
125
|
-
return sections.join("\n\n");
|
|
126
|
-
}
|
|
127
|
-
function formatSporeBody(input) {
|
|
128
|
-
const sections = [];
|
|
129
|
-
const calloutType = observationCalloutType(input.observationType);
|
|
130
|
-
const calloutTitle = capitalize(tagNormalize(input.observationType));
|
|
131
|
-
sections.push(`# ${input.title}`);
|
|
132
|
-
sections.push(callout(calloutType, calloutTitle, input.content));
|
|
133
|
-
const fields = [];
|
|
134
|
-
if (input.sessionId) {
|
|
135
|
-
fields.push(inlineField("Session", wikilink(sessionNoteId(input.sessionId))));
|
|
136
|
-
}
|
|
137
|
-
fields.push(inlineField("Observation", input.observationType));
|
|
138
|
-
if (fields.length > 0) sections.push(fields.join("\n"));
|
|
139
|
-
if (input.root_cause) sections.push(`## Root Cause
|
|
140
|
-
${input.root_cause}`);
|
|
141
|
-
if (input.fix) sections.push(`## Fix
|
|
142
|
-
${input.fix}`);
|
|
143
|
-
if (input.rationale) sections.push(`## Rationale
|
|
144
|
-
${input.rationale}`);
|
|
145
|
-
if (input.alternatives_rejected) sections.push(`## Alternatives Rejected
|
|
146
|
-
${input.alternatives_rejected}`);
|
|
147
|
-
if (input.gained) sections.push(`## Gained
|
|
148
|
-
${input.gained}`);
|
|
149
|
-
if (input.sacrificed) sections.push(`## Sacrificed
|
|
150
|
-
${input.sacrificed}`);
|
|
151
|
-
const allTags = buildTags("spore", input.observationType, input.tags ?? []);
|
|
152
|
-
sections.push(footerTags(allTags));
|
|
153
|
-
return sections.join("\n\n");
|
|
154
|
-
}
|
|
155
|
-
function formatPlanBody(input) {
|
|
156
|
-
const sections = [];
|
|
157
|
-
const fields = [];
|
|
158
|
-
fields.push(inlineField("Plan", wikilink(input.id)));
|
|
159
|
-
fields.push(inlineField("Status", input.status));
|
|
160
|
-
if (input.author) fields.push(inlineField("Author", input.author));
|
|
161
|
-
if (input.created) fields.push(inlineField("Created", input.created));
|
|
162
|
-
sections.push(fields.join("\n"));
|
|
163
|
-
sections.push(input.content);
|
|
164
|
-
if (input.sessions?.length) {
|
|
165
|
-
const links = input.sessions.map((s) => `- ${wikilink(sessionNoteId(s.id), s.title)}`);
|
|
166
|
-
sections.push(`## Sessions
|
|
167
|
-
${links.join("\n")}`);
|
|
168
|
-
}
|
|
169
|
-
const statusTag = tagNormalize(input.status);
|
|
170
|
-
const allTags = buildTags("plan", statusTag, input.tags ?? []);
|
|
171
|
-
sections.push(footerTags(allTags));
|
|
172
|
-
return sections.join("\n\n");
|
|
173
|
-
}
|
|
174
|
-
function formatArtifactBody(input) {
|
|
175
|
-
const sections = [];
|
|
176
|
-
const fields = [];
|
|
177
|
-
fields.push(inlineField("Artifact", wikilink(input.id)));
|
|
178
|
-
fields.push(inlineField("Source", `\`${input.source_path}\``));
|
|
179
|
-
fields.push(inlineField("Type", input.artifact_type));
|
|
180
|
-
fields.push(inlineField("Session", wikilink(sessionNoteId(input.sessionId))));
|
|
181
|
-
sections.push(fields.join("\n"));
|
|
182
|
-
sections.push(input.content);
|
|
183
|
-
const allTags = buildTags("artifact", input.artifact_type, input.tags ?? []);
|
|
184
|
-
sections.push(footerTags(allTags));
|
|
185
|
-
return sections.join("\n\n");
|
|
186
|
-
}
|
|
187
|
-
function formatTeamBody(input) {
|
|
188
|
-
const sections = [];
|
|
189
|
-
sections.push(`# ${input.user}`);
|
|
190
|
-
sections.push(callout("info", "Team Member", input.role ?? "Contributor"));
|
|
191
|
-
const fields = [];
|
|
192
|
-
fields.push(inlineField("User", input.user));
|
|
193
|
-
if (input.role) fields.push(inlineField("Role", input.role));
|
|
194
|
-
sections.push(fields.join("\n"));
|
|
195
|
-
if (input.recentSessions?.length) {
|
|
196
|
-
const links = input.recentSessions.map((s) => `- ${wikilink(sessionNoteId(s.id), s.title)}`);
|
|
197
|
-
sections.push(`## Recent Sessions
|
|
198
|
-
${links.join("\n")}`);
|
|
199
|
-
}
|
|
200
|
-
const allTags = buildTags("team", "", [`user/${input.user}`]);
|
|
201
|
-
sections.push(footerTags(allTags));
|
|
202
|
-
return sections.join("\n\n");
|
|
203
|
-
}
|
|
204
|
-
var FOOTER_TAG_PREFIX = "\n#type/";
|
|
205
|
-
function extractSection(body, heading) {
|
|
206
|
-
const start = body.indexOf(heading);
|
|
207
|
-
if (start === -1) return "";
|
|
208
|
-
const section = body.slice(start + heading.length);
|
|
209
|
-
const footerIdx = section.lastIndexOf(FOOTER_TAG_PREFIX);
|
|
210
|
-
if (footerIdx !== -1) return section.slice(0, footerIdx).trim();
|
|
211
|
-
return section.trim();
|
|
212
|
-
}
|
|
213
|
-
function capitalize(s) {
|
|
214
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
215
|
-
}
|
|
216
|
-
function formatDuration(started, ended) {
|
|
217
|
-
const ms = new Date(ended).getTime() - new Date(started).getTime();
|
|
218
|
-
if (isNaN(ms) || ms < 0) return "";
|
|
219
|
-
const totalMinutes = Math.floor(ms / 6e4);
|
|
220
|
-
if (totalMinutes < 1) return "<1m";
|
|
221
|
-
const hours = Math.floor(totalMinutes / 60);
|
|
222
|
-
const minutes = totalMinutes % 60;
|
|
223
|
-
if (hours === 0) return `${minutes}m`;
|
|
224
|
-
if (minutes === 0) return `${hours}h`;
|
|
225
|
-
return `${hours}h ${minutes}m`;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// src/vault/writer.ts
|
|
229
|
-
var import_yaml = __toESM(require_dist(), 1);
|
|
230
|
-
import fs from "fs";
|
|
231
|
-
import path from "path";
|
|
232
|
-
var VaultWriter = class {
|
|
233
|
-
constructor(vaultDir) {
|
|
234
|
-
this.vaultDir = vaultDir;
|
|
235
|
-
}
|
|
236
|
-
writeSession(input) {
|
|
237
|
-
const date = input.started.slice(0, 10);
|
|
238
|
-
const relativePath = sessionRelativePath(input.id, date);
|
|
239
|
-
const frontmatter = {
|
|
240
|
-
type: "session",
|
|
241
|
-
id: input.id,
|
|
242
|
-
agent: input.agent ?? "claude-code",
|
|
243
|
-
user: input.user ?? "",
|
|
244
|
-
started: input.started
|
|
245
|
-
};
|
|
246
|
-
if (input.ended) frontmatter.ended = input.ended;
|
|
247
|
-
if (input.parent) frontmatter.parent = input.parent;
|
|
248
|
-
if (input.parent_reason) frontmatter.parent_reason = input.parent_reason;
|
|
249
|
-
if (input.plans?.length) frontmatter.plans = input.plans;
|
|
250
|
-
if (input.branch) frontmatter.branch = input.branch;
|
|
251
|
-
frontmatter.tags = buildTags("session", "ended", [
|
|
252
|
-
...input.user ? [`user/${input.user}`] : [],
|
|
253
|
-
...input.tags ?? []
|
|
254
|
-
]);
|
|
255
|
-
if (input.tools_used != null) frontmatter.tools_used = input.tools_used;
|
|
256
|
-
if (input.files_changed != null) frontmatter.files_changed = input.files_changed;
|
|
257
|
-
this.writeMarkdown(relativePath, frontmatter, input.summary);
|
|
258
|
-
return relativePath;
|
|
259
|
-
}
|
|
260
|
-
writePlan(input) {
|
|
261
|
-
const relativePath = `plans/${input.id}.md`;
|
|
262
|
-
const fullPath = path.join(this.vaultDir, relativePath);
|
|
263
|
-
const status = input.status ?? "active";
|
|
264
|
-
let created = (/* @__PURE__ */ new Date()).toISOString();
|
|
265
|
-
try {
|
|
266
|
-
const existing = fs.readFileSync(fullPath, "utf-8");
|
|
267
|
-
const fmMatch = existing.match(/^---\n([\s\S]*?)\n---/);
|
|
268
|
-
if (fmMatch) {
|
|
269
|
-
const parsed = import_yaml.default.parse(fmMatch[1]);
|
|
270
|
-
if (typeof parsed.created === "string") created = parsed.created;
|
|
271
|
-
}
|
|
272
|
-
} catch {
|
|
273
|
-
}
|
|
274
|
-
const frontmatter = {
|
|
275
|
-
type: "plan",
|
|
276
|
-
id: input.id,
|
|
277
|
-
status,
|
|
278
|
-
created
|
|
279
|
-
};
|
|
280
|
-
if (input.author) frontmatter.author = input.author;
|
|
281
|
-
frontmatter.tags = buildTags("plan", status, input.tags ?? []);
|
|
282
|
-
const body = formatPlanBody({
|
|
283
|
-
id: input.id,
|
|
284
|
-
status,
|
|
285
|
-
author: input.author,
|
|
286
|
-
created,
|
|
287
|
-
content: input.content,
|
|
288
|
-
tags: input.tags
|
|
289
|
-
});
|
|
290
|
-
this.writeMarkdown(relativePath, frontmatter, body);
|
|
291
|
-
return relativePath;
|
|
292
|
-
}
|
|
293
|
-
writeSpore(input) {
|
|
294
|
-
const normalizedType = input.observation_type.replace(/_/g, "-");
|
|
295
|
-
const relativePath = `spores/${normalizedType}/${input.id}.md`;
|
|
296
|
-
const fullPath = path.join(this.vaultDir, relativePath);
|
|
297
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
298
|
-
let created = now;
|
|
299
|
-
try {
|
|
300
|
-
const existing = fs.readFileSync(fullPath, "utf-8");
|
|
301
|
-
const fmMatch = existing.match(/^---\n([\s\S]*?)\n---/);
|
|
302
|
-
if (fmMatch) {
|
|
303
|
-
const parsed = import_yaml.default.parse(fmMatch[1]);
|
|
304
|
-
if (typeof parsed.created === "string") created = parsed.created;
|
|
305
|
-
}
|
|
306
|
-
} catch {
|
|
307
|
-
}
|
|
308
|
-
const frontmatter = {
|
|
309
|
-
type: "spore",
|
|
310
|
-
id: input.id,
|
|
311
|
-
observation_type: input.observation_type,
|
|
312
|
-
created
|
|
313
|
-
};
|
|
314
|
-
if (input.session) frontmatter.session = input.session;
|
|
315
|
-
if (input.plan) frontmatter.plan = input.plan;
|
|
316
|
-
frontmatter.tags = buildTags("spore", input.observation_type, input.tags ?? []);
|
|
317
|
-
this.writeMarkdown(relativePath, frontmatter, input.content);
|
|
318
|
-
return relativePath;
|
|
319
|
-
}
|
|
320
|
-
writeArtifact(input) {
|
|
321
|
-
const relativePath = `artifacts/${input.id}.md`;
|
|
322
|
-
const fullPath = path.join(this.vaultDir, relativePath);
|
|
323
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
324
|
-
let created = now;
|
|
325
|
-
try {
|
|
326
|
-
const existing = fs.readFileSync(fullPath, "utf-8");
|
|
327
|
-
const fmMatch = existing.match(/^---\n([\s\S]*?)\n---/);
|
|
328
|
-
if (fmMatch) {
|
|
329
|
-
const parsed = import_yaml.default.parse(fmMatch[1]);
|
|
330
|
-
if (typeof parsed.created === "string") created = parsed.created;
|
|
331
|
-
}
|
|
332
|
-
} catch {
|
|
333
|
-
}
|
|
334
|
-
const frontmatter = {
|
|
335
|
-
type: "artifact",
|
|
336
|
-
id: input.id,
|
|
337
|
-
artifact_type: input.artifact_type,
|
|
338
|
-
source_path: input.source_path,
|
|
339
|
-
title: input.title,
|
|
340
|
-
last_captured_by: sessionNoteId(input.session),
|
|
341
|
-
created,
|
|
342
|
-
updated: now,
|
|
343
|
-
tags: buildTags("artifact", input.artifact_type, input.tags ?? [])
|
|
344
|
-
};
|
|
345
|
-
const body = formatArtifactBody({
|
|
346
|
-
id: input.id,
|
|
347
|
-
title: input.title,
|
|
348
|
-
artifact_type: input.artifact_type,
|
|
349
|
-
source_path: input.source_path,
|
|
350
|
-
sessionId: input.session,
|
|
351
|
-
content: input.content,
|
|
352
|
-
tags: input.tags
|
|
353
|
-
});
|
|
354
|
-
this.writeMarkdown(relativePath, frontmatter, body);
|
|
355
|
-
return relativePath;
|
|
356
|
-
}
|
|
357
|
-
writeTeamMember(input) {
|
|
358
|
-
const relativePath = `team/${input.user}.md`;
|
|
359
|
-
const frontmatter = {
|
|
360
|
-
type: "team-member",
|
|
361
|
-
user: input.user,
|
|
362
|
-
joined: (/* @__PURE__ */ new Date()).toISOString(),
|
|
363
|
-
tags: buildTags("team", "", [`user/${input.user}`])
|
|
364
|
-
};
|
|
365
|
-
if (input.role) frontmatter.role = input.role;
|
|
366
|
-
const body = formatTeamBody({
|
|
367
|
-
user: input.user,
|
|
368
|
-
role: input.role
|
|
369
|
-
});
|
|
370
|
-
this.writeMarkdown(relativePath, frontmatter, body);
|
|
371
|
-
return relativePath;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Update frontmatter fields on an existing note without touching the body.
|
|
375
|
-
* By default only adds fields that don't exist. Set overwrite=true to replace existing values.
|
|
376
|
-
* Returns true if the update was applied, false if the file doesn't exist.
|
|
377
|
-
*/
|
|
378
|
-
updateNoteFrontmatter(relativePath, fields, overwrite = false) {
|
|
379
|
-
const fullPath = path.join(this.vaultDir, relativePath);
|
|
380
|
-
let fileContent;
|
|
381
|
-
try {
|
|
382
|
-
fileContent = fs.readFileSync(fullPath, "utf-8");
|
|
383
|
-
} catch {
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
const fmMatch = fileContent.match(/^---\n([\s\S]*?)\n---/);
|
|
387
|
-
if (!fmMatch) return false;
|
|
388
|
-
const parsed = import_yaml.default.parse(fmMatch[1]);
|
|
389
|
-
for (const [key, value] of Object.entries(fields)) {
|
|
390
|
-
if (overwrite || parsed[key] === void 0) {
|
|
391
|
-
parsed[key] = value;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
const body = fileContent.slice(fmMatch[0].length);
|
|
395
|
-
const fmYaml = import_yaml.default.stringify(parsed, { defaultStringType: "QUOTE_DOUBLE", defaultKeyType: "PLAIN" }).trim();
|
|
396
|
-
this.atomicWrite(fullPath, `---
|
|
397
|
-
${fmYaml}
|
|
398
|
-
---${body}`);
|
|
399
|
-
return true;
|
|
400
|
-
}
|
|
401
|
-
/** @deprecated Use updateNoteFrontmatter instead */
|
|
402
|
-
updateSessionFrontmatter(relativePath, fields) {
|
|
403
|
-
return this.updateNoteFrontmatter(relativePath, fields);
|
|
404
|
-
}
|
|
405
|
-
writeMarkdown(relativePath, frontmatter, content) {
|
|
406
|
-
const fullPath = path.join(this.vaultDir, relativePath);
|
|
407
|
-
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
408
|
-
const fmYaml = import_yaml.default.stringify(frontmatter, { defaultStringType: "QUOTE_DOUBLE", defaultKeyType: "PLAIN" }).trim();
|
|
409
|
-
const file = `---
|
|
410
|
-
${fmYaml}
|
|
411
|
-
---
|
|
412
|
-
|
|
413
|
-
${content}
|
|
414
|
-
`;
|
|
415
|
-
this.atomicWrite(fullPath, file);
|
|
416
|
-
}
|
|
417
|
-
/** Write to a temp file then rename — prevents Obsidian from seeing a truncated file mid-write. */
|
|
418
|
-
atomicWrite(fullPath, content) {
|
|
419
|
-
const tmp = `${fullPath}.tmp`;
|
|
420
|
-
fs.writeFileSync(tmp, content, "utf-8");
|
|
421
|
-
fs.renameSync(tmp, fullPath);
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
|
|
425
|
-
export {
|
|
426
|
-
sessionNoteId,
|
|
427
|
-
bareSessionId,
|
|
428
|
-
sessionWikilink,
|
|
429
|
-
sessionRelativePath,
|
|
430
|
-
callout,
|
|
431
|
-
formatSessionBody,
|
|
432
|
-
formatSporeBody,
|
|
433
|
-
extractSection,
|
|
434
|
-
VaultWriter
|
|
435
|
-
};
|
|
436
|
-
//# sourceMappingURL=chunk-KC7ENQTN.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vault/session-id.ts","../src/obsidian/formatter.ts","../src/vault/writer.ts"],"sourcesContent":["/**\n * Session ID convention utilities.\n *\n * The runtime session ID is a bare UUID (e.g., \"abc123\").\n * The vault stores it with a \"session-\" prefix (e.g., \"session-abc123\").\n * These helpers convert between the two forms.\n */\nconst SESSION_PREFIX = 'session-';\n\n/** Convert a bare session ID to a vault note ID: \"abc123\" → \"session-abc123\" */\nexport function sessionNoteId(bareId: string): string {\n if (bareId.startsWith(SESSION_PREFIX)) return bareId;\n return `${SESSION_PREFIX}${bareId}`;\n}\n\n/** Convert a vault note ID to a bare session ID: \"session-abc123\" → \"abc123\" */\nexport function bareSessionId(noteId: string): string {\n if (noteId.startsWith(SESSION_PREFIX)) return noteId.slice(SESSION_PREFIX.length);\n return noteId;\n}\n\n/** Convert a bare session ID to an Obsidian wikilink: \"abc123\" → \"[[session-abc123]]\" */\nexport function sessionWikilink(bareId: string): string {\n return `[[${sessionNoteId(bareId)}]]`;\n}\n\n/** Build the relative vault path for a session note */\nexport function sessionRelativePath(bareId: string, date: string): string {\n return `sessions/${date}/${sessionNoteId(bareId)}.md`;\n}\n\n","/**\n * Pure formatting functions for Obsidian-native vault notes.\n * No I/O, no external dependencies — just string transforms.\n */\nimport type { ArtifactType } from '../vault/types.js';\nimport { sessionNoteId } from '../vault/session-id.js';\n\n// Callout type mapping for observation types\nconst CALLOUT_MAP: Record<string, string> = {\n gotcha: 'warning',\n bug_fix: 'bug',\n decision: 'info',\n discovery: 'tip',\n trade_off: 'question',\n};\n\nexport function observationCalloutType(observationType: string): string {\n return CALLOUT_MAP[observationType] ?? 'note';\n}\n\n/**\n * Escape angle brackets that Obsidian would interpret as HTML tags.\n * Matches `<` followed by a letter, `/`, or `!` (opening/closing tags, comments).\n */\nexport function escapeHtmlTags(text: string): string {\n return text.replace(/<(?=[a-zA-Z/!])/g, '\\\\<');\n}\n\nexport function callout(type: string, title: string, content: string): string {\n const safe = escapeHtmlTags(content);\n const indented = safe.split('\\n').map((line) => `> ${line}`).join('\\n');\n return `> [!${type}] ${title}\\n${indented}`;\n}\n\nexport function inlineField(key: string, value: string): string {\n return `${key}:: ${value}`;\n}\n\nexport function wikilink(target: string, display?: string): string {\n return display ? `[[${target}|${display}]]` : `[[${target}]]`;\n}\n\n/**\n * Normalize an observation_type to a tag-safe form.\n * Frontmatter keeps underscores; tags use hyphens per Obsidian convention.\n */\nfunction tagNormalize(s: string): string {\n return s.replace(/_/g, '-');\n}\n\n/**\n * Sanitize a user/LLM-provided tag for Obsidian compatibility.\n * Obsidian tags cannot contain spaces — replace with slash (nested tag).\n * Strips leading # if present.\n */\nfunction sanitizeTag(raw: string): string {\n const stripped = raw.startsWith('#') ? raw.slice(1) : raw;\n return stripped.replace(/\\s+/g, '/');\n}\n\nexport function buildTags(type: string, subtype: string, extraTags: string[] = []): string[] {\n const tags: string[] = [`type/${type}`];\n\n if (subtype) {\n tags.push(`${type}/${tagNormalize(subtype)}`);\n }\n\n for (const tag of extraTags) {\n const normalized = sanitizeTag(tag);\n if (normalized && !tags.includes(normalized)) {\n tags.push(normalized);\n }\n }\n\n return tags;\n}\n\nexport function footerTags(tags: string[]): string {\n return tags.map((t) => (t.startsWith('#') ? t : `#${t}`)).join(' ');\n}\n\n// --- Session formatting ---\n\nexport interface SessionBodyInput {\n title: string;\n narrative: string;\n sessionId: string;\n user?: string;\n started?: string;\n ended?: string;\n branch?: string;\n relatedMemories?: Array<{ id: string; title: string }>;\n turns: Array<{\n prompt: string;\n toolCount: number;\n aiResponse?: string;\n /** Filenames of images in the vault attachments folder */\n images?: string[];\n }>;\n tags?: string[];\n}\n\nexport function formatSessionBody(input: SessionBodyInput): string {\n const sections: string[] = [];\n\n sections.push(`# ${input.title}`);\n\n if (input.narrative) {\n sections.push(callout('abstract', 'Summary', input.narrative));\n }\n\n // Inline fields\n const fields: string[] = [];\n fields.push(inlineField('Session', wikilink(sessionNoteId(input.sessionId))));\n if (input.user) fields.push(inlineField('User', input.user));\n if (input.started && input.ended) {\n const duration = formatDuration(input.started, input.ended);\n if (duration) fields.push(inlineField('Duration', duration));\n }\n if (input.branch) fields.push(inlineField('Branch', `\\`${input.branch}\\``));\n sections.push(fields.join('\\n'));\n\n // Related spores\n if (input.relatedMemories?.length) {\n const links = input.relatedMemories.map((m) => `- ${wikilink(m.id, m.title)}`);\n sections.push(`## Related Spores\\n${links.join('\\n')}`);\n }\n\n // Conversation turns — always rebuilt from the full transcript.\n // The transcript is the source of truth for the complete conversation.\n if (input.turns.length > 0) {\n const turnLines: string[] = [];\n for (let i = 0; i < input.turns.length; i++) {\n const turn = input.turns[i];\n const turnNum = i + 1;\n turnLines.push(`### Turn ${turnNum}`);\n if (turn.prompt || turn.images?.length) {\n // Build prompt content: text + images + tool count\n const parts: string[] = [];\n if (turn.prompt) parts.push(turn.prompt);\n if (turn.images?.length) {\n parts.push(turn.images.map((f) => `![[${f}]]`).join('\\n'));\n }\n if (turn.toolCount > 0) parts.push(`*${turn.toolCount} tool calls*`);\n turnLines.push(callout('user', 'Prompt', parts.join('\\n\\n')));\n } else if (turn.toolCount > 0) {\n turnLines.push(callout('user', 'Prompt', `*${turn.toolCount} tool calls*`));\n }\n if (turn.aiResponse) {\n turnLines.push(callout('assistant', 'Response', turn.aiResponse));\n }\n }\n sections.push(`## Conversation\\n\\n${turnLines.join('\\n\\n')}`);\n }\n\n // Footer tags\n const allTags = buildTags('session', 'ended', [\n ...(input.user ? [`user/${input.user}`] : []),\n ...(input.tags ?? []),\n ]);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Spore formatting ---\n\nexport interface SporeBodyInput {\n title: string;\n observationType: string;\n content: string;\n sessionId?: string;\n root_cause?: string;\n fix?: string;\n rationale?: string;\n alternatives_rejected?: string;\n gained?: string;\n sacrificed?: string;\n tags?: string[];\n}\n\nexport function formatSporeBody(input: SporeBodyInput): string {\n const sections: string[] = [];\n const calloutType = observationCalloutType(input.observationType);\n const calloutTitle = capitalize(tagNormalize(input.observationType));\n\n sections.push(`# ${input.title}`);\n sections.push(callout(calloutType, calloutTitle, input.content));\n\n // Inline fields\n const fields: string[] = [];\n if (input.sessionId) {\n fields.push(inlineField('Session', wikilink(sessionNoteId(input.sessionId))));\n }\n fields.push(inlineField('Observation', input.observationType));\n if (fields.length > 0) sections.push(fields.join('\\n'));\n\n // Type-specific sub-sections\n if (input.root_cause) sections.push(`## Root Cause\\n${input.root_cause}`);\n if (input.fix) sections.push(`## Fix\\n${input.fix}`);\n if (input.rationale) sections.push(`## Rationale\\n${input.rationale}`);\n if (input.alternatives_rejected) sections.push(`## Alternatives Rejected\\n${input.alternatives_rejected}`);\n if (input.gained) sections.push(`## Gained\\n${input.gained}`);\n if (input.sacrificed) sections.push(`## Sacrificed\\n${input.sacrificed}`);\n\n // Footer tags\n const allTags = buildTags('spore', input.observationType, input.tags ?? []);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Plan formatting ---\n\nexport interface PlanBodyInput {\n id: string;\n status: string;\n author?: string;\n created?: string;\n sessions?: Array<{ id: string; title: string }>;\n content: string;\n tags?: string[];\n}\n\nexport function formatPlanBody(input: PlanBodyInput): string {\n const sections: string[] = [];\n\n // Inline fields block\n const fields: string[] = [];\n fields.push(inlineField('Plan', wikilink(input.id)));\n fields.push(inlineField('Status', input.status));\n if (input.author) fields.push(inlineField('Author', input.author));\n if (input.created) fields.push(inlineField('Created', input.created));\n sections.push(fields.join('\\n'));\n\n // User-provided content body (don't restructure)\n sections.push(input.content);\n\n // Sessions section\n if (input.sessions?.length) {\n const links = input.sessions.map((s) => `- ${wikilink(sessionNoteId(s.id), s.title)}`);\n sections.push(`## Sessions\\n${links.join('\\n')}`);\n }\n\n // Footer tags\n const statusTag = tagNormalize(input.status);\n const allTags = buildTags('plan', statusTag, input.tags ?? []);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Artifact formatting ---\n\nexport interface ArtifactBodyInput {\n id: string;\n title: string;\n artifact_type: ArtifactType;\n source_path: string;\n sessionId: string;\n content: string;\n tags?: string[];\n}\n\nexport function formatArtifactBody(input: ArtifactBodyInput): string {\n const sections: string[] = [];\n\n // Inline fields\n const fields: string[] = [];\n fields.push(inlineField('Artifact', wikilink(input.id)));\n fields.push(inlineField('Source', `\\`${input.source_path}\\``));\n fields.push(inlineField('Type', input.artifact_type));\n fields.push(inlineField('Session', wikilink(sessionNoteId(input.sessionId))));\n sections.push(fields.join('\\n'));\n\n // Body: full content from disk\n sections.push(input.content);\n\n // Footer tags\n const allTags = buildTags('artifact', input.artifact_type, input.tags ?? []);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Team formatting ---\n\nexport interface TeamBodyInput {\n user: string;\n role?: string;\n recentSessions?: Array<{ id: string; title: string }>;\n}\n\nexport function formatTeamBody(input: TeamBodyInput): string {\n const sections: string[] = [];\n\n sections.push(`# ${input.user}`);\n sections.push(callout('info', 'Team Member', input.role ?? 'Contributor'));\n\n // Inline fields\n const fields: string[] = [];\n fields.push(inlineField('User', input.user));\n if (input.role) fields.push(inlineField('Role', input.role));\n sections.push(fields.join('\\n'));\n\n // Recent sessions\n if (input.recentSessions?.length) {\n const links = input.recentSessions.map((s) => `- ${wikilink(sessionNoteId(s.id), s.title)}`);\n sections.push(`## Recent Sessions\\n${links.join('\\n')}`);\n }\n\n // Footer tags\n const allTags = buildTags('team', '', [`user/${input.user}`]);\n sections.push(footerTags(allTags));\n\n return sections.join('\\n\\n');\n}\n\n// --- Section extraction ---\n\n/** Footer tag prefix used to terminate section extraction. */\nconst FOOTER_TAG_PREFIX = '\\n#type/';\n\n/**\n * Extract a named section from a session/spore note body.\n * Returns content between the section heading and the footer tags.\n */\nexport function extractSection(body: string, heading: string): string {\n const start = body.indexOf(heading);\n if (start === -1) return '';\n const section = body.slice(start + heading.length);\n const footerIdx = section.lastIndexOf(FOOTER_TAG_PREFIX);\n if (footerIdx !== -1) return section.slice(0, footerIdx).trim();\n return section.trim();\n}\n\n// --- Helpers ---\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction formatDuration(started: string, ended: string): string {\n const ms = new Date(ended).getTime() - new Date(started).getTime();\n if (isNaN(ms) || ms < 0) return '';\n const totalMinutes = Math.floor(ms / 60000);\n if (totalMinutes < 1) return '<1m';\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n if (hours === 0) return `${minutes}m`;\n if (minutes === 0) return `${hours}h`;\n return `${hours}h ${minutes}m`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport { buildTags, formatTeamBody, formatPlanBody, formatArtifactBody } from '../obsidian/formatter.js';\nimport type { ArtifactType } from './types.js';\nimport { sessionNoteId, sessionRelativePath } from './session-id.js';\n\ninterface WriteSessionInput {\n id: string;\n agent?: string;\n user?: string;\n started: string;\n ended?: string;\n parent?: string;\n parent_reason?: string;\n plans?: string[];\n branch?: string;\n tags?: string[];\n tools_used?: number;\n files_changed?: number;\n summary: string;\n}\n\ninterface WritePlanInput {\n id: string;\n status?: string;\n author?: string;\n tags?: string[];\n content: string;\n}\n\ninterface WriteSporeInput {\n id: string;\n observation_type: string;\n session?: string;\n plan?: string;\n tags?: string[];\n content: string;\n}\n\ninterface WriteArtifactInput {\n id: string;\n artifact_type: ArtifactType;\n source_path: string;\n title: string;\n session: string;\n tags?: string[];\n content: string;\n}\n\ninterface WriteTeamMemberInput {\n user: string;\n role?: string;\n}\n\nexport class VaultWriter {\n constructor(private vaultDir: string) {}\n\n writeSession(input: WriteSessionInput): string {\n const date = input.started.slice(0, 10);\n const relativePath = sessionRelativePath(input.id, date);\n\n const frontmatter: Record<string, unknown> = {\n type: 'session',\n id: input.id,\n agent: input.agent ?? 'claude-code',\n user: input.user ?? '',\n started: input.started,\n };\n if (input.ended) frontmatter.ended = input.ended;\n if (input.parent) frontmatter.parent = input.parent;\n if (input.parent_reason) frontmatter.parent_reason = input.parent_reason;\n if (input.plans?.length) frontmatter.plans = input.plans;\n if (input.branch) frontmatter.branch = input.branch;\n frontmatter.tags = buildTags('session', 'ended', [\n ...(input.user ? [`user/${input.user}`] : []),\n ...(input.tags ?? []),\n ]);\n if (input.tools_used != null) frontmatter.tools_used = input.tools_used;\n if (input.files_changed != null) frontmatter.files_changed = input.files_changed;\n\n this.writeMarkdown(relativePath, frontmatter, input.summary);\n return relativePath;\n }\n\n writePlan(input: WritePlanInput): string {\n const relativePath = `plans/${input.id}.md`;\n const fullPath = path.join(this.vaultDir, relativePath);\n\n const status = input.status ?? 'active';\n let created = new Date().toISOString();\n\n // Preserve created from existing file (idempotent)\n try {\n const existing = fs.readFileSync(fullPath, 'utf-8');\n const fmMatch = existing.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (fmMatch) {\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n if (typeof parsed.created === 'string') created = parsed.created;\n }\n } catch {\n // File doesn't exist yet\n }\n const frontmatter: Record<string, unknown> = {\n type: 'plan',\n id: input.id,\n status,\n created,\n };\n if (input.author) frontmatter.author = input.author;\n frontmatter.tags = buildTags('plan', status, input.tags ?? []);\n\n const body = formatPlanBody({\n id: input.id,\n status,\n author: input.author,\n created,\n content: input.content,\n tags: input.tags,\n });\n\n this.writeMarkdown(relativePath, frontmatter, body);\n return relativePath;\n }\n\n writeSpore(input: WriteSporeInput): string {\n const normalizedType = input.observation_type.replace(/_/g, '-');\n const relativePath = `spores/${normalizedType}/${input.id}.md`;\n const fullPath = path.join(this.vaultDir, relativePath);\n const now = new Date().toISOString();\n\n // Preserve created from existing file (idempotent)\n let created = now;\n try {\n const existing = fs.readFileSync(fullPath, 'utf-8');\n const fmMatch = existing.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (fmMatch) {\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n if (typeof parsed.created === 'string') created = parsed.created;\n }\n } catch {\n // File doesn't exist yet — created = now\n }\n\n const frontmatter: Record<string, unknown> = {\n type: 'spore',\n id: input.id,\n observation_type: input.observation_type,\n created,\n };\n if (input.session) frontmatter.session = input.session;\n if (input.plan) frontmatter.plan = input.plan;\n frontmatter.tags = buildTags('spore', input.observation_type, input.tags ?? []);\n\n this.writeMarkdown(relativePath, frontmatter, input.content);\n return relativePath;\n }\n\n writeArtifact(input: WriteArtifactInput): string {\n const relativePath = `artifacts/${input.id}.md`;\n const fullPath = path.join(this.vaultDir, relativePath);\n const now = new Date().toISOString();\n\n let created = now;\n\n // Preserve created from existing file (latest-wins update)\n try {\n const existing = fs.readFileSync(fullPath, 'utf-8');\n const fmMatch = existing.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (fmMatch) {\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n if (typeof parsed.created === 'string') created = parsed.created;\n }\n } catch {\n // File doesn't exist yet — created = now\n }\n\n const frontmatter: Record<string, unknown> = {\n type: 'artifact',\n id: input.id,\n artifact_type: input.artifact_type,\n source_path: input.source_path,\n title: input.title,\n last_captured_by: sessionNoteId(input.session),\n created,\n updated: now,\n tags: buildTags('artifact', input.artifact_type, input.tags ?? []),\n };\n\n const body = formatArtifactBody({\n id: input.id,\n title: input.title,\n artifact_type: input.artifact_type,\n source_path: input.source_path,\n sessionId: input.session,\n content: input.content,\n tags: input.tags,\n });\n\n this.writeMarkdown(relativePath, frontmatter, body);\n return relativePath;\n }\n\n writeTeamMember(input: WriteTeamMemberInput): string {\n const relativePath = `team/${input.user}.md`;\n\n const frontmatter: Record<string, unknown> = {\n type: 'team-member',\n user: input.user,\n joined: new Date().toISOString(),\n tags: buildTags('team', '', [`user/${input.user}`]),\n };\n if (input.role) frontmatter.role = input.role;\n\n const body = formatTeamBody({\n user: input.user,\n role: input.role,\n });\n\n this.writeMarkdown(relativePath, frontmatter, body);\n return relativePath;\n }\n\n /**\n * Update frontmatter fields on an existing note without touching the body.\n * By default only adds fields that don't exist. Set overwrite=true to replace existing values.\n * Returns true if the update was applied, false if the file doesn't exist.\n */\n updateNoteFrontmatter(relativePath: string, fields: Record<string, unknown>, overwrite = false): boolean {\n const fullPath = path.join(this.vaultDir, relativePath);\n let fileContent: string;\n try {\n fileContent = fs.readFileSync(fullPath, 'utf-8');\n } catch {\n return false;\n }\n\n const fmMatch = fileContent.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!fmMatch) return false;\n\n const parsed = YAML.parse(fmMatch[1]) as Record<string, unknown>;\n for (const [key, value] of Object.entries(fields)) {\n if (overwrite || parsed[key] === undefined) {\n parsed[key] = value;\n }\n }\n\n const body = fileContent.slice(fmMatch[0].length);\n const fmYaml = YAML.stringify(parsed, { defaultStringType: 'QUOTE_DOUBLE', defaultKeyType: 'PLAIN' }).trim();\n this.atomicWrite(fullPath, `---\\n${fmYaml}\\n---${body}`);\n return true;\n }\n\n /** @deprecated Use updateNoteFrontmatter instead */\n updateSessionFrontmatter(relativePath: string, fields: Record<string, unknown>): boolean {\n return this.updateNoteFrontmatter(relativePath, fields);\n }\n\n private writeMarkdown(relativePath: string, frontmatter: Record<string, unknown>, content: string): void {\n const fullPath = path.join(this.vaultDir, relativePath);\n fs.mkdirSync(path.dirname(fullPath), { recursive: true });\n\n const fmYaml = YAML.stringify(frontmatter, { defaultStringType: 'QUOTE_DOUBLE', defaultKeyType: 'PLAIN' }).trim();\n const file = `---\\n${fmYaml}\\n---\\n\\n${content}\\n`;\n this.atomicWrite(fullPath, file);\n }\n\n /** Write to a temp file then rename — prevents Obsidian from seeing a truncated file mid-write. */\n private atomicWrite(fullPath: string, content: string): void {\n const tmp = `${fullPath}.tmp`;\n fs.writeFileSync(tmp, content, 'utf-8');\n fs.renameSync(tmp, fullPath);\n }\n}\n"],"mappings":"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","names":["YAML"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/intelligence/ollama.ts","../src/intelligence/lm-studio.ts"],"sourcesContent":["import type { LlmProvider, EmbeddingProvider, LlmResponse, EmbeddingResponse, LlmRequestOptions } from './llm.js';\nimport { LLM_REQUEST_TIMEOUT_MS, EMBEDDING_REQUEST_TIMEOUT_MS, DAEMON_CLIENT_TIMEOUT_MS } from '../constants.js';\n\ninterface OllamaConfig {\n model?: string;\n base_url?: string;\n context_window?: number;\n max_tokens?: number;\n // Legacy fields (ignored, kept for backward compat during migration)\n embedding_model?: string;\n summary_model?: string;\n}\n\n// Ollama API endpoints\nconst ENDPOINT_GENERATE = '/api/generate';\nconst ENDPOINT_EMBED = '/api/embed';\nconst ENDPOINT_TAGS = '/api/tags';\n\nexport class OllamaBackend implements LlmProvider, EmbeddingProvider {\n static readonly DEFAULT_BASE_URL = 'http://localhost:11434';\n readonly name = 'ollama';\n private baseUrl: string;\n private model: string;\n private defaultMaxTokens: number;\n private contextWindow: number | undefined;\n\n constructor(config?: OllamaConfig) {\n this.baseUrl = config?.base_url ?? OllamaBackend.DEFAULT_BASE_URL;\n this.model = config?.model ?? config?.summary_model ?? 'llama3.2';\n this.defaultMaxTokens = config?.max_tokens ?? 1024;\n this.contextWindow = config?.context_window;\n }\n\n async summarize(prompt: string, opts?: LlmRequestOptions): Promise<LlmResponse> {\n const maxTokens = opts?.maxTokens ?? this.defaultMaxTokens;\n\n // Send num_ctx from config or per-call override. Ollama reloads the model\n // on num_ctx changes, but consistent values (same num_ctx every call)\n // only cause one reload on first use. Without this, Ollama falls back to\n // its model default (often 2048), ignoring the user's configured context.\n const contextLength = opts?.contextLength ?? this.contextWindow;\n const options: Record<string, unknown> = { num_predict: maxTokens };\n if (contextLength) {\n options.num_ctx = contextLength;\n }\n\n const body: Record<string, unknown> = {\n model: this.model,\n prompt,\n stream: false,\n options,\n };\n\n // System prompt — sent as a separate field instead of concatenated into prompt\n if (opts?.systemPrompt) {\n body.system = opts.systemPrompt;\n }\n\n // Thinking control — false suppresses chain-of-thought for reasoning models\n if (opts?.reasoning) {\n body.think = opts.reasoning === 'off' ? false : opts.reasoning;\n }\n\n // Keep model loaded between requests (useful for digest cycles)\n if (opts?.keepAlive) {\n body.keep_alive = opts.keepAlive;\n }\n\n const response = await fetch(`${this.baseUrl}${ENDPOINT_GENERATE}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(opts?.timeoutMs ?? LLM_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`Ollama summarize failed: ${response.status} ${errorBody.slice(0, 500)}`);\n }\n\n const data = await response.json() as { response: string; model: string };\n return { text: data.response, model: data.model };\n }\n\n async embed(text: string): Promise<EmbeddingResponse> {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_EMBED}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.model,\n input: text,\n }),\n signal: AbortSignal.timeout(EMBEDDING_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n throw new Error(`Ollama embed failed: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json() as { embeddings: number[][]; model: string };\n const embedding = data.embeddings[0];\n return { embedding, model: data.model, dimensions: embedding.length };\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_TAGS}`, {\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /** List available models on this Ollama instance. */\n async listModels(timeoutMs?: number): Promise<string[]> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_TAGS}`, {\n signal: AbortSignal.timeout(timeoutMs ?? DAEMON_CLIENT_TIMEOUT_MS),\n });\n const data = await response.json() as { models: Array<{ name: string }> };\n return data.models.map((m) => m.name);\n } catch {\n return [];\n }\n }\n}\n","import type { LlmProvider, EmbeddingProvider, LlmResponse, EmbeddingResponse, LlmRequestOptions } from './llm.js';\nimport { LLM_REQUEST_TIMEOUT_MS, EMBEDDING_REQUEST_TIMEOUT_MS, DAEMON_CLIENT_TIMEOUT_MS } from '../constants.js';\n\ninterface LmStudioConfig {\n model?: string;\n base_url?: string;\n context_window?: number;\n max_tokens?: number;\n // Legacy fields\n embedding_model?: string;\n summary_model?: string;\n}\n\n// LM Studio API endpoints\nconst ENDPOINT_CHAT = '/api/v1/chat';\nconst ENDPOINT_MODELS_LOAD = '/api/v1/models/load';\nconst ENDPOINT_MODELS_LIST = '/v1/models';\nconst ENDPOINT_MODELS_NATIVE = '/api/v1/models';\nconst ENDPOINT_EMBEDDINGS = '/v1/embeddings';\n\n/** Shape of a loaded instance from the LM Studio native models API. */\ninterface NativeLoadedInstance {\n id: string;\n config: {\n context_length: number;\n flash_attention: boolean;\n offload_kv_cache_to_gpu: boolean;\n };\n}\n\n/** Shape of a model entry from the LM Studio native models API. */\ninterface NativeModelEntry {\n type: string;\n key: string;\n loaded_instances: NativeLoadedInstance[];\n}\n\nexport class LmStudioBackend implements LlmProvider, EmbeddingProvider {\n static readonly DEFAULT_BASE_URL = 'http://localhost:1234';\n readonly name = 'lm-studio';\n private baseUrl: string;\n private model: string;\n private instanceId: string | null = null;\n private contextWindow: number | undefined;\n private defaultMaxTokens: number;\n\n constructor(config?: LmStudioConfig) {\n this.baseUrl = config?.base_url ?? LmStudioBackend.DEFAULT_BASE_URL;\n this.model = config?.model ?? config?.summary_model ?? 'llama3.2';\n this.contextWindow = config?.context_window;\n this.defaultMaxTokens = config?.max_tokens ?? 1024;\n }\n\n /**\n * Generate text using LM Studio's native REST API (/api/v1/chat).\n * Routes to our specific instance by ID when available, with model name +\n * context_length as fallback. This ensures correct routing when multiple\n * daemons share the same LM Studio, and graceful degradation when our\n * instance is evicted by idle TTL.\n */\n async summarize(prompt: string, opts?: LlmRequestOptions): Promise<LlmResponse> {\n const maxTokens = opts?.maxTokens ?? this.defaultMaxTokens;\n const contextLength = opts?.contextLength ?? this.contextWindow;\n\n const body: Record<string, unknown> = {\n model: this.instanceId ?? this.model,\n input: prompt,\n max_output_tokens: maxTokens,\n store: false,\n };\n\n // Always send context_length — even when routing by instance ID.\n // If our instance was evicted and LM Studio auto-loads, this ensures\n // the replacement gets the correct context window.\n if (contextLength) {\n body.context_length = contextLength;\n }\n\n // System prompt — sent separately from user content\n if (opts?.systemPrompt) {\n body.system_prompt = opts.systemPrompt;\n }\n\n // Reasoning control — 'off' suppresses chain-of-thought for reasoning models\n if (opts?.reasoning) {\n body.reasoning = opts.reasoning;\n }\n\n const response = await fetch(`${this.baseUrl}${ENDPOINT_CHAT}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(opts?.timeoutMs ?? LLM_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n // If our instance was evicted, clear the ID so ensureLoaded\n // reloads on the next cycle instead of hitting a stale ID repeatedly\n if (response.status === 404 && this.instanceId) {\n this.instanceId = null;\n }\n throw new Error(`LM Studio summarize failed: ${response.status} ${errorBody.slice(0, 500)}`);\n }\n\n const data = await response.json() as {\n model_instance_id: string;\n output: Array<{ type: string; content: string }>;\n };\n const messageOutput = data.output.find((o) => o.type === 'message');\n const text = messageOutput?.content ?? '';\n return { text, model: data.model_instance_id };\n }\n\n /**\n * Generate embeddings using LM Studio's OpenAI-compatible endpoint.\n * (The native API doesn't have an embedding endpoint — OpenAI-compat is fine here.)\n */\n async embed(text: string): Promise<EmbeddingResponse> {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_EMBEDDINGS}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.model,\n input: text,\n }),\n signal: AbortSignal.timeout(EMBEDDING_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n throw new Error(`LM Studio embed failed: ${response.status}`);\n }\n\n const data = await response.json() as {\n data: Array<{ embedding: number[] }>;\n model: string;\n };\n const embedding = data.data[0].embedding;\n return { embedding, model: data.model, dimensions: embedding.length };\n }\n\n /**\n * Ensure a model instance is loaded with the desired settings.\n * Called every digest cycle (not cached) so it recovers from idle TTL eviction.\n *\n * The load API is necessary to control offload_kv_cache_to_gpu — a load-time\n * setting that cannot be set per-request via the chat API.\n *\n * Multi-daemon safe: finds or loads our own compatible instance without\n * touching instances from other daemons/projects. Routes by instance ID.\n */\n async ensureLoaded(contextLength?: number, gpuKvCache?: boolean): Promise<void> {\n const ctx = contextLength ?? this.contextWindow;\n const kvCache = gpuKvCache ?? false;\n\n // Query native API for existing loaded instances of this model\n const instances = await this.getLoadedInstances();\n\n // Look for a compatible instance we can reuse (ours or anyone's)\n for (const instance of instances) {\n const matchesContext = !ctx || instance.config.context_length === ctx;\n const matchesKvCache = instance.config.offload_kv_cache_to_gpu === kvCache;\n if (matchesContext && matchesKvCache) {\n this.instanceId = instance.id;\n return;\n }\n }\n\n // No compatible instance — load our own (don't touch others)\n const body: Record<string, unknown> = {\n model: this.model,\n flash_attention: true,\n offload_kv_cache_to_gpu: kvCache,\n };\n if (ctx) {\n body.context_length = ctx;\n }\n\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_LOAD}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(LLM_REQUEST_TIMEOUT_MS),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`LM Studio model load failed: ${response.status} ${errorBody.slice(0, 200)}`);\n }\n\n const loadResult = await response.json() as Record<string, unknown>;\n const id = (loadResult.instance_id ?? loadResult.id ?? loadResult.model_instance_id) as string | undefined;\n if (id) {\n this.instanceId = id;\n }\n }\n\n /**\n * Query the LM Studio native API for loaded instances of this model.\n * Returns an empty array if the API is unavailable or the model has no loaded instances.\n */\n private async getLoadedInstances(): Promise<NativeLoadedInstance[]> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_NATIVE}`, {\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n if (!response.ok) return [];\n\n const data = await response.json() as { models: NativeModelEntry[] };\n const entry = data.models.find((m) => m.key === this.model);\n return entry?.loaded_instances ?? [];\n } catch {\n return [];\n }\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_LIST}`, {\n signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /** List available models on this LM Studio instance. */\n async listModels(timeoutMs?: number): Promise<string[]> {\n try {\n const response = await fetch(`${this.baseUrl}${ENDPOINT_MODELS_LIST}`, {\n signal: AbortSignal.timeout(timeoutMs ?? DAEMON_CLIENT_TIMEOUT_MS),\n });\n const data = await response.json() as { data: Array<{ id: string }> };\n return data.data.map((m) => m.id);\n } catch {\n return [];\n }\n }\n}\n"],"mappings":"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","names":[]}
|