@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.
- package/.claude/agents/analyst.md +198 -0
- package/.claude/agents/backend-developer.md +126 -0
- package/.claude/agents/brain-keeper.md +229 -0
- package/.claude/agents/code-reviewer.md +181 -0
- package/.claude/agents/database-engineer.md +94 -0
- package/.claude/agents/devops-engineer.md +141 -0
- package/.claude/agents/frontend-developer.md +97 -0
- package/.claude/agents/gate-keeper.md +118 -0
- package/.claude/agents/migrator.md +291 -0
- package/.claude/agents/mobile-developer.md +80 -0
- package/.claude/agents/n8n-specialist.md +94 -0
- package/.claude/agents/product-owner.md +115 -0
- package/.claude/agents/qa-engineer.md +232 -0
- package/.claude/agents/release-engineer.md +204 -0
- package/.claude/agents/scaffold.md +87 -0
- package/.claude/agents/security-engineer.md +199 -0
- package/.claude/agents/sprint-runner.md +44 -0
- package/.claude/agents/stack-resolver.md +84 -0
- package/.claude/agents/tech-lead.md +182 -0
- package/.claude/agents/update-template.md +54 -0
- package/.claude/agents/ux-designer.md +118 -0
- package/.claude/settings.json +44 -0
- package/.claude/skills/README.md +332 -0
- package/.claude/skills/active-project/SKILL.md +129 -0
- package/.claude/skills/api-integration-test/SKILL.md +64 -0
- package/.claude/skills/auto-test-guard/SKILL.md +237 -0
- package/.claude/skills/auto-test-guard/resources/backend-tests.md +20 -0
- package/.claude/skills/auto-test-guard/resources/e2e-tests.md +24 -0
- package/.claude/skills/auto-test-guard/resources/execution-report.md +49 -0
- package/.claude/skills/auto-test-guard/resources/frontend-tests.md +18 -0
- package/.claude/skills/auto-test-guard/resources/initial-setup.md +108 -0
- package/.claude/skills/auto-test-guard/resources/run-suite.md +48 -0
- package/.claude/skills/auto-test-guard/resources/senior-gate.md +19 -0
- package/.claude/skills/brain-keeper/SKILL.md +60 -0
- package/.claude/skills/brain-keeper/obsidian/app.json +9 -0
- package/.claude/skills/brain-keeper/obsidian/appearance.json +4 -0
- package/.claude/skills/brain-keeper/obsidian/core-plugins.json +20 -0
- package/.claude/skills/brain-keeper/obsidian/daily-notes.json +5 -0
- package/.claude/skills/brain-keeper/obsidian/graph.json +32 -0
- package/.claude/skills/brain-keeper/obsidian/snippets/folder-colors.css +90 -0
- package/.claude/skills/brain-keeper/obsidian/templates.json +5 -0
- package/.claude/skills/brain-keeper/templates/README.md +51 -0
- package/.claude/skills/brain-keeper/templates/adr.md +40 -0
- package/.claude/skills/brain-keeper/templates/bug.md +35 -0
- package/.claude/skills/brain-keeper/templates/daily.md +38 -0
- package/.claude/skills/brain-keeper/templates/feature.md +62 -0
- package/.claude/skills/brain-keeper/templates/meeting.md +34 -0
- package/.claude/skills/brain-keeper/templates/tech-debt.md +21 -0
- package/.claude/skills/caveman/SKILL.md +187 -0
- package/.claude/skills/create-stack-pack/SKILL.md +281 -0
- package/.claude/skills/grill-me/SKILL.md +79 -0
- package/.claude/skills/honcho-memory/SKILL.md +207 -0
- package/.claude/skills/honcho-memory/docs/api-endpoints-verified.md +75 -0
- package/.claude/skills/honcho-memory/hooks/on-prompt-submit.js +221 -0
- package/.claude/skills/honcho-memory/hooks/on-stop.js +193 -0
- package/.claude/skills/honcho-memory/lib/honcho-client.js +363 -0
- package/.claude/skills/honcho-memory/lib/memory-injector.js +93 -0
- package/.claude/skills/honcho-memory/package.json +32 -0
- package/.claude/skills/honcho-memory/scripts/cli.js +370 -0
- package/.claude/skills/honcho-memory/scripts/setup.js +109 -0
- package/.claude/skills/honcho-memory/tests/t001-api-endpoints-verified.test.js +89 -0
- package/.claude/skills/honcho-memory/tests/t002-structure.test.js +97 -0
- package/.claude/skills/honcho-memory/tests/t003-honcho-client.test.js +162 -0
- package/.claude/skills/honcho-memory/tests/t004-soft-delete.test.js +259 -0
- package/.claude/skills/honcho-memory/tests/t005-memory-injector.test.js +175 -0
- package/.claude/skills/honcho-memory/tests/t006-on-prompt-submit.test.js +215 -0
- package/.claude/skills/honcho-memory/tests/t007-on-stop.test.js +165 -0
- package/.claude/skills/honcho-memory/tests/t008-cli.test.js +214 -0
- package/.claude/skills/honcho-memory/tests/t009-setup.test.js +232 -0
- package/.claude/skills/honcho-memory/tests/t010-skill-md.test.js +114 -0
- package/.claude/skills/honcho-memory/tests/t011-settings-hooks.test.js +105 -0
- package/.claude/skills/honcho-memory/tests/t012-docs-update.test.js +106 -0
- package/.claude/skills/honcho-memory/tests/t013-smoke-e2e.test.js +90 -0
- package/.claude/skills/pair-debug/SKILL.md +288 -0
- package/.claude/skills/prd-ready-check/SKILL.md +58 -0
- package/.claude/skills/project-manager/SKILL.md +167 -0
- package/.claude/skills/quality-standards/SKILL.md +201 -0
- package/.claude/skills/quick-feature/SKILL.md +264 -0
- package/.claude/skills/run-sprint/SKILL.md +342 -0
- package/.claude/skills/scaffold/SKILL.md +58 -0
- package/.claude/skills/stack-discovery/SKILL.md +159 -0
- package/.claude/skills/test-coverage-auditor/SKILL.md +59 -0
- package/.claude/skills/to-issues/SKILL.md +163 -0
- package/.claude/skills/to-prd/SKILL.md +130 -0
- package/.claude/skills/update-template/SKILL.md +254 -0
- package/.claude/stacks/CODEOWNERS +30 -0
- package/.claude/stacks/README.md +88 -0
- package/.claude/stacks/_template.md +116 -0
- package/.claude/stacks/java/spring-boot-3.md +376 -0
- package/.claude/stacks/java/spring-boot-4.md +438 -0
- package/.claude/stacks/typescript/angular-18.md +420 -0
- package/.claude/stacks/typescript/angular-19.md +397 -0
- package/.claude/stacks/typescript/angular-21.md +494 -0
- package/CLAUDE.md +453 -0
- package/README.md +391 -0
- package/bin/cli.js +773 -0
- package/bin/lib/backup.js +62 -0
- package/bin/lib/detect-stack.js +476 -0
- package/bin/lib/help.js +233 -0
- package/bin/lib/identity.js +108 -0
- package/bin/lib/local-dir.js +69 -0
- package/bin/lib/manifest.js +236 -0
- package/bin/lib/sync-all.js +394 -0
- package/bin/lib/version-check.js +398 -0
- package/dashboard/db.js +199 -0
- package/dashboard/package.json +22 -0
- package/dashboard/public/app.js +709 -0
- package/dashboard/public/content/docs/agents-reference.en.md +911 -0
- package/dashboard/public/content/docs/architecture-overview.en.md +260 -0
- package/dashboard/public/content/docs/autonomy-matrix.en.md +186 -0
- package/dashboard/public/content/docs/git-flow.en.md +525 -0
- package/dashboard/public/content/docs/honcho-memory.en.md +394 -0
- package/dashboard/public/content/docs/hooks-reference.en.md +420 -0
- package/dashboard/public/content/docs/pipeline.en.md +400 -0
- package/dashboard/public/content/docs/quality-gate.en.md +315 -0
- package/dashboard/public/content/docs/skills-reference.en.md +500 -0
- package/dashboard/public/content/docs/stack-rules.en.md +362 -0
- package/dashboard/public/content/docs/troubleshooting.en.md +637 -0
- package/dashboard/public/content/manifest.json +102 -0
- package/dashboard/public/content/manual/backend.en.md +1138 -0
- package/dashboard/public/content/manual/existing-project.en.md +831 -0
- package/dashboard/public/content/manual/frontend.en.md +1065 -0
- package/dashboard/public/content/manual/fullstack.en.md +1508 -0
- package/dashboard/public/content/manual/mobile.en.md +866 -0
- package/dashboard/public/index.html +108 -0
- package/dashboard/public/style.css +610 -0
- package/dashboard/public/vendor/marked.min.js +69 -0
- package/dashboard/rtk.js +143 -0
- package/dashboard/server-app.js +403 -0
- package/dashboard/server.js +104 -0
- package/dashboard/test/sprint1.test.js +406 -0
- package/dashboard/test/sprint2.test.js +571 -0
- package/dashboard/test/sprint3.test.js +560 -0
- package/package.json +33 -0
- package/scripts/hooks/subagent-telemetry.sh +14 -0
- package/scripts/hooks/telemetry-writer.js +250 -0
- 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
|
+
}
|