@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,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli.js — Honcho memory skill CLI dispatcher
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* status — print connection status, memory count, enabled flags
|
|
6
|
+
* save <text> — save an explicit memory, print the ID
|
|
7
|
+
* search <query> — search memories (peer-scoped), print top-N formatted
|
|
8
|
+
* list — list memories for the current session (sha256(CWD)[:16])
|
|
9
|
+
* forget <id> — soft-delete memory via PUT metadata.deleted=true; exit 1 if no ID
|
|
10
|
+
*
|
|
11
|
+
* Env vars:
|
|
12
|
+
* HONCHO_DRY_RUN=1 — skip real Honcho calls; emit simulated output
|
|
13
|
+
* HONCHO_BASE_URL — override base URL from config
|
|
14
|
+
* HONCHO_TIMEOUT_MS — override timeout in ms
|
|
15
|
+
*
|
|
16
|
+
* Exit codes:
|
|
17
|
+
* 0 — success
|
|
18
|
+
* 1 — error (e.g. forget without ID, unknown subcommand, fatal)
|
|
19
|
+
*
|
|
20
|
+
* API key is never printed to stdout/stderr.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { createHash } from "crypto";
|
|
24
|
+
import { createClient, loadConfig } from "../lib/honcho-client.js";
|
|
25
|
+
|
|
26
|
+
const DRY_RUN = process.env.HONCHO_DRY_RUN === "1";
|
|
27
|
+
|
|
28
|
+
/** Derive session ID from CWD: sha256(CWD)[:16] */
|
|
29
|
+
function deriveSessionId(cwd) {
|
|
30
|
+
return createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function log(msg) {
|
|
34
|
+
process.stdout.write(msg + "\n");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function warn(msg) {
|
|
38
|
+
process.stderr.write("HONCHO_WARN: " + msg + "\n");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Build effective config from file + env overrides. Returns null if missing/invalid. */
|
|
42
|
+
function getEffectiveConfig() {
|
|
43
|
+
const cfg = loadConfig();
|
|
44
|
+
|
|
45
|
+
if (process.env.HONCHO_BASE_URL) {
|
|
46
|
+
return {
|
|
47
|
+
apiKey: cfg?.apiKey ?? "dry-run-key",
|
|
48
|
+
peerName: cfg?.peerName ?? "elton",
|
|
49
|
+
workspace: cfg?.workspace ?? "claude_code",
|
|
50
|
+
endpoint: { baseUrl: process.env.HONCHO_BASE_URL },
|
|
51
|
+
enabled: cfg?.enabled !== false,
|
|
52
|
+
topN: cfg?.topN ?? 5,
|
|
53
|
+
timeoutMs: process.env.HONCHO_TIMEOUT_MS
|
|
54
|
+
? parseInt(process.env.HONCHO_TIMEOUT_MS, 10)
|
|
55
|
+
: (cfg?.timeoutMs ?? 450),
|
|
56
|
+
inferenceEnabled: cfg?.inferenceEnabled ?? false,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!cfg) return null;
|
|
61
|
+
|
|
62
|
+
if (process.env.HONCHO_TIMEOUT_MS) {
|
|
63
|
+
cfg.timeoutMs = parseInt(process.env.HONCHO_TIMEOUT_MS, 10);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return cfg;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Subcommand handlers
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
async function cmdStatus() {
|
|
74
|
+
if (DRY_RUN) {
|
|
75
|
+
const cfg = loadConfig();
|
|
76
|
+
log("connection: DRY_RUN");
|
|
77
|
+
log("memories: 0");
|
|
78
|
+
log(`enabled: ${cfg?.enabled !== false ? "true" : "false"}`);
|
|
79
|
+
log(`inferenceEnabled: ${cfg?.inferenceEnabled === true ? "true" : "false"}`);
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const cfg = getEffectiveConfig();
|
|
84
|
+
if (!cfg) {
|
|
85
|
+
log("connection: FAIL");
|
|
86
|
+
log("memories: 0");
|
|
87
|
+
log("enabled: false");
|
|
88
|
+
log("inferenceEnabled: false");
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const client = createClient({
|
|
93
|
+
baseUrl: cfg.endpoint.baseUrl,
|
|
94
|
+
apiKey: cfg.apiKey,
|
|
95
|
+
timeoutMs: cfg.timeoutMs ?? 450,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const workspace = cfg.workspace ?? "claude_code";
|
|
99
|
+
const cwd = process.cwd();
|
|
100
|
+
const sessionId = deriveSessionId(cwd);
|
|
101
|
+
|
|
102
|
+
// Test connectivity by attempting to get/create app
|
|
103
|
+
const appResult = await client.getOrCreateApp(workspace);
|
|
104
|
+
const connected = appResult !== null;
|
|
105
|
+
|
|
106
|
+
// Count memories for the current session
|
|
107
|
+
let memCount = 0;
|
|
108
|
+
if (connected) {
|
|
109
|
+
const listResult = await client.listMemories(workspace, sessionId, {});
|
|
110
|
+
if (listResult && Array.isArray(listResult.items)) {
|
|
111
|
+
memCount = listResult.items.length;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
log(`connection: ${connected ? "OK" : "FAIL"}`);
|
|
116
|
+
log(`memories: ${memCount}`);
|
|
117
|
+
log(`enabled: ${cfg.enabled !== false ? "true" : "false"}`);
|
|
118
|
+
log(`inferenceEnabled: ${cfg.inferenceEnabled === true ? "true" : "false"}`);
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function cmdSave(text) {
|
|
123
|
+
const cwd = process.cwd();
|
|
124
|
+
const sessionId = deriveSessionId(cwd);
|
|
125
|
+
const project = cwd.split(/[/\\]/).pop() ?? "unknown";
|
|
126
|
+
|
|
127
|
+
if (DRY_RUN) {
|
|
128
|
+
const dryId = `dry-${Date.now()}`;
|
|
129
|
+
log(JSON.stringify({
|
|
130
|
+
dryRun: true,
|
|
131
|
+
action: "save",
|
|
132
|
+
id: dryId,
|
|
133
|
+
content: (text ?? "").slice(0, 100),
|
|
134
|
+
}));
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const cfg = getEffectiveConfig();
|
|
139
|
+
if (!cfg) {
|
|
140
|
+
warn("No valid config found — cannot save memory");
|
|
141
|
+
process.exit(0);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const workspace = cfg.workspace ?? "claude_code";
|
|
145
|
+
const peerId = cfg.peerName ?? "elton";
|
|
146
|
+
|
|
147
|
+
const client = createClient({
|
|
148
|
+
baseUrl: cfg.endpoint.baseUrl,
|
|
149
|
+
apiKey: cfg.apiKey,
|
|
150
|
+
timeoutMs: cfg.timeoutMs ?? 450,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const result = await client.saveMemory(workspace, sessionId, peerId, text ?? "", {
|
|
154
|
+
type: "explicit",
|
|
155
|
+
source: "user",
|
|
156
|
+
project,
|
|
157
|
+
directory: cwd,
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (result) {
|
|
162
|
+
const id = result.id ?? result.items?.[0]?.id ?? "unknown";
|
|
163
|
+
log(`Saved memory. ID: ${id}`);
|
|
164
|
+
} else {
|
|
165
|
+
warn("Failed to save memory — Honcho may be unreachable");
|
|
166
|
+
}
|
|
167
|
+
process.exit(0);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function cmdList() {
|
|
171
|
+
const cwd = process.cwd();
|
|
172
|
+
const sessionId = deriveSessionId(cwd);
|
|
173
|
+
|
|
174
|
+
if (DRY_RUN) {
|
|
175
|
+
log(JSON.stringify({
|
|
176
|
+
dryRun: true,
|
|
177
|
+
action: "list",
|
|
178
|
+
sessionId,
|
|
179
|
+
items: [],
|
|
180
|
+
}));
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const cfg = getEffectiveConfig();
|
|
185
|
+
if (!cfg) {
|
|
186
|
+
warn("No valid config found — cannot list memories");
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const workspace = cfg.workspace ?? "claude_code";
|
|
191
|
+
|
|
192
|
+
const client = createClient({
|
|
193
|
+
baseUrl: cfg.endpoint.baseUrl,
|
|
194
|
+
apiKey: cfg.apiKey,
|
|
195
|
+
timeoutMs: cfg.timeoutMs ?? 450,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const result = await client.listMemories(workspace, sessionId, {});
|
|
199
|
+
if (!result) {
|
|
200
|
+
warn("Failed to list memories — Honcho may be unreachable");
|
|
201
|
+
process.exit(0);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const items = result.items ?? [];
|
|
205
|
+
if (items.length === 0) {
|
|
206
|
+
log("No memories found for current session.");
|
|
207
|
+
process.exit(0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (let i = 0; i < items.length; i++) {
|
|
211
|
+
const m = items[i];
|
|
212
|
+
const meta = m.metadata ?? {};
|
|
213
|
+
const ts = meta.timestamp ?? "";
|
|
214
|
+
const type = meta.type ?? "unknown";
|
|
215
|
+
log(`[${i + 1}] id:${m.id ?? "?"} type:${type} ts:${ts}`);
|
|
216
|
+
log(` ${m.content ?? ""}`);
|
|
217
|
+
}
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function cmdSearch(query) {
|
|
222
|
+
const cfg = getEffectiveConfig();
|
|
223
|
+
const cwd = process.cwd();
|
|
224
|
+
|
|
225
|
+
if (DRY_RUN) {
|
|
226
|
+
log(JSON.stringify({
|
|
227
|
+
dryRun: true,
|
|
228
|
+
action: "search",
|
|
229
|
+
query: (query ?? "").slice(0, 100),
|
|
230
|
+
items: [],
|
|
231
|
+
}));
|
|
232
|
+
process.exit(0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!cfg) {
|
|
236
|
+
warn("No valid config found — cannot search memories");
|
|
237
|
+
process.exit(0);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const workspace = cfg.workspace ?? "claude_code";
|
|
241
|
+
const peerId = cfg.peerName ?? "elton";
|
|
242
|
+
const topN = cfg.topN ?? 5;
|
|
243
|
+
|
|
244
|
+
const client = createClient({
|
|
245
|
+
baseUrl: cfg.endpoint.baseUrl,
|
|
246
|
+
apiKey: cfg.apiKey,
|
|
247
|
+
timeoutMs: cfg.timeoutMs ?? 450,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const result = await client.searchMemories(workspace, peerId, query ?? "", topN);
|
|
251
|
+
if (!result) {
|
|
252
|
+
warn("Failed to search memories — Honcho may be unreachable");
|
|
253
|
+
process.exit(0);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const items = result.items ?? [];
|
|
257
|
+
if (items.length === 0) {
|
|
258
|
+
log("No memories found matching your query.");
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (let i = 0; i < items.length; i++) {
|
|
263
|
+
const m = items[i];
|
|
264
|
+
const meta = m.metadata ?? {};
|
|
265
|
+
const type = meta.type ?? "unknown";
|
|
266
|
+
const score = m.score != null ? ` score:${m.score.toFixed(3)}` : "";
|
|
267
|
+
log(`[${i + 1}] id:${m.id ?? "?"} type:${type}${score}`);
|
|
268
|
+
log(` ${m.content ?? ""}`);
|
|
269
|
+
}
|
|
270
|
+
process.exit(0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function cmdForget(id) {
|
|
274
|
+
if (!id) {
|
|
275
|
+
process.stderr.write("Error: forget requires a memory ID. Usage: cli.js forget <id>\n");
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const cwd = process.cwd();
|
|
280
|
+
const sessionId = deriveSessionId(cwd);
|
|
281
|
+
|
|
282
|
+
if (DRY_RUN) {
|
|
283
|
+
log(JSON.stringify({
|
|
284
|
+
dryRun: true,
|
|
285
|
+
action: "forget",
|
|
286
|
+
method: "PUT",
|
|
287
|
+
id,
|
|
288
|
+
sessionId,
|
|
289
|
+
metadata: { deleted: true },
|
|
290
|
+
}));
|
|
291
|
+
process.exit(0);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const cfg = getEffectiveConfig();
|
|
295
|
+
if (!cfg) {
|
|
296
|
+
warn("No valid config found — cannot forget memory");
|
|
297
|
+
process.exit(0);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const workspace = cfg.workspace ?? "claude_code";
|
|
301
|
+
|
|
302
|
+
const client = createClient({
|
|
303
|
+
baseUrl: cfg.endpoint.baseUrl,
|
|
304
|
+
apiKey: cfg.apiKey,
|
|
305
|
+
timeoutMs: cfg.timeoutMs ?? 450,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const result = await client.softDeleteMemory(workspace, sessionId, id);
|
|
309
|
+
if (result) {
|
|
310
|
+
log(JSON.stringify({
|
|
311
|
+
action: "forget",
|
|
312
|
+
method: "PUT",
|
|
313
|
+
id,
|
|
314
|
+
result,
|
|
315
|
+
}));
|
|
316
|
+
} else {
|
|
317
|
+
warn(`Failed to soft-delete memory id=${id} — Honcho may be unreachable or ID not found`);
|
|
318
|
+
}
|
|
319
|
+
process.exit(0);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
// Main dispatcher
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
async function main() {
|
|
327
|
+
const [subcommand, ...rest] = process.argv.slice(2);
|
|
328
|
+
|
|
329
|
+
switch (subcommand) {
|
|
330
|
+
case "status":
|
|
331
|
+
await cmdStatus();
|
|
332
|
+
break;
|
|
333
|
+
|
|
334
|
+
case "save":
|
|
335
|
+
await cmdSave(rest.join(" ") || undefined);
|
|
336
|
+
break;
|
|
337
|
+
|
|
338
|
+
case "list":
|
|
339
|
+
await cmdList();
|
|
340
|
+
break;
|
|
341
|
+
|
|
342
|
+
case "search":
|
|
343
|
+
await cmdSearch(rest.join(" ") || undefined);
|
|
344
|
+
break;
|
|
345
|
+
|
|
346
|
+
case "forget":
|
|
347
|
+
await cmdForget(rest[0]);
|
|
348
|
+
break;
|
|
349
|
+
|
|
350
|
+
default:
|
|
351
|
+
if (!subcommand) {
|
|
352
|
+
process.stderr.write(
|
|
353
|
+
"Usage: cli.js <subcommand> [args]\n" +
|
|
354
|
+
"Subcommands: status, save <text>, list, search <query>, forget <id>\n"
|
|
355
|
+
);
|
|
356
|
+
} else {
|
|
357
|
+
process.stderr.write(
|
|
358
|
+
`Unknown subcommand: "${subcommand}"\n` +
|
|
359
|
+
"Usage: cli.js <subcommand> [args]\n" +
|
|
360
|
+
"Subcommands: status, save <text>, list, search <query>, forget <id>\n"
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
main().catch((err) => {
|
|
368
|
+
warn("Unexpected error: " + (err?.message ?? String(err)));
|
|
369
|
+
process.exit(1);
|
|
370
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* setup.js — Honcho memory skill setup wizard
|
|
3
|
+
*
|
|
4
|
+
* Verifies connection to Honcho, checks config validity, and optionally
|
|
5
|
+
* migrates legacy memory/*.md files to Honcho.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* bun run scripts/setup.js # interactive wizard
|
|
9
|
+
* bun run scripts/setup.js --dry-run # non-interactive verification only
|
|
10
|
+
*
|
|
11
|
+
* Exit codes:
|
|
12
|
+
* 0 — success or graceful degradation
|
|
13
|
+
* 1 — fatal error (missing bun, config parse error, etc.)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync, readdirSync } from "fs";
|
|
17
|
+
import { join, resolve } from "path";
|
|
18
|
+
import { createClient, loadConfig } from "../lib/honcho-client.js";
|
|
19
|
+
|
|
20
|
+
const DRY_RUN = process.argv.includes("--dry-run");
|
|
21
|
+
const CONFIG_PATH = "C:\\Users\\elton\\.honcho\\config.json";
|
|
22
|
+
|
|
23
|
+
function log(msg) {
|
|
24
|
+
process.stdout.write(msg + "\n");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function warn(msg) {
|
|
28
|
+
process.stderr.write("HONCHO_WARN: " + msg + "\n");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function checkConnection(cfg) {
|
|
32
|
+
const client = createClient({
|
|
33
|
+
baseUrl: cfg.endpoint.baseUrl,
|
|
34
|
+
apiKey: cfg.apiKey,
|
|
35
|
+
timeoutMs: cfg.timeoutMs ?? 450,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Use the workspace from config to test connectivity
|
|
39
|
+
const workspaceId = cfg.hosts?.claude_code?.workspace ?? "claude_code";
|
|
40
|
+
const result = await client.getOrCreateApp(workspaceId);
|
|
41
|
+
return result !== null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function countMemoryFiles(cwd) {
|
|
45
|
+
const memDir = join(cwd, "memory");
|
|
46
|
+
if (!existsSync(memDir)) return 0;
|
|
47
|
+
try {
|
|
48
|
+
return readdirSync(memDir).filter((f) => f.endsWith(".md")).length;
|
|
49
|
+
} catch {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function main() {
|
|
55
|
+
// Step 1: verify config exists and is parseable
|
|
56
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
57
|
+
warn(`Config file not found: ${CONFIG_PATH}`);
|
|
58
|
+
log("Config: MISSING");
|
|
59
|
+
log("Connection: FAIL");
|
|
60
|
+
if (DRY_RUN) {
|
|
61
|
+
log("DRY_RUN: config missing, cannot proceed");
|
|
62
|
+
}
|
|
63
|
+
process.exit(0); // graceful degradation
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const cfg = loadConfig();
|
|
67
|
+
if (!cfg) {
|
|
68
|
+
warn("Config file exists but failed to parse or is missing required fields");
|
|
69
|
+
log("Config: INVALID");
|
|
70
|
+
log("Connection: FAIL");
|
|
71
|
+
if (DRY_RUN) {
|
|
72
|
+
log("DRY_RUN: config invalid, cannot proceed");
|
|
73
|
+
}
|
|
74
|
+
process.exit(0); // graceful degradation
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
log("Config valid");
|
|
78
|
+
|
|
79
|
+
// Step 2: check connection
|
|
80
|
+
const connected = await checkConnection(cfg);
|
|
81
|
+
|
|
82
|
+
if (connected) {
|
|
83
|
+
log("Connection OK");
|
|
84
|
+
} else {
|
|
85
|
+
warn("Could not reach Honcho at " + cfg.endpoint.baseUrl);
|
|
86
|
+
log("Connection FAIL");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Step 3: dry-run mode — report memory files and exit
|
|
90
|
+
if (DRY_RUN) {
|
|
91
|
+
const cwd = process.cwd();
|
|
92
|
+
const memCount = countMemoryFiles(cwd);
|
|
93
|
+
if (memCount > 0) {
|
|
94
|
+
log(`DRY_RUN: would migrate ${memCount} files`);
|
|
95
|
+
} else {
|
|
96
|
+
log("DRY_RUN: no memory files found");
|
|
97
|
+
}
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Step 4: interactive mode (future — not implemented in Sprint 0)
|
|
102
|
+
log("Setup complete. Run with --dry-run for non-interactive verification.");
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
main().catch((err) => {
|
|
107
|
+
warn("Unexpected error in setup: " + (err?.message ?? String(err)));
|
|
108
|
+
process.exit(0); // graceful degradation
|
|
109
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T-001: Verify Honcho v3 endpoints documented correctly
|
|
3
|
+
*
|
|
4
|
+
* Failing tests to be written BEFORE implementation.
|
|
5
|
+
* These tests confirm the api-endpoints-verified.md file:
|
|
6
|
+
* - exists
|
|
7
|
+
* - contains a table with 6+ endpoints
|
|
8
|
+
* - contains a verifiedAt ISO-8601 field
|
|
9
|
+
* - each endpoint row has method, path, and verified status
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readFileSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
|
|
15
|
+
const SKILL_DIR = new URL("../", import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1");
|
|
16
|
+
const DOCS_DIR = join(SKILL_DIR, "docs");
|
|
17
|
+
const TARGET_FILE = join(DOCS_DIR, "api-endpoints-verified.md");
|
|
18
|
+
|
|
19
|
+
function readFile() {
|
|
20
|
+
return readFileSync(TARGET_FILE, "utf8");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Test 1: file must exist
|
|
24
|
+
const test1 = () => {
|
|
25
|
+
if (!existsSync(TARGET_FILE)) {
|
|
26
|
+
throw new Error(`api-endpoints-verified.md does not exist at: ${TARGET_FILE}`);
|
|
27
|
+
}
|
|
28
|
+
console.log("PASS: api-endpoints-verified.md exists");
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Test 2: must contain verifiedAt ISO-8601
|
|
32
|
+
const test2 = () => {
|
|
33
|
+
const content = readFile();
|
|
34
|
+
const iso8601Pattern = /verifiedAt[:\s]+\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
35
|
+
if (!iso8601Pattern.test(content)) {
|
|
36
|
+
throw new Error("api-endpoints-verified.md missing verifiedAt ISO-8601 field");
|
|
37
|
+
}
|
|
38
|
+
console.log("PASS: verifiedAt ISO-8601 present");
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Test 3: must have markdown table with 6+ endpoint rows
|
|
42
|
+
const test3 = () => {
|
|
43
|
+
const content = readFile();
|
|
44
|
+
// Count lines that look like markdown table rows with | separators
|
|
45
|
+
const tableRows = content
|
|
46
|
+
.split("\n")
|
|
47
|
+
.filter((line) => line.trim().startsWith("|") && !line.includes("---") && !line.includes("Operação") && !line.includes("Operation") && !line.includes("Method") && !line.includes("Método"));
|
|
48
|
+
if (tableRows.length < 6) {
|
|
49
|
+
throw new Error(`Expected 6+ endpoint rows in table, found ${tableRows.length}`);
|
|
50
|
+
}
|
|
51
|
+
console.log(`PASS: table contains ${tableRows.length} endpoint rows (>= 6)`);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Test 4: must contain expected operations
|
|
55
|
+
const test4 = () => {
|
|
56
|
+
const content = readFile();
|
|
57
|
+
const requiredOps = ["getOrCreateApp", "getOrCreateSession", "saveMemory", "searchMemories", "listMemories", "deleteMemory"];
|
|
58
|
+
const missing = requiredOps.filter((op) => !content.includes(op));
|
|
59
|
+
if (missing.length > 0) {
|
|
60
|
+
throw new Error(`Missing operations in doc: ${missing.join(", ")}`);
|
|
61
|
+
}
|
|
62
|
+
console.log("PASS: all 6 required operations documented");
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Test 5: must contain HTTP methods GET/POST/DELETE
|
|
66
|
+
const test5 = () => {
|
|
67
|
+
const content = readFile();
|
|
68
|
+
for (const method of ["GET", "POST", "DELETE"]) {
|
|
69
|
+
if (!content.includes(method)) {
|
|
70
|
+
throw new Error(`Missing HTTP method ${method} in documentation`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
console.log("PASS: HTTP methods GET, POST, DELETE present");
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Run all tests
|
|
77
|
+
let failed = 0;
|
|
78
|
+
const tests = [test1, test2, test3, test4, test5];
|
|
79
|
+
for (const t of tests) {
|
|
80
|
+
try {
|
|
81
|
+
t();
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.error(`FAIL: ${e.message}`);
|
|
84
|
+
failed++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(`\nResults: ${tests.length - failed}/${tests.length} passed`);
|
|
89
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T-002: Verify package.json + directory structure
|
|
3
|
+
*
|
|
4
|
+
* Failing tests to be written BEFORE implementation.
|
|
5
|
+
* Verifies:
|
|
6
|
+
* - package.json exists with "type": "module" (ESM)
|
|
7
|
+
* - lib/, hooks/, scripts/ directories exist
|
|
8
|
+
* - bun install succeeds (exit code 0)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync } from "fs";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
import { execSync } from "child_process";
|
|
14
|
+
|
|
15
|
+
const SKILL_DIR = new URL("../", import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1");
|
|
16
|
+
const PKG_FILE = join(SKILL_DIR, "package.json");
|
|
17
|
+
|
|
18
|
+
function readPkg() {
|
|
19
|
+
return JSON.parse(readFileSync(PKG_FILE, "utf8"));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Test 1: package.json must exist
|
|
23
|
+
const test1 = () => {
|
|
24
|
+
if (!existsSync(PKG_FILE)) {
|
|
25
|
+
throw new Error(`package.json does not exist at: ${PKG_FILE}`);
|
|
26
|
+
}
|
|
27
|
+
console.log("PASS: package.json exists");
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Test 2: package.json must have "type": "module"
|
|
31
|
+
const test2 = () => {
|
|
32
|
+
const pkg = readPkg();
|
|
33
|
+
if (pkg.type !== "module") {
|
|
34
|
+
throw new Error(`Expected "type": "module" in package.json, got: "${pkg.type}"`);
|
|
35
|
+
}
|
|
36
|
+
console.log('PASS: package.json has "type": "module"');
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Test 3: package.json must have a name field
|
|
40
|
+
const test3 = () => {
|
|
41
|
+
const pkg = readPkg();
|
|
42
|
+
if (!pkg.name) {
|
|
43
|
+
throw new Error("package.json missing 'name' field");
|
|
44
|
+
}
|
|
45
|
+
console.log(`PASS: package.json name = "${pkg.name}"`);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Test 4: lib/ directory must exist
|
|
49
|
+
const test4 = () => {
|
|
50
|
+
const libDir = join(SKILL_DIR, "lib");
|
|
51
|
+
if (!existsSync(libDir)) {
|
|
52
|
+
throw new Error(`lib/ directory does not exist at: ${libDir}`);
|
|
53
|
+
}
|
|
54
|
+
console.log("PASS: lib/ directory exists");
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Test 5: hooks/ directory must exist
|
|
58
|
+
const test5 = () => {
|
|
59
|
+
const hooksDir = join(SKILL_DIR, "hooks");
|
|
60
|
+
if (!existsSync(hooksDir)) {
|
|
61
|
+
throw new Error(`hooks/ directory does not exist at: ${hooksDir}`);
|
|
62
|
+
}
|
|
63
|
+
console.log("PASS: hooks/ directory exists");
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Test 6: scripts/ directory must exist
|
|
67
|
+
const test6 = () => {
|
|
68
|
+
const scriptsDir = join(SKILL_DIR, "scripts");
|
|
69
|
+
if (!existsSync(scriptsDir)) {
|
|
70
|
+
throw new Error(`scripts/ directory does not exist at: ${scriptsDir}`);
|
|
71
|
+
}
|
|
72
|
+
console.log("PASS: scripts/ directory exists");
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Test 7: package.json scripts must include "test"
|
|
76
|
+
const test7 = () => {
|
|
77
|
+
const pkg = readPkg();
|
|
78
|
+
if (!pkg.scripts || !pkg.scripts.test) {
|
|
79
|
+
throw new Error("package.json missing scripts.test");
|
|
80
|
+
}
|
|
81
|
+
console.log(`PASS: package.json scripts.test = "${pkg.scripts.test}"`);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Run all tests
|
|
85
|
+
let failed = 0;
|
|
86
|
+
const tests = [test1, test2, test3, test4, test5, test6, test7];
|
|
87
|
+
for (const t of tests) {
|
|
88
|
+
try {
|
|
89
|
+
t();
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(`FAIL: ${e.message}`);
|
|
92
|
+
failed++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`\nResults: ${tests.length - failed}/${tests.length} passed`);
|
|
97
|
+
process.exit(failed > 0 ? 1 : 0);
|