@drdeeks/character-kit 1.0.1

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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +178 -0
  3. package/deploy/ack_monitor.py +140 -0
  4. package/deploy/ack_watchdog.py +110 -0
  5. package/deploy/agent-character-monitor.service +21 -0
  6. package/deploy/agent-character-watchdog.service +21 -0
  7. package/deploy/agent-enforcer-proof.service +18 -0
  8. package/deploy/agent-enforcer.service +53 -0
  9. package/deploy/deploy-ack-services.sh +47 -0
  10. package/deploy/deploy-agent-enforcer.sh +133 -0
  11. package/deploy/proof-self-respawn.sh +59 -0
  12. package/node/bin/ack.js +383 -0
  13. package/node/bin/aik.js +520 -0
  14. package/node/bin/install.js +409 -0
  15. package/node/enforcer/agent_enforcer_daemon.js +690 -0
  16. package/node/examples/.agent/constitution.yaml +38 -0
  17. package/node/examples/.agent/enforcer.yaml +33 -0
  18. package/node/examples/.agent/habits/affirm_character_each_action.yaml +9 -0
  19. package/node/examples/.agent/habits/audit_not_silent.yaml +9 -0
  20. package/node/examples/.agent/habits/binding_map_one_core.yaml +9 -0
  21. package/node/examples/.agent/habits/character_hash_visible.yaml +9 -0
  22. package/node/examples/.agent/habits/check_duplication_before_debug.yaml +9 -0
  23. package/node/examples/.agent/habits/complete-thoroughly.yaml +16 -0
  24. package/node/examples/.agent/habits/consistent-info-across-files.yaml +18 -0
  25. package/node/examples/.agent/habits/document-for-next-agent.yaml +15 -0
  26. package/node/examples/.agent/habits/documented_rollback.yaml +9 -0
  27. package/node/examples/.agent/habits/drift_signal_detection.yaml +9 -0
  28. package/node/examples/.agent/habits/due-diligence.yaml +16 -0
  29. package/node/examples/.agent/habits/enterprise-grade-modular.yaml +16 -0
  30. package/node/examples/.agent/habits/fail_closed_tamper_evident.yaml +9 -0
  31. package/node/examples/.agent/habits/forever_one_idea.yaml +9 -0
  32. package/node/examples/.agent/habits/graceful_degradation.yaml +9 -0
  33. package/node/examples/.agent/habits/idempotent_operations.yaml +9 -0
  34. package/node/examples/.agent/habits/interactive-no-stalls.yaml +16 -0
  35. package/node/examples/.agent/habits/layered_not_rewritten.yaml +9 -0
  36. package/node/examples/.agent/habits/lossless_consolidation.yaml +9 -0
  37. package/node/examples/.agent/habits/no-credential-leak.yaml +41 -0
  38. package/node/examples/.agent/habits/no-deception.yaml +14 -0
  39. package/node/examples/.agent/habits/no_hold_narration.yaml +9 -0
  40. package/node/examples/.agent/habits/one_concern_per_file.yaml +9 -0
  41. package/node/examples/.agent/habits/optimized-robust.yaml +16 -0
  42. package/node/examples/.agent/habits/registered_plugin_not_string.yaml +9 -0
  43. package/node/examples/.agent/habits/rename_as_layer_op.yaml +9 -0
  44. package/node/examples/.agent/habits/resolve-root-cause.yaml +15 -0
  45. package/node/examples/.agent/habits/rigorous-commits-no-push.yaml +18 -0
  46. package/node/examples/.agent/habits/safe-deletion-via-trash.yaml +20 -0
  47. package/node/examples/.agent/habits/safe_file_permissions.yaml +9 -0
  48. package/node/examples/.agent/habits/self-healing-portability.yaml +14 -0
  49. package/node/examples/.agent/habits/shippable-pride.yaml +15 -0
  50. package/node/examples/.agent/habits/single_source_of_truth.yaml +9 -0
  51. package/node/examples/.agent/habits/test_of_forever.yaml +9 -0
  52. package/node/examples/.agent/habits/timeout_and_retry.yaml +9 -0
  53. package/node/examples/.agent/habits/track_defects_openly.yaml +9 -0
  54. package/node/examples/.agent/habits/validate-against-real-source.yaml +14 -0
  55. package/node/examples/.agent/habits/verify-against-docs.yaml +15 -0
  56. package/node/examples/.agent/habits/verify-functionality-not-syntax.yaml +15 -0
  57. package/node/examples/.agent/habits/versioning-discipline.yaml +18 -0
  58. package/node/package.json +53 -0
  59. package/node/src/enforcer/client.js +149 -0
  60. package/node/src/hooks/character.js +286 -0
  61. package/node/src/index.js +24 -0
  62. package/node/src/knowledge/indexer.js +486 -0
  63. package/node/src/knowledge/semantic.js +154 -0
  64. package/node/src/memory/index.js +355 -0
  65. package/node/tests/character.test.js +80 -0
  66. package/node/tests/habit-create.test.js +67 -0
  67. package/node/tests/habits-default.test.js +68 -0
  68. package/node/tests/install-chain.test.js +19 -0
  69. package/node/tests/install.test.js +104 -0
  70. package/package.json +65 -0
  71. package/python/agent_character_kit/__init__.py +388 -0
  72. package/python/agent_character_kit/__main__.py +333 -0
  73. package/python/agent_character_kit/_yaml.py +33 -0
  74. package/python/agent_character_kit/enforcer.py +379 -0
  75. package/python/agent_character_kit/memory.py +257 -0
  76. package/python/agent_character_kit/semantic.py +126 -0
  77. package/python/example_workspace/.agent/habits/affirm_character_each_action.yaml +9 -0
  78. package/python/example_workspace/.agent/habits/audit_not_silent.yaml +9 -0
  79. package/python/example_workspace/.agent/habits/binding_map_one_core.yaml +9 -0
  80. package/python/example_workspace/.agent/habits/character_hash_visible.yaml +9 -0
  81. package/python/example_workspace/.agent/habits/check_duplication_before_debug.yaml +9 -0
  82. package/python/example_workspace/.agent/habits/complete-thoroughly.yaml +16 -0
  83. package/python/example_workspace/.agent/habits/consistent-info-across-files.yaml +18 -0
  84. package/python/example_workspace/.agent/habits/document-for-next-agent.yaml +15 -0
  85. package/python/example_workspace/.agent/habits/documented_rollback.yaml +9 -0
  86. package/python/example_workspace/.agent/habits/drift_signal_detection.yaml +9 -0
  87. package/python/example_workspace/.agent/habits/due-diligence.yaml +16 -0
  88. package/python/example_workspace/.agent/habits/enterprise-grade-modular.yaml +16 -0
  89. package/python/example_workspace/.agent/habits/fail_closed_tamper_evident.yaml +9 -0
  90. package/python/example_workspace/.agent/habits/forever_one_idea.yaml +9 -0
  91. package/python/example_workspace/.agent/habits/graceful_degradation.yaml +9 -0
  92. package/python/example_workspace/.agent/habits/idempotent_operations.yaml +9 -0
  93. package/python/example_workspace/.agent/habits/interactive-no-stalls.yaml +16 -0
  94. package/python/example_workspace/.agent/habits/layered_not_rewritten.yaml +9 -0
  95. package/python/example_workspace/.agent/habits/lossless_consolidation.yaml +9 -0
  96. package/python/example_workspace/.agent/habits/no-credential-leak.yaml +41 -0
  97. package/python/example_workspace/.agent/habits/no-deception.yaml +14 -0
  98. package/python/example_workspace/.agent/habits/no_hold_narration.yaml +9 -0
  99. package/python/example_workspace/.agent/habits/one_concern_per_file.yaml +9 -0
  100. package/python/example_workspace/.agent/habits/optimized-robust.yaml +16 -0
  101. package/python/example_workspace/.agent/habits/registered_plugin_not_string.yaml +9 -0
  102. package/python/example_workspace/.agent/habits/rename_as_layer_op.yaml +9 -0
  103. package/python/example_workspace/.agent/habits/resolve-root-cause.yaml +15 -0
  104. package/python/example_workspace/.agent/habits/rigorous-commits-no-push.yaml +18 -0
  105. package/python/example_workspace/.agent/habits/safe-deletion-via-trash.yaml +20 -0
  106. package/python/example_workspace/.agent/habits/safe_file_permissions.yaml +9 -0
  107. package/python/example_workspace/.agent/habits/self-healing-portability.yaml +14 -0
  108. package/python/example_workspace/.agent/habits/shippable-pride.yaml +15 -0
  109. package/python/example_workspace/.agent/habits/single_source_of_truth.yaml +9 -0
  110. package/python/example_workspace/.agent/habits/test_of_forever.yaml +9 -0
  111. package/python/example_workspace/.agent/habits/timeout_and_retry.yaml +9 -0
  112. package/python/example_workspace/.agent/habits/track_defects_openly.yaml +9 -0
  113. package/python/example_workspace/.agent/habits/validate-against-real-source.yaml +14 -0
  114. package/python/example_workspace/.agent/habits/verify-against-docs.yaml +15 -0
  115. package/python/example_workspace/.agent/habits/verify-functionality-not-syntax.yaml +15 -0
  116. package/python/example_workspace/.agent/habits/versioning-discipline.yaml +18 -0
  117. package/python/hermes_plugin/README.md +63 -0
  118. package/python/hermes_plugin/__init__.py +367 -0
  119. package/python/hermes_plugin/config.yaml +20 -0
  120. package/python/hermes_plugin/plugin.yaml +7 -0
  121. package/python/hermes_plugin/test_plugin.py +166 -0
  122. package/python/pyproject.toml +19 -0
  123. package/python/tests/python_parity_test.py +84 -0
@@ -0,0 +1,520 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import { processToolCall, generateConfig, EnforcerClient } from "../src/index.js";
5
+ import fs from "fs";
6
+ import os from "os";
7
+ import path from "path";
8
+
9
+ // Components are loaded lazily inside their commands so you can use ONLY the
10
+ // character layer (hook + enforcer) without pulling in indexer / memory /
11
+ // semantic. Nothing is forced.
12
+ const getIndexer = async () => (await import("../src/knowledge/indexer.js")).DocumentIndexer;
13
+ const getSemantic = async () => (await import("../src/knowledge/semantic.js")).SemanticSearch;
14
+ const getMemory = async () => (await import("../src/memory/index.js")).Memory;
15
+ import { spawn } from "child_process";
16
+ import { fileURLToPath } from "url";
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name("aik")
22
+ .description("Agent Character Kit — Character enforcement + knowledge + memory")
23
+ .version("1.0.0");
24
+
25
+ // ─── Hook Command (Core) ────────────────────────────────────────────────────
26
+
27
+ program
28
+ .command("hook")
29
+ .description("Run character hook for tool validation (core enforcement)")
30
+ .option("-f, --framework <fw>", "Framework (claude|cursor|gemini|hermes|opencode|generic|auto)", "auto")
31
+ .option("-c, --config", "Generate hook configuration for a framework")
32
+ .option("--framework-config <fw>", "Framework to generate config for")
33
+ .option("--hook-command <cmd>", "Custom hook command path")
34
+ .action(async (opts) => {
35
+ if (opts.config) {
36
+ const fw = opts.frameworkConfig || opts.framework;
37
+ const config = generateConfig(fw, opts.hookCommand);
38
+ console.log(JSON.stringify(config, null, 2));
39
+ return;
40
+ }
41
+
42
+ let input = "";
43
+ if (!process.stdin.isTTY) {
44
+ input = await new Promise((resolve) => {
45
+ let data = "";
46
+ process.stdin.on("data", (chunk) => (data += chunk));
47
+ process.stdin.on("end", () => resolve(data));
48
+ });
49
+ }
50
+
51
+ const payload = input ? JSON.parse(input) : {};
52
+ const result = await processToolCall(payload, { framework: opts.framework });
53
+ console.log(JSON.stringify(result.output));
54
+ process.exit(result.exitCode);
55
+ });
56
+
57
+ // ─── Enforcer Commands ──────────────────────────────────────────────────────
58
+
59
+ program
60
+ .command("enforcer")
61
+ .description("Enforcer daemon operations")
62
+ .option("-s, --status", "Check enforcer status")
63
+ .option("--validate", "Validate workspace integrity")
64
+ .option("--heartbeat", "Send heartbeat")
65
+ .option("--reload", "Reload constitution + habits + policy")
66
+ .option("--start", "Start the daemon in the foreground")
67
+ .option("--supervise", "Run the daemon with self-healing (restart 5-15s if killed)")
68
+ .option("--install", "Install a platform service (systemd/launchd) for self-healing")
69
+ .option("--user", "With --install: create a dedicated enforcer user (hardening)")
70
+ .action(async (opts) => {
71
+ const client = new EnforcerClient();
72
+
73
+ if (opts.start) {
74
+ const { runDaemon } = await import("../enforcer/agent_enforcer_daemon.js");
75
+ runDaemon();
76
+ return;
77
+ }
78
+
79
+ if (opts.supervise) {
80
+ await superviseDaemon();
81
+ return;
82
+ }
83
+
84
+ if (opts.install) {
85
+ installService(!!opts.user);
86
+ return;
87
+ }
88
+
89
+ if (opts.reload) {
90
+ const result = await client.call("reload");
91
+ console.log(JSON.stringify(result, null, 2));
92
+ return;
93
+ }
94
+
95
+ if (opts.status) {
96
+ const result = await client.validateWorkspace();
97
+ console.log(JSON.stringify(result, null, 2));
98
+ } else if (opts.validate) {
99
+ const result = await client.validateWorkspace();
100
+ console.log(JSON.stringify(result, null, 2));
101
+ } else if (opts.heartbeat) {
102
+ const result = await client.heartbeat();
103
+ console.log(JSON.stringify(result, null, 2));
104
+ } else {
105
+ console.log("Use --status, --validate, --heartbeat, --start, --supervise, or --install");
106
+ }
107
+ });
108
+
109
+ // ─── Self-healing supervisor ─────────────────────────────────────────────────
110
+
111
+ async function superviseDaemon() {
112
+ const daemonScript = path.resolve(import.meta.dirname || path.dirname(fileURLToPath(import.meta.url)), "..", "enforcer", "agent_enforcer_daemon.js");
113
+ let child = null;
114
+
115
+ const start = () => {
116
+ child = spawn(process.execPath, [daemonScript], {
117
+ env: process.env,
118
+ stdio: "inherit",
119
+ });
120
+ child.on("exit", (code, signal) => {
121
+ console.error(`[supervise] daemon exited (${signal || code}); scheduling restart`);
122
+ scheduleRestart();
123
+ });
124
+ child.on("error", (e) => {
125
+ console.error(`[supervise] spawn error: ${e.message}`);
126
+ scheduleRestart();
127
+ });
128
+ };
129
+
130
+ const scheduleRestart = () => {
131
+ // Self-healing: bring it back within 5-15 seconds.
132
+ const delay = 5000 + Math.floor(Math.random() * 10000);
133
+ console.error(`[supervise] restarting in ${Math.round(delay / 1000)}s`);
134
+ setTimeout(start, delay);
135
+ };
136
+
137
+ const shutdown = () => { if (child) child.kill(); process.exit(0); };
138
+ process.on("SIGTERM", shutdown);
139
+ process.on("SIGINT", shutdown);
140
+
141
+ console.error("[supervise] self-healing supervisor started");
142
+ start();
143
+ }
144
+
145
+ // ─── Cross-platform installer ────────────────────────────────────────────────
146
+
147
+ function installService(createUser) {
148
+ const HOME = process.env.HOME || os.homedir();
149
+ const aikBin = path.resolve(process.cwd(), "bin", "aik.js");
150
+ const launcher = `node ${aikBin} enforcer --supervise`;
151
+ const platform = process.platform;
152
+
153
+ if (platform === "linux" || platform === "freebsd") {
154
+ const unit = `[Unit]
155
+ Description=Agent Character Enforcer (self-healing)
156
+ After=network.target
157
+
158
+ [Service]
159
+ Type=simple
160
+ ExecStart=${launcher}
161
+ Restart=always
162
+ RestartSec=5
163
+ # Self-healing: if killed, systemd brings it back within 5s.
164
+ Environment=HOME=${HOME}
165
+ Environment=AGENT_WORKSPACE=${process.env.AGENT_WORKSPACE || path.join(HOME, ".agent-character-kit", "workspace")}
166
+ ${createUser ? "User=aienforcer\n" : ""}WorkingDirectory=${process.cwd()}
167
+
168
+ [Install]
169
+ WantedBy=default.target
170
+ `;
171
+ const unitPath = path.join(HOME, ".config", "systemd", "user", "agent-enforcer.service");
172
+ fs.mkdirSync(path.dirname(unitPath), { recursive: true });
173
+ fs.writeFileSync(unitPath, unit);
174
+ if (createUser) {
175
+ console.log("# Create the dedicated user (run as root):");
176
+ console.log(`sudo useradd -r -s /usr/sbin/nologin aienforcer`);
177
+ console.log(`sudo chown -R aienforcer ${path.join(HOME, "run", "agent-enforcer")}`);
178
+ }
179
+ console.log(`Wrote ${unitPath}`);
180
+ console.log("Enable + start:");
181
+ console.log(" systemctl --user daemon-reload");
182
+ console.log(" systemctl --user enable --now agent-enforcer.service");
183
+ } else if (platform === "darwin") {
184
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
185
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
186
+ <plist version="1.0">
187
+ <dict>
188
+ <key>Label</key><string>com.agentcharacterkit.enforcer</string>
189
+ <key>ProgramArguments</key>
190
+ <array><string>node</string><string>${aikBin}</string><string>enforcer</string><string>--supervise</string></array>
191
+ <key>RunAtLoad</key><true/>
192
+ <key>KeepAlive</key><true/>
193
+ <key>ThrottleInterval</key><integer>5</integer>
194
+ <key>EnvironmentVariables</key>
195
+ <dict>
196
+ <key>HOME</key><string>${HOME}</string>
197
+ <key>AGENT_WORKSPACE</key><string>${process.env.AGENT_WORKSPACE || path.join(HOME, ".agent-character-kit", "workspace")}</string>
198
+ </dict>
199
+ </dict>
200
+ </plist>
201
+ `;
202
+ const plistPath = path.join(HOME, "Library", "LaunchAgents", "com.agentcharacterkit.enforcer.plist");
203
+ fs.mkdirSync(path.dirname(plistPath), { recursive: true });
204
+ fs.writeFileSync(plistPath, plist);
205
+ console.log(`Wrote ${plistPath}`);
206
+ console.log("Load + start:");
207
+ console.log(` launchctl load ${plistPath}`);
208
+ console.log(` launchctl start com.agentcharacterkit.enforcer`);
209
+ } else {
210
+ console.log("No native service manager for this platform.");
211
+ console.log("Run the self-healing supervisor directly:");
212
+ console.log(` ${launcher}`);
213
+ }
214
+ }
215
+
216
+ // ─── Index Commands (Knowledge) ─────────────────────────────────────────────
217
+
218
+ const indexCmd = program
219
+ .command("index")
220
+ .description("Document indexing with YAML frontmatter");
221
+
222
+ indexCmd
223
+ .command("run")
224
+ .description("Index the user-supplied corpus (defaults to ./corpus)")
225
+ .argument("[path]", "Directory to index", "corpus")
226
+ .option("-e, --extensions <exts>", "File extensions (comma-separated). Omit to discover all supported types", "")
227
+ .option("--category <cat>", "Force category for all files")
228
+ .action(async (dirPath, opts) => {
229
+ const corpus = path.resolve(process.cwd(), dirPath);
230
+ if (dirPath === "corpus" && !fs.existsSync(corpus)) {
231
+ fs.mkdirSync(corpus, { recursive: true });
232
+ console.error(`[aik] created empty corpus at ${corpus} — drop user docs/links/examples here.`);
233
+ }
234
+ const indexer = new (await getIndexer())(process.cwd());
235
+ await indexer.init();
236
+ const extensions = opts.extensions
237
+ ? opts.extensions.split(",").map((e) => e.trim()).filter(Boolean)
238
+ : undefined;
239
+ const result = await indexer.indexDirectory(corpus, { extensions, category: opts.category });
240
+ console.log(JSON.stringify(result, null, 2));
241
+ });
242
+
243
+ indexCmd
244
+ .command("file")
245
+ .description("Index a single file")
246
+ .argument("<path>", "File to index")
247
+ .option("--category <cat>", "Document category")
248
+ .option("--tags <tags>", "Comma-separated tags")
249
+ .action(async (filePath, opts) => {
250
+ const indexer = new (await getIndexer())(process.cwd());
251
+ await indexer.init();
252
+ const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined;
253
+ const result = await indexer.indexFile(filePath, { category: opts.category, tags });
254
+ console.log(JSON.stringify(result, null, 2));
255
+ });
256
+
257
+ indexCmd
258
+ .command("search")
259
+ .description("Search indexed documents")
260
+ .argument("<query>", "Search query")
261
+ .option("-l, --limit <n>", "Max results", "10")
262
+ .option("-c, --category <cat>", "Filter by category")
263
+ .action(async (query, opts) => {
264
+ const indexer = new (await getIndexer())(process.cwd());
265
+ await indexer.init();
266
+ const results = await indexer.search(query, { limit: parseInt(opts.limit), category: opts.category });
267
+ console.log(JSON.stringify(results, null, 2));
268
+ });
269
+
270
+ indexCmd
271
+ .command("status")
272
+ .description("Show indexing status")
273
+ .action(async () => {
274
+ const indexer = new (await getIndexer())(process.cwd());
275
+ await indexer.init();
276
+ console.log(JSON.stringify(indexer.status(), null, 2));
277
+ });
278
+
279
+ indexCmd
280
+ .command("list")
281
+ .description("List indexed documents")
282
+ .option("-c, --category <cat>", "Filter by category")
283
+ .action(async (opts) => {
284
+ const indexer = new (await getIndexer())(process.cwd());
285
+ await indexer.init();
286
+ console.log(JSON.stringify(indexer.listDocuments(opts.category), null, 2));
287
+ });
288
+
289
+ // ─── Semantic Commands ──────────────────────────────────────────────────────
290
+
291
+ const semanticCmd = program
292
+ .command("semantic")
293
+ .description("Semantic (vector) search");
294
+
295
+ semanticCmd
296
+ .command("index")
297
+ .description("Build semantic vectors for indexed documents")
298
+ .action(async () => {
299
+ const indexer = new (await getIndexer())(process.cwd());
300
+ await indexer.init();
301
+ const semantic = new (await getSemantic())(process.cwd());
302
+ await semantic.init();
303
+
304
+ const docs = indexer.listDocuments();
305
+ let count = 0;
306
+ for (const doc of docs) {
307
+ const docData = indexer.index.documents[doc.id];
308
+ if (!docData?.chunks) continue;
309
+ for (const chunk of docData.chunks) {
310
+ await semantic.indexDocument(chunk.id, chunk.content, {
311
+ parent: doc.id,
312
+ category: doc.category,
313
+ tags: doc.tags,
314
+ });
315
+ count++;
316
+ }
317
+ }
318
+ await semantic.save();
319
+ console.log(JSON.stringify({ indexed: count, status: "ok" }, null, 2));
320
+ });
321
+
322
+ semanticCmd
323
+ .command("search")
324
+ .description("Semantic search across vectors")
325
+ .argument("<query>", "Search query")
326
+ .option("-l, --limit <n>", "Max results", "10")
327
+ .action(async (query, opts) => {
328
+ const semantic = new (await getSemantic())(process.cwd());
329
+ await semantic.init();
330
+ const results = await semantic.search(query, parseInt(opts.limit));
331
+ console.log(JSON.stringify(results, null, 2));
332
+ });
333
+
334
+ semanticCmd
335
+ .command("hybrid")
336
+ .description("Hybrid search (keyword + semantic)")
337
+ .argument("<query>", "Search query")
338
+ .option("-l, --limit <n>", "Max results", "10")
339
+ .action(async (query, opts) => {
340
+ const indexer = new (await getIndexer())(process.cwd());
341
+ await indexer.init();
342
+ const semantic = new (await getSemantic())(process.cwd());
343
+ await semantic.init();
344
+
345
+ const keywordResults = await indexer.search(query, { limit: parseInt(opts.limit) });
346
+ const results = await semantic.hybridSearch(query, keywordResults, parseInt(opts.limit));
347
+ console.log(JSON.stringify(results, null, 2));
348
+ });
349
+
350
+ semanticCmd
351
+ .command("status")
352
+ .description("Show semantic search status")
353
+ .action(async () => {
354
+ const semantic = new (await getSemantic())(process.cwd());
355
+ await semantic.init();
356
+ console.log(JSON.stringify(semantic.status(), null, 2));
357
+ });
358
+
359
+ // ─── Memory Commands ────────────────────────────────────────────────────────
360
+
361
+ const memoryCmd = program
362
+ .command("memory")
363
+ .description("Memory system (daily/weekly/long-term)");
364
+
365
+ memoryCmd
366
+ .command("log")
367
+ .description("Log entry to today's daily note")
368
+ .argument("<entry>", "Entry content")
369
+ .option("-t, --tags <tags>", "Comma-separated tags")
370
+ .option("-c, --category <cat>", "Entry category", "general")
371
+ .action(async (entry, opts) => {
372
+ const memory = new (await getMemory())(process.cwd());
373
+ await memory.init();
374
+ const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [];
375
+ const result = await memory.daily.log(entry, { tags, category: opts.category });
376
+ console.log(JSON.stringify(result, null, 2));
377
+ });
378
+
379
+ memoryCmd
380
+ .command("today")
381
+ .description("Show today's daily note")
382
+ .action(async () => {
383
+ const memory = new (await getMemory())(process.cwd());
384
+ await memory.init();
385
+ const note = await memory.daily.getToday();
386
+ console.log(JSON.stringify(note, null, 2));
387
+ });
388
+
389
+ memoryCmd
390
+ .command("lesson")
391
+ .description("Add lesson to long-term memory")
392
+ .argument("<title>", "Lesson title")
393
+ .argument("<content>", "Lesson content")
394
+ .option("-t, --tags <tags>", "Comma-separated tags")
395
+ .action(async (title, content, opts) => {
396
+ const memory = new (await getMemory())(process.cwd());
397
+ await memory.init();
398
+ const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [];
399
+ const result = await memory.longterm.addLesson(title, content, { tags });
400
+ console.log(JSON.stringify(result, null, 2));
401
+ });
402
+
403
+ memoryCmd
404
+ .command("pattern")
405
+ .description("Add pattern to long-term memory")
406
+ .argument("<name>", "Pattern name")
407
+ .argument("<description>", "Pattern description")
408
+ .action(async (name, description) => {
409
+ const memory = new (await getMemory())(process.cwd());
410
+ await memory.init();
411
+ const result = await memory.longterm.addPattern(name, description);
412
+ console.log(JSON.stringify(result, null, 2));
413
+ });
414
+
415
+ memoryCmd
416
+ .command("decision")
417
+ .description("Record an important decision")
418
+ .argument("<title>", "Decision title")
419
+ .argument("<context>", "Context")
420
+ .argument("<decision>", "What was decided")
421
+ .argument("<rationale>", "Why")
422
+ .action(async (title, context, decision, rationale) => {
423
+ const memory = new (await getMemory())(process.cwd());
424
+ await memory.init();
425
+ const result = await memory.longterm.addDecision(title, context, decision, rationale);
426
+ console.log(JSON.stringify(result, null, 2));
427
+ });
428
+
429
+ memoryCmd
430
+ .command("search")
431
+ .description("Search all memory layers")
432
+ .argument("<query>", "Search query")
433
+ .action(async (query) => {
434
+ const memory = new (await getMemory())(process.cwd());
435
+ await memory.init();
436
+ const results = await memory.search(query);
437
+ console.log(JSON.stringify(results, null, 2));
438
+ });
439
+
440
+ memoryCmd
441
+ .command("status")
442
+ .description("Show memory system status")
443
+ .action(async () => {
444
+ const memory = new (await getMemory())(process.cwd());
445
+ await memory.init();
446
+ const status = await memory.status();
447
+ console.log(JSON.stringify(status, null, 2));
448
+ });
449
+
450
+ // ─── Knowledge Graph Commands ───────────────────────────────────────────────
451
+
452
+ const kgCmd = program
453
+ .command("knowledge")
454
+ .description("Knowledge graph (entities)");
455
+
456
+ kgCmd
457
+ .command("add")
458
+ .description("Add entity to knowledge graph")
459
+ .argument("<name>", "Entity name")
460
+ .option("--type <type>", "Entity type", "general")
461
+ .option("--facts <json>", "JSON facts object", "{}")
462
+ .option("-t, --tags <tags>", "Comma-separated tags")
463
+ .action(async (name, opts) => {
464
+ const memory = new (await getMemory())(process.cwd());
465
+ await memory.init();
466
+ const facts = JSON.parse(opts.facts);
467
+ const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [];
468
+ const result = await memory.knowledge.addEntity(name, opts.type, facts, tags);
469
+ console.log(JSON.stringify(result, null, 2));
470
+ });
471
+
472
+ kgCmd
473
+ .command("get")
474
+ .description("Get entity from knowledge graph")
475
+ .argument("<name>", "Entity name")
476
+ .action(async (name) => {
477
+ const memory = new (await getMemory())(process.cwd());
478
+ await memory.init();
479
+ const entity = await memory.knowledge.getEntity(name);
480
+ console.log(JSON.stringify(entity, null, 2));
481
+ });
482
+
483
+ kgCmd
484
+ .command("search")
485
+ .description("Search knowledge graph")
486
+ .argument("<query>", "Search query")
487
+ .action(async (query) => {
488
+ const memory = new (await getMemory())(process.cwd());
489
+ await memory.init();
490
+ const results = await memory.knowledge.searchEntities(query);
491
+ console.log(JSON.stringify(results, null, 2));
492
+ });
493
+
494
+ kgCmd
495
+ .command("list")
496
+ .description("List all entities")
497
+ .option("--type <type>", "Filter by type")
498
+ .action(async (opts) => {
499
+ const memory = new (await getMemory())(process.cwd());
500
+ await memory.init();
501
+ const entities = await memory.knowledge.listEntities(opts.type);
502
+ console.log(JSON.stringify(entities, null, 2));
503
+ });
504
+
505
+ // ─── Config Command ─────────────────────────────────────────────────────────
506
+
507
+ program
508
+ .command("config")
509
+ .description("Generate hook configurations for all frameworks")
510
+ .option("--hook-command <cmd>", "Custom hook command", "npx aik hook")
511
+ .action((opts) => {
512
+ const frameworks = ["claude", "cursor", "gemini"];
513
+ const configs = {};
514
+ for (const fw of frameworks) {
515
+ configs[fw] = generateConfig(fw, opts.hookCommand);
516
+ }
517
+ console.log(JSON.stringify(configs, null, 2));
518
+ });
519
+
520
+ program.parse();