@eltonssouza/development-utility-kit 1.0.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 (137) hide show
  1. package/.claude/agents/analyst.md +198 -0
  2. package/.claude/agents/backend-developer.md +126 -0
  3. package/.claude/agents/brain-keeper.md +229 -0
  4. package/.claude/agents/code-reviewer.md +181 -0
  5. package/.claude/agents/database-engineer.md +94 -0
  6. package/.claude/agents/devops-engineer.md +141 -0
  7. package/.claude/agents/frontend-developer.md +97 -0
  8. package/.claude/agents/gate-keeper.md +118 -0
  9. package/.claude/agents/migrator.md +291 -0
  10. package/.claude/agents/mobile-developer.md +80 -0
  11. package/.claude/agents/n8n-specialist.md +94 -0
  12. package/.claude/agents/product-owner.md +115 -0
  13. package/.claude/agents/qa-engineer.md +232 -0
  14. package/.claude/agents/release-engineer.md +204 -0
  15. package/.claude/agents/scaffold.md +87 -0
  16. package/.claude/agents/security-engineer.md +199 -0
  17. package/.claude/agents/sprint-runner.md +44 -0
  18. package/.claude/agents/stack-resolver.md +84 -0
  19. package/.claude/agents/tech-lead.md +182 -0
  20. package/.claude/agents/update-template.md +54 -0
  21. package/.claude/agents/ux-designer.md +118 -0
  22. package/.claude/settings.json +44 -0
  23. package/.claude/skills/README.md +332 -0
  24. package/.claude/skills/active-project/SKILL.md +129 -0
  25. package/.claude/skills/api-integration-test/SKILL.md +64 -0
  26. package/.claude/skills/auto-test-guard/SKILL.md +237 -0
  27. package/.claude/skills/auto-test-guard/resources/backend-tests.md +20 -0
  28. package/.claude/skills/auto-test-guard/resources/e2e-tests.md +24 -0
  29. package/.claude/skills/auto-test-guard/resources/execution-report.md +49 -0
  30. package/.claude/skills/auto-test-guard/resources/frontend-tests.md +18 -0
  31. package/.claude/skills/auto-test-guard/resources/initial-setup.md +108 -0
  32. package/.claude/skills/auto-test-guard/resources/run-suite.md +48 -0
  33. package/.claude/skills/auto-test-guard/resources/senior-gate.md +19 -0
  34. package/.claude/skills/brain-keeper/SKILL.md +60 -0
  35. package/.claude/skills/brain-keeper/obsidian/app.json +9 -0
  36. package/.claude/skills/brain-keeper/obsidian/appearance.json +4 -0
  37. package/.claude/skills/brain-keeper/obsidian/core-plugins.json +20 -0
  38. package/.claude/skills/brain-keeper/obsidian/daily-notes.json +5 -0
  39. package/.claude/skills/brain-keeper/obsidian/graph.json +32 -0
  40. package/.claude/skills/brain-keeper/obsidian/snippets/folder-colors.css +90 -0
  41. package/.claude/skills/brain-keeper/obsidian/templates.json +5 -0
  42. package/.claude/skills/brain-keeper/templates/README.md +51 -0
  43. package/.claude/skills/brain-keeper/templates/adr.md +40 -0
  44. package/.claude/skills/brain-keeper/templates/bug.md +35 -0
  45. package/.claude/skills/brain-keeper/templates/daily.md +38 -0
  46. package/.claude/skills/brain-keeper/templates/feature.md +62 -0
  47. package/.claude/skills/brain-keeper/templates/meeting.md +34 -0
  48. package/.claude/skills/brain-keeper/templates/tech-debt.md +21 -0
  49. package/.claude/skills/caveman/SKILL.md +187 -0
  50. package/.claude/skills/create-stack-pack/SKILL.md +281 -0
  51. package/.claude/skills/grill-me/SKILL.md +79 -0
  52. package/.claude/skills/honcho-memory/SKILL.md +207 -0
  53. package/.claude/skills/honcho-memory/docs/api-endpoints-verified.md +75 -0
  54. package/.claude/skills/honcho-memory/hooks/on-prompt-submit.js +221 -0
  55. package/.claude/skills/honcho-memory/hooks/on-stop.js +193 -0
  56. package/.claude/skills/honcho-memory/lib/honcho-client.js +363 -0
  57. package/.claude/skills/honcho-memory/lib/memory-injector.js +93 -0
  58. package/.claude/skills/honcho-memory/package.json +32 -0
  59. package/.claude/skills/honcho-memory/scripts/cli.js +370 -0
  60. package/.claude/skills/honcho-memory/scripts/setup.js +109 -0
  61. package/.claude/skills/honcho-memory/tests/t001-api-endpoints-verified.test.js +89 -0
  62. package/.claude/skills/honcho-memory/tests/t002-structure.test.js +97 -0
  63. package/.claude/skills/honcho-memory/tests/t003-honcho-client.test.js +162 -0
  64. package/.claude/skills/honcho-memory/tests/t004-soft-delete.test.js +259 -0
  65. package/.claude/skills/honcho-memory/tests/t005-memory-injector.test.js +175 -0
  66. package/.claude/skills/honcho-memory/tests/t006-on-prompt-submit.test.js +215 -0
  67. package/.claude/skills/honcho-memory/tests/t007-on-stop.test.js +165 -0
  68. package/.claude/skills/honcho-memory/tests/t008-cli.test.js +214 -0
  69. package/.claude/skills/honcho-memory/tests/t009-setup.test.js +232 -0
  70. package/.claude/skills/honcho-memory/tests/t010-skill-md.test.js +114 -0
  71. package/.claude/skills/honcho-memory/tests/t011-settings-hooks.test.js +105 -0
  72. package/.claude/skills/honcho-memory/tests/t012-docs-update.test.js +106 -0
  73. package/.claude/skills/honcho-memory/tests/t013-smoke-e2e.test.js +90 -0
  74. package/.claude/skills/pair-debug/SKILL.md +288 -0
  75. package/.claude/skills/prd-ready-check/SKILL.md +58 -0
  76. package/.claude/skills/project-manager/SKILL.md +167 -0
  77. package/.claude/skills/quality-standards/SKILL.md +201 -0
  78. package/.claude/skills/quick-feature/SKILL.md +264 -0
  79. package/.claude/skills/run-sprint/SKILL.md +342 -0
  80. package/.claude/skills/scaffold/SKILL.md +58 -0
  81. package/.claude/skills/stack-discovery/SKILL.md +159 -0
  82. package/.claude/skills/test-coverage-auditor/SKILL.md +59 -0
  83. package/.claude/skills/to-issues/SKILL.md +163 -0
  84. package/.claude/skills/to-prd/SKILL.md +130 -0
  85. package/.claude/skills/update-template/SKILL.md +254 -0
  86. package/.claude/stacks/CODEOWNERS +30 -0
  87. package/.claude/stacks/README.md +88 -0
  88. package/.claude/stacks/_template.md +116 -0
  89. package/.claude/stacks/java/spring-boot-3.md +376 -0
  90. package/.claude/stacks/java/spring-boot-4.md +438 -0
  91. package/.claude/stacks/typescript/angular-18.md +420 -0
  92. package/.claude/stacks/typescript/angular-19.md +397 -0
  93. package/.claude/stacks/typescript/angular-21.md +494 -0
  94. package/CLAUDE.md +453 -0
  95. package/README.md +391 -0
  96. package/bin/cli.js +773 -0
  97. package/bin/lib/backup.js +62 -0
  98. package/bin/lib/detect-stack.js +476 -0
  99. package/bin/lib/help.js +233 -0
  100. package/bin/lib/identity.js +108 -0
  101. package/bin/lib/local-dir.js +69 -0
  102. package/bin/lib/manifest.js +236 -0
  103. package/bin/lib/sync-all.js +394 -0
  104. package/bin/lib/version-check.js +398 -0
  105. package/dashboard/db.js +199 -0
  106. package/dashboard/package.json +22 -0
  107. package/dashboard/public/app.js +709 -0
  108. package/dashboard/public/content/docs/agents-reference.en.md +911 -0
  109. package/dashboard/public/content/docs/architecture-overview.en.md +260 -0
  110. package/dashboard/public/content/docs/autonomy-matrix.en.md +186 -0
  111. package/dashboard/public/content/docs/git-flow.en.md +525 -0
  112. package/dashboard/public/content/docs/honcho-memory.en.md +394 -0
  113. package/dashboard/public/content/docs/hooks-reference.en.md +420 -0
  114. package/dashboard/public/content/docs/pipeline.en.md +400 -0
  115. package/dashboard/public/content/docs/quality-gate.en.md +315 -0
  116. package/dashboard/public/content/docs/skills-reference.en.md +500 -0
  117. package/dashboard/public/content/docs/stack-rules.en.md +362 -0
  118. package/dashboard/public/content/docs/troubleshooting.en.md +637 -0
  119. package/dashboard/public/content/manifest.json +102 -0
  120. package/dashboard/public/content/manual/backend.en.md +1138 -0
  121. package/dashboard/public/content/manual/existing-project.en.md +831 -0
  122. package/dashboard/public/content/manual/frontend.en.md +1065 -0
  123. package/dashboard/public/content/manual/fullstack.en.md +1508 -0
  124. package/dashboard/public/content/manual/mobile.en.md +866 -0
  125. package/dashboard/public/index.html +108 -0
  126. package/dashboard/public/style.css +610 -0
  127. package/dashboard/public/vendor/marked.min.js +69 -0
  128. package/dashboard/rtk.js +143 -0
  129. package/dashboard/server-app.js +403 -0
  130. package/dashboard/server.js +104 -0
  131. package/dashboard/test/sprint1.test.js +406 -0
  132. package/dashboard/test/sprint2.test.js +571 -0
  133. package/dashboard/test/sprint3.test.js +560 -0
  134. package/package.json +33 -0
  135. package/scripts/hooks/subagent-telemetry.sh +14 -0
  136. package/scripts/hooks/telemetry-writer.js +250 -0
  137. package/scripts/latest-versions.json +56 -0
@@ -0,0 +1,363 @@
1
+ /**
2
+ * honcho-client.js
3
+ *
4
+ * Honcho v3 REST client using native fetch (Bun/Node 18+).
5
+ * All public functions return null on any error — never throw to caller.
6
+ * API: https://honcho.pazzne.cloud (verified via /openapi.json 2026-05-26)
7
+ *
8
+ * Key deviations from DISCOVERY doc (see docs/api-endpoints-verified.md):
9
+ * - No "apps" or "users" concept — workspace = app, peer = user identity
10
+ * - Individual message DELETE does not exist; deleteSession deletes the session
11
+ * - Soft-delete: PUT /messages/{id} with body { metadata: { deleted: true } }
12
+ * - listMemories uses POST /messages/list (not GET)
13
+ * - saveMemory uses batch endpoint with required peer_id per message
14
+ * - listMemories and searchMemories filter out metadata.deleted = true client-side
15
+ */
16
+
17
+ import { readFileSync, existsSync } from "fs";
18
+ import { join } from "path";
19
+
20
+ const CONFIG_PATH = "C:\\Users\\elton\\.honcho\\config.json";
21
+
22
+ /**
23
+ * Load and parse config from C:\Users\elton\.honcho\config.json.
24
+ * Returns null if file missing or unparseable.
25
+ * apiKey is never logged.
26
+ */
27
+ export function loadConfig() {
28
+ try {
29
+ if (!existsSync(CONFIG_PATH)) {
30
+ return null;
31
+ }
32
+ const raw = readFileSync(CONFIG_PATH, "utf8");
33
+ const cfg = JSON.parse(raw);
34
+ if (!cfg.apiKey || !cfg.endpoint?.baseUrl) {
35
+ return null;
36
+ }
37
+ return cfg;
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Create an AbortController-backed timeout promise.
45
+ * Returns { controller, timeoutId, signal }.
46
+ */
47
+ function makeTimeout(ms) {
48
+ const controller = new AbortController();
49
+ const timeoutId = setTimeout(() => controller.abort(), ms);
50
+ return { controller, timeoutId, signal: controller.signal };
51
+ }
52
+
53
+ /**
54
+ * Execute a fetch with timeout. Returns Response or null on error/timeout.
55
+ * Never throws.
56
+ */
57
+ async function fetchSafe(url, options, timeoutMs) {
58
+ const { signal, timeoutId } = makeTimeout(timeoutMs);
59
+ try {
60
+ const resp = await fetch(url, { ...options, signal });
61
+ clearTimeout(timeoutId);
62
+ return resp;
63
+ } catch {
64
+ clearTimeout(timeoutId);
65
+ return null;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Parse JSON body from response. Returns null on failure.
71
+ */
72
+ async function parseJson(resp) {
73
+ if (!resp) return null;
74
+ try {
75
+ if (!resp.ok) return null;
76
+ return await resp.json();
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Create a Honcho v3 client bound to a specific base URL, API key, and timeout.
84
+ *
85
+ * @param {object} opts
86
+ * @param {string} opts.baseUrl - e.g. "https://honcho.pazzne.cloud/v3"
87
+ * @param {string} opts.apiKey - bearer token (never logged)
88
+ * @param {number} [opts.timeoutMs=450] - request timeout in milliseconds
89
+ */
90
+ export function createClient({ baseUrl, apiKey, timeoutMs = 450 }) {
91
+ const base = baseUrl.replace(/\/$/, "");
92
+
93
+ function headers() {
94
+ return {
95
+ Authorization: `Bearer ${apiKey}`,
96
+ "Content-Type": "application/json",
97
+ Accept: "application/json",
98
+ };
99
+ }
100
+
101
+ /**
102
+ * GET or CREATE a workspace (maps to "app" in DISCOVERY terminology).
103
+ * POST /v3/workspaces with idempotent id.
104
+ *
105
+ * @param {string} workspaceId - workspace identifier (^[a-zA-Z0-9_-]+$)
106
+ * @returns {object|null} workspace object or null on error
107
+ */
108
+ async function getOrCreateApp(workspaceId) {
109
+ const resp = await fetchSafe(
110
+ `${base}/workspaces`,
111
+ {
112
+ method: "POST",
113
+ headers: headers(),
114
+ body: JSON.stringify({ id: workspaceId }),
115
+ },
116
+ timeoutMs
117
+ );
118
+ return parseJson(resp);
119
+ }
120
+
121
+ /**
122
+ * GET or CREATE a session within a workspace.
123
+ * POST /v3/workspaces/{workspaceId}/sessions with idempotent id.
124
+ *
125
+ * @param {string} workspaceId
126
+ * @param {string} sessionId - session identifier (per-directory, derived from CWD)
127
+ * @param {object} [metadata={}] - optional metadata
128
+ * @returns {object|null}
129
+ */
130
+ async function getOrCreateSession(workspaceId, sessionId, metadata = {}) {
131
+ const resp = await fetchSafe(
132
+ `${base}/workspaces/${encodeURIComponent(workspaceId)}/sessions`,
133
+ {
134
+ method: "POST",
135
+ headers: headers(),
136
+ body: JSON.stringify({ id: sessionId, metadata }),
137
+ },
138
+ timeoutMs
139
+ );
140
+ return parseJson(resp);
141
+ }
142
+
143
+ /**
144
+ * Save a memory (message) into a session.
145
+ * POST /v3/workspaces/{ws}/sessions/{session}/messages (batch endpoint).
146
+ *
147
+ * @param {string} workspaceId
148
+ * @param {string} sessionId
149
+ * @param {string} peerId - peer/user identity (peerName from config)
150
+ * @param {string} content - memory text content
151
+ * @param {object} [metadata={}] - e.g. { type: "explicit", source: "user", ... }
152
+ * @returns {object|null} created message batch result or null on error
153
+ */
154
+ async function saveMemory(workspaceId, sessionId, peerId, content, metadata = {}) {
155
+ const resp = await fetchSafe(
156
+ `${base}/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}/messages`,
157
+ {
158
+ method: "POST",
159
+ headers: headers(),
160
+ body: JSON.stringify({
161
+ messages: [
162
+ {
163
+ content,
164
+ peer_id: peerId,
165
+ metadata,
166
+ },
167
+ ],
168
+ }),
169
+ },
170
+ timeoutMs
171
+ );
172
+ return parseJson(resp);
173
+ }
174
+
175
+ /**
176
+ * Search memories semantically for a peer within a workspace.
177
+ * POST /v3/workspaces/{ws}/peers/{peerId}/search
178
+ * Client-side post-filter removes any message with metadata.deleted = true (BR-008).
179
+ *
180
+ * @param {string} workspaceId
181
+ * @param {string} peerId
182
+ * @param {string} query - search query text
183
+ * @param {number} [limit=5] - max results (1-100)
184
+ * @returns {object|null} search results (items filtered) or null on error
185
+ */
186
+ async function searchMemories(workspaceId, peerId, query, limit = 5) {
187
+ const resp = await fetchSafe(
188
+ `${base}/workspaces/${encodeURIComponent(workspaceId)}/peers/${encodeURIComponent(peerId)}/search`,
189
+ {
190
+ method: "POST",
191
+ headers: headers(),
192
+ body: JSON.stringify({ query, limit }),
193
+ },
194
+ timeoutMs
195
+ );
196
+ const data = await parseJson(resp);
197
+ if (!data) return null;
198
+ // Filter soft-deleted items client-side (BR-008)
199
+ if (Array.isArray(data.items)) {
200
+ data.items = data.items.filter((m) => m.metadata?.deleted !== true);
201
+ }
202
+ return data;
203
+ }
204
+
205
+ /**
206
+ * List messages (memories) in a session.
207
+ * POST /v3/workspaces/{ws}/sessions/{session}/messages/list
208
+ * Client-side post-filter removes any message with metadata.deleted = true (BR-008).
209
+ *
210
+ * @param {string} workspaceId
211
+ * @param {string} sessionId
212
+ * @param {object} [filters={}] - optional filters
213
+ * @returns {object|null} Page[Message] (items filtered) or null on error
214
+ */
215
+ async function listMemories(workspaceId, sessionId, filters = {}) {
216
+ const body = Object.keys(filters).length > 0 ? { filters } : {};
217
+ const resp = await fetchSafe(
218
+ `${base}/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}/messages/list`,
219
+ {
220
+ method: "POST",
221
+ headers: headers(),
222
+ body: JSON.stringify(body),
223
+ },
224
+ timeoutMs
225
+ );
226
+ const data = await parseJson(resp);
227
+ if (!data) return null;
228
+ // Filter soft-deleted items client-side (BR-008)
229
+ if (Array.isArray(data.items)) {
230
+ data.items = data.items.filter((m) => m.metadata?.deleted !== true);
231
+ }
232
+ return data;
233
+ }
234
+
235
+ /**
236
+ * Delete a session (and all its messages).
237
+ * DELETE /v3/workspaces/{ws}/sessions/{sessionId}
238
+ *
239
+ * @param {string} workspaceId
240
+ * @param {string} sessionId
241
+ * @returns {object|null} result or null on error
242
+ */
243
+ async function deleteMemory(workspaceId, sessionId) {
244
+ const resp = await fetchSafe(
245
+ `${base}/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}`,
246
+ {
247
+ method: "DELETE",
248
+ headers: headers(),
249
+ },
250
+ timeoutMs
251
+ );
252
+ if (!resp) return null;
253
+ try {
254
+ // 204 No Content is a success response for DELETE
255
+ if (resp.status === 204 || resp.ok) {
256
+ return { deleted: true, sessionId };
257
+ }
258
+ return null;
259
+ } catch {
260
+ return null;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Soft-delete a specific message by setting metadata.deleted = true.
266
+ * PUT /v3/workspaces/{ws}/sessions/{sessionId}/messages/{messageId}
267
+ * body: { "metadata": { "deleted": true } }
268
+ *
269
+ * @param {string} workspaceId
270
+ * @param {string} sessionId
271
+ * @param {string} messageId
272
+ * @returns {object|null} updated message or null on error
273
+ */
274
+ async function softDeleteMemory(workspaceId, sessionId, messageId) {
275
+ const resp = await fetchSafe(
276
+ `${base}/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}/messages/${encodeURIComponent(messageId)}`,
277
+ {
278
+ method: "PUT",
279
+ headers: headers(),
280
+ body: JSON.stringify({ metadata: { deleted: true } }),
281
+ },
282
+ timeoutMs
283
+ );
284
+ return parseJson(resp);
285
+ }
286
+
287
+ return {
288
+ getOrCreateApp,
289
+ getOrCreateSession,
290
+ saveMemory,
291
+ searchMemories,
292
+ listMemories,
293
+ deleteMemory,
294
+ softDeleteMemory,
295
+ };
296
+ }
297
+
298
+ // Module-level convenience functions that read config automatically.
299
+ // These are the "standalone" exports that tests can call directly.
300
+
301
+ /** @returns {object|null} */
302
+ export async function getOrCreateApp(workspaceId) {
303
+ const cfg = loadConfig();
304
+ if (!cfg) return null;
305
+ const client = createClient({ baseUrl: cfg.endpoint.baseUrl, apiKey: cfg.apiKey, timeoutMs: cfg.timeoutMs ?? 450 });
306
+ return client.getOrCreateApp(workspaceId);
307
+ }
308
+
309
+ /** @returns {object|null} */
310
+ export async function getOrCreateSession(workspaceId, sessionId, metadata = {}) {
311
+ const cfg = loadConfig();
312
+ if (!cfg) return null;
313
+ const client = createClient({ baseUrl: cfg.endpoint.baseUrl, apiKey: cfg.apiKey, timeoutMs: cfg.timeoutMs ?? 450 });
314
+ return client.getOrCreateSession(workspaceId, sessionId, metadata);
315
+ }
316
+
317
+ /** @returns {object|null} */
318
+ export async function saveMemory(workspaceId, sessionId, peerId, content, metadata = {}) {
319
+ const cfg = loadConfig();
320
+ if (!cfg) return null;
321
+ const client = createClient({ baseUrl: cfg.endpoint.baseUrl, apiKey: cfg.apiKey, timeoutMs: cfg.timeoutMs ?? 450 });
322
+ return client.saveMemory(workspaceId, sessionId, peerId, content, metadata);
323
+ }
324
+
325
+ /** @returns {object|null} */
326
+ export async function searchMemories(workspaceId, peerId, query, limit = 5) {
327
+ const cfg = loadConfig();
328
+ if (!cfg) return null;
329
+ const client = createClient({ baseUrl: cfg.endpoint.baseUrl, apiKey: cfg.apiKey, timeoutMs: cfg.timeoutMs ?? 450 });
330
+ return client.searchMemories(workspaceId, peerId, query, limit);
331
+ }
332
+
333
+ /** @returns {object|null} */
334
+ export async function listMemories(workspaceId, sessionId, filters = {}) {
335
+ const cfg = loadConfig();
336
+ if (!cfg) return null;
337
+ const client = createClient({ baseUrl: cfg.endpoint.baseUrl, apiKey: cfg.apiKey, timeoutMs: cfg.timeoutMs ?? 450 });
338
+ return client.listMemories(workspaceId, sessionId, filters);
339
+ }
340
+
341
+ /** @returns {object|null} */
342
+ export async function deleteMemory(workspaceId, sessionId) {
343
+ const cfg = loadConfig();
344
+ if (!cfg) return null;
345
+ const client = createClient({ baseUrl: cfg.endpoint.baseUrl, apiKey: cfg.apiKey, timeoutMs: cfg.timeoutMs ?? 450 });
346
+ return client.deleteMemory(workspaceId, sessionId);
347
+ }
348
+
349
+ /**
350
+ * Soft-delete a specific message by setting metadata.deleted = true.
351
+ * PUT /v3/workspaces/{ws}/sessions/{sessionId}/messages/{messageId}
352
+ *
353
+ * @param {string} workspaceId
354
+ * @param {string} sessionId
355
+ * @param {string} messageId
356
+ * @returns {object|null} updated message or null on error
357
+ */
358
+ export async function softDeleteMemory(workspaceId, sessionId, messageId) {
359
+ const cfg = loadConfig();
360
+ if (!cfg) return null;
361
+ const client = createClient({ baseUrl: cfg.endpoint.baseUrl, apiKey: cfg.apiKey, timeoutMs: cfg.timeoutMs ?? 450 });
362
+ return client.softDeleteMemory(workspaceId, sessionId, messageId);
363
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * memory-injector.js
3
+ *
4
+ * Formats a list of MemoryFact objects into a [HONCHO MEMORY] block
5
+ * suitable for injection into a Claude Code system prompt extension.
6
+ *
7
+ * Rules (per plan):
8
+ * - FR-005: builds [HONCHO MEMORY]...[/HONCHO MEMORY] block with metadata
9
+ * - BR-007: total block limited to 2000 characters
10
+ * - NFR-006: prefix = [HONCHO MEMORY], suffix = [/HONCHO MEMORY]
11
+ */
12
+
13
+ const MAX_CHARS = 2000;
14
+ const OPEN_TAG = "[HONCHO MEMORY]";
15
+ const CLOSE_TAG = "[/HONCHO MEMORY]";
16
+
17
+ /**
18
+ * @typedef {object} MemoryFact
19
+ * @property {string} content - memory text content
20
+ * @property {object} [metadata] - associated metadata
21
+ * @property {string} [metadata.type] - "explicit" | "inferred" | "project-context"
22
+ * @property {string} [metadata.timestamp] - ISO-8601 timestamp
23
+ * @property {string} [metadata.project] - project name
24
+ * @property {string} [metadata.source] - "user" | "inference"
25
+ */
26
+
27
+ /**
28
+ * Format a single MemoryFact into a text block line.
29
+ * @param {MemoryFact} memory
30
+ * @param {number} index - 1-based index
31
+ * @returns {string}
32
+ */
33
+ function formatMemory(memory, index) {
34
+ const meta = memory.metadata ?? {};
35
+ const parts = [];
36
+
37
+ if (meta.type) parts.push(`type:${meta.type}`);
38
+ if (meta.timestamp) parts.push(`ts:${meta.timestamp}`);
39
+ if (meta.project) parts.push(`project:${meta.project}`);
40
+
41
+ const metaStr = parts.length > 0 ? ` [${parts.join(" | ")}]` : "";
42
+ return `${index}. ${memory.content}${metaStr}`;
43
+ }
44
+
45
+ /**
46
+ * Build the [HONCHO MEMORY] injection block from an array of MemoryFact objects.
47
+ * Returns an empty string when memories array is empty.
48
+ * Total output is capped at MAX_CHARS (2000) — memories are truncated to fit.
49
+ *
50
+ * @param {MemoryFact[]} memories - ordered list of memory facts to inject
51
+ * @returns {string} formatted block or empty string
52
+ */
53
+ export function buildContextBlock(memories) {
54
+ if (!Array.isArray(memories) || memories.length === 0) {
55
+ return "";
56
+ }
57
+
58
+ const lines = [];
59
+ for (let i = 0; i < memories.length; i++) {
60
+ lines.push(formatMemory(memories[i], i + 1));
61
+ }
62
+
63
+ const inner = lines.join("\n");
64
+ const full = `${OPEN_TAG}\n${inner}\n${CLOSE_TAG}`;
65
+
66
+ if (full.length <= MAX_CHARS) {
67
+ return full;
68
+ }
69
+
70
+ // Truncate: build block greedily, memory by memory, until limit reached
71
+ let accumulated = `${OPEN_TAG}\n`;
72
+ const closingNeeded = `\n${CLOSE_TAG}`;
73
+
74
+ for (let i = 0; i < lines.length; i++) {
75
+ const lineToAdd = (i === 0 ? "" : "\n") + lines[i];
76
+ const projected = accumulated + lineToAdd + closingNeeded;
77
+ if (projected.length <= MAX_CHARS) {
78
+ accumulated += lineToAdd;
79
+ } else {
80
+ // Try to fit a truncated version of this line
81
+ const budgetForLine = MAX_CHARS - accumulated.length - closingNeeded.length - (i === 0 ? 0 : 1);
82
+ if (budgetForLine > 10) {
83
+ // Enough room to add a partial line
84
+ const prefix = i === 0 ? "" : "\n";
85
+ const truncated = lines[i].slice(0, budgetForLine);
86
+ accumulated += prefix + truncated;
87
+ }
88
+ break;
89
+ }
90
+ }
91
+
92
+ return accumulated + closingNeeded;
93
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "honcho-memory",
3
+ "version": "0.1.0",
4
+ "description": "Honcho v3 memory integration skill for Claude Code — semantic memory persistence across sessions",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "bun run tests/t001-api-endpoints-verified.test.js && bun run tests/t002-structure.test.js && bun run tests/t003-honcho-client.test.js && bun run tests/t004-soft-delete.test.js && bun run tests/t005-memory-injector.test.js && bun run tests/t006-on-prompt-submit.test.js && bun run tests/t007-on-stop.test.js && bun run tests/t008-cli.test.js && bun run tests/t009-setup.test.js && bun run tests/t010-skill-md.test.js && bun run tests/t011-settings-hooks.test.js && bun run tests/t012-docs-update.test.js && bun run tests/t013-smoke-e2e.test.js",
8
+ "test:sprint0": "bun run tests/t001-api-endpoints-verified.test.js && bun run tests/t002-structure.test.js && bun run tests/t003-honcho-client.test.js",
9
+ "test:sprint1": "bun run tests/t004-soft-delete.test.js && bun run tests/t005-memory-injector.test.js && bun run tests/t006-on-prompt-submit.test.js && bun run tests/t007-on-stop.test.js",
10
+ "test:sprint2": "bun run tests/t008-cli.test.js && bun run tests/t009-setup.test.js",
11
+ "test:sprint3": "bun run tests/t010-skill-md.test.js && bun run tests/t011-settings-hooks.test.js && bun run tests/t012-docs-update.test.js && bun run tests/t013-smoke-e2e.test.js",
12
+ "test:t001": "bun run tests/t001-api-endpoints-verified.test.js",
13
+ "test:t002": "bun run tests/t002-structure.test.js",
14
+ "test:t003": "bun run tests/t003-honcho-client.test.js",
15
+ "test:t004": "bun run tests/t004-soft-delete.test.js",
16
+ "test:t005": "bun run tests/t005-memory-injector.test.js",
17
+ "test:t006": "bun run tests/t006-on-prompt-submit.test.js",
18
+ "test:t007": "bun run tests/t007-on-stop.test.js",
19
+ "test:t008": "bun run tests/t008-cli.test.js",
20
+ "test:t009": "bun run tests/t009-setup.test.js",
21
+ "test:t010": "bun run tests/t010-skill-md.test.js",
22
+ "test:t011": "bun run tests/t011-settings-hooks.test.js",
23
+ "test:t012": "bun run tests/t012-docs-update.test.js",
24
+ "test:t013": "bun run tests/t013-smoke-e2e.test.js",
25
+ "setup": "bun run scripts/setup.js",
26
+ "setup:dry": "bun run scripts/setup.js --dry-run",
27
+ "status": "bun run scripts/cli.js status"
28
+ },
29
+ "engines": {
30
+ "bun": ">=1.3.14"
31
+ }
32
+ }