@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.
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/deploy/ack_monitor.py +140 -0
- package/deploy/ack_watchdog.py +110 -0
- package/deploy/agent-character-monitor.service +21 -0
- package/deploy/agent-character-watchdog.service +21 -0
- package/deploy/agent-enforcer-proof.service +18 -0
- package/deploy/agent-enforcer.service +53 -0
- package/deploy/deploy-ack-services.sh +47 -0
- package/deploy/deploy-agent-enforcer.sh +133 -0
- package/deploy/proof-self-respawn.sh +59 -0
- package/node/bin/ack.js +383 -0
- package/node/bin/aik.js +520 -0
- package/node/bin/install.js +409 -0
- package/node/enforcer/agent_enforcer_daemon.js +690 -0
- package/node/examples/.agent/constitution.yaml +38 -0
- package/node/examples/.agent/enforcer.yaml +33 -0
- package/node/examples/.agent/habits/affirm_character_each_action.yaml +9 -0
- package/node/examples/.agent/habits/audit_not_silent.yaml +9 -0
- package/node/examples/.agent/habits/binding_map_one_core.yaml +9 -0
- package/node/examples/.agent/habits/character_hash_visible.yaml +9 -0
- package/node/examples/.agent/habits/check_duplication_before_debug.yaml +9 -0
- package/node/examples/.agent/habits/complete-thoroughly.yaml +16 -0
- package/node/examples/.agent/habits/consistent-info-across-files.yaml +18 -0
- package/node/examples/.agent/habits/document-for-next-agent.yaml +15 -0
- package/node/examples/.agent/habits/documented_rollback.yaml +9 -0
- package/node/examples/.agent/habits/drift_signal_detection.yaml +9 -0
- package/node/examples/.agent/habits/due-diligence.yaml +16 -0
- package/node/examples/.agent/habits/enterprise-grade-modular.yaml +16 -0
- package/node/examples/.agent/habits/fail_closed_tamper_evident.yaml +9 -0
- package/node/examples/.agent/habits/forever_one_idea.yaml +9 -0
- package/node/examples/.agent/habits/graceful_degradation.yaml +9 -0
- package/node/examples/.agent/habits/idempotent_operations.yaml +9 -0
- package/node/examples/.agent/habits/interactive-no-stalls.yaml +16 -0
- package/node/examples/.agent/habits/layered_not_rewritten.yaml +9 -0
- package/node/examples/.agent/habits/lossless_consolidation.yaml +9 -0
- package/node/examples/.agent/habits/no-credential-leak.yaml +41 -0
- package/node/examples/.agent/habits/no-deception.yaml +14 -0
- package/node/examples/.agent/habits/no_hold_narration.yaml +9 -0
- package/node/examples/.agent/habits/one_concern_per_file.yaml +9 -0
- package/node/examples/.agent/habits/optimized-robust.yaml +16 -0
- package/node/examples/.agent/habits/registered_plugin_not_string.yaml +9 -0
- package/node/examples/.agent/habits/rename_as_layer_op.yaml +9 -0
- package/node/examples/.agent/habits/resolve-root-cause.yaml +15 -0
- package/node/examples/.agent/habits/rigorous-commits-no-push.yaml +18 -0
- package/node/examples/.agent/habits/safe-deletion-via-trash.yaml +20 -0
- package/node/examples/.agent/habits/safe_file_permissions.yaml +9 -0
- package/node/examples/.agent/habits/self-healing-portability.yaml +14 -0
- package/node/examples/.agent/habits/shippable-pride.yaml +15 -0
- package/node/examples/.agent/habits/single_source_of_truth.yaml +9 -0
- package/node/examples/.agent/habits/test_of_forever.yaml +9 -0
- package/node/examples/.agent/habits/timeout_and_retry.yaml +9 -0
- package/node/examples/.agent/habits/track_defects_openly.yaml +9 -0
- package/node/examples/.agent/habits/validate-against-real-source.yaml +14 -0
- package/node/examples/.agent/habits/verify-against-docs.yaml +15 -0
- package/node/examples/.agent/habits/verify-functionality-not-syntax.yaml +15 -0
- package/node/examples/.agent/habits/versioning-discipline.yaml +18 -0
- package/node/package.json +53 -0
- package/node/src/enforcer/client.js +149 -0
- package/node/src/hooks/character.js +286 -0
- package/node/src/index.js +24 -0
- package/node/src/knowledge/indexer.js +486 -0
- package/node/src/knowledge/semantic.js +154 -0
- package/node/src/memory/index.js +355 -0
- package/node/tests/character.test.js +80 -0
- package/node/tests/habit-create.test.js +67 -0
- package/node/tests/habits-default.test.js +68 -0
- package/node/tests/install-chain.test.js +19 -0
- package/node/tests/install.test.js +104 -0
- package/package.json +65 -0
- package/python/agent_character_kit/__init__.py +388 -0
- package/python/agent_character_kit/__main__.py +333 -0
- package/python/agent_character_kit/_yaml.py +33 -0
- package/python/agent_character_kit/enforcer.py +379 -0
- package/python/agent_character_kit/memory.py +257 -0
- package/python/agent_character_kit/semantic.py +126 -0
- package/python/example_workspace/.agent/habits/affirm_character_each_action.yaml +9 -0
- package/python/example_workspace/.agent/habits/audit_not_silent.yaml +9 -0
- package/python/example_workspace/.agent/habits/binding_map_one_core.yaml +9 -0
- package/python/example_workspace/.agent/habits/character_hash_visible.yaml +9 -0
- package/python/example_workspace/.agent/habits/check_duplication_before_debug.yaml +9 -0
- package/python/example_workspace/.agent/habits/complete-thoroughly.yaml +16 -0
- package/python/example_workspace/.agent/habits/consistent-info-across-files.yaml +18 -0
- package/python/example_workspace/.agent/habits/document-for-next-agent.yaml +15 -0
- package/python/example_workspace/.agent/habits/documented_rollback.yaml +9 -0
- package/python/example_workspace/.agent/habits/drift_signal_detection.yaml +9 -0
- package/python/example_workspace/.agent/habits/due-diligence.yaml +16 -0
- package/python/example_workspace/.agent/habits/enterprise-grade-modular.yaml +16 -0
- package/python/example_workspace/.agent/habits/fail_closed_tamper_evident.yaml +9 -0
- package/python/example_workspace/.agent/habits/forever_one_idea.yaml +9 -0
- package/python/example_workspace/.agent/habits/graceful_degradation.yaml +9 -0
- package/python/example_workspace/.agent/habits/idempotent_operations.yaml +9 -0
- package/python/example_workspace/.agent/habits/interactive-no-stalls.yaml +16 -0
- package/python/example_workspace/.agent/habits/layered_not_rewritten.yaml +9 -0
- package/python/example_workspace/.agent/habits/lossless_consolidation.yaml +9 -0
- package/python/example_workspace/.agent/habits/no-credential-leak.yaml +41 -0
- package/python/example_workspace/.agent/habits/no-deception.yaml +14 -0
- package/python/example_workspace/.agent/habits/no_hold_narration.yaml +9 -0
- package/python/example_workspace/.agent/habits/one_concern_per_file.yaml +9 -0
- package/python/example_workspace/.agent/habits/optimized-robust.yaml +16 -0
- package/python/example_workspace/.agent/habits/registered_plugin_not_string.yaml +9 -0
- package/python/example_workspace/.agent/habits/rename_as_layer_op.yaml +9 -0
- package/python/example_workspace/.agent/habits/resolve-root-cause.yaml +15 -0
- package/python/example_workspace/.agent/habits/rigorous-commits-no-push.yaml +18 -0
- package/python/example_workspace/.agent/habits/safe-deletion-via-trash.yaml +20 -0
- package/python/example_workspace/.agent/habits/safe_file_permissions.yaml +9 -0
- package/python/example_workspace/.agent/habits/self-healing-portability.yaml +14 -0
- package/python/example_workspace/.agent/habits/shippable-pride.yaml +15 -0
- package/python/example_workspace/.agent/habits/single_source_of_truth.yaml +9 -0
- package/python/example_workspace/.agent/habits/test_of_forever.yaml +9 -0
- package/python/example_workspace/.agent/habits/timeout_and_retry.yaml +9 -0
- package/python/example_workspace/.agent/habits/track_defects_openly.yaml +9 -0
- package/python/example_workspace/.agent/habits/validate-against-real-source.yaml +14 -0
- package/python/example_workspace/.agent/habits/verify-against-docs.yaml +15 -0
- package/python/example_workspace/.agent/habits/verify-functionality-not-syntax.yaml +15 -0
- package/python/example_workspace/.agent/habits/versioning-discipline.yaml +18 -0
- package/python/hermes_plugin/README.md +63 -0
- package/python/hermes_plugin/__init__.py +367 -0
- package/python/hermes_plugin/config.yaml +20 -0
- package/python/hermes_plugin/plugin.yaml +7 -0
- package/python/hermes_plugin/test_plugin.py +166 -0
- package/python/pyproject.toml +19 -0
- package/python/tests/python_parity_test.py +84 -0
package/node/bin/aik.js
ADDED
|
@@ -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();
|