@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,409 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @character-kit interactive installer.
4
+ *
5
+ * One command deploys the WHOLE kit and wires every component:
6
+ * - the enforcement daemon (CORE, out-of-process)
7
+ * - the harness companion (thin client: hook config for any framework)
8
+ * - the acknowledgment monitor (credits the daemon from the ack log)
9
+ * - the monitor watchdog (revives the monitor)
10
+ *
11
+ * Everything resolves via env (AGENT_WORKSPACE / ENFORCER_SOCKET / ACK_ACK_LOG)
12
+ * written to a single .env. No hardcoded paths, no stalls, no force-closes:
13
+ * prompts wait for a response, child processes are spawned detached+unref so
14
+ * the installer always exits cleanly.
15
+ *
16
+ * Usage:
17
+ * node bin/install.js # interactive, asks about each component
18
+ * node bin/install.js --yes # non-interactive, all components on
19
+ * node bin/install.js --all # everything, root mode, generic harness
20
+ * node bin/install.js --workspace X --socket unix --harness claude --user --yes
21
+ */
22
+
23
+ import fs from "fs";
24
+ import os from "os";
25
+ import path from "path";
26
+ import readline from "readline";
27
+ import { spawn } from "child_process";
28
+ import { fileURLToPath, pathToFileURL } from "url";
29
+
30
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
31
+ const REPO = path.resolve(__dirname, "..", ".."); // package root
32
+ const DAEMON = path.join(REPO, "node", "enforcer", "agent_enforcer_daemon.js");
33
+ const MONITOR = path.join(REPO, "deploy", "ack_monitor.py");
34
+ const WATCHDOG = path.join(REPO, "deploy", "ack_watchdog.py");
35
+
36
+ // ─── arg parsing (non-interactive) ────────────────────────────────────────────
37
+ function parseArgs(argv) {
38
+ const out = { workspace: null, socket: null, harness: null, root: null, yes: false, monitor: true, watchdog: true, companion: true, createHabit: false, habitName: null, habitPrompt: null, habitLogic: null, all: false, hookCommand: null };
39
+ for (let i = 0; i < argv.length; i++) {
40
+ const a = argv[i];
41
+ if (a === "--workspace") out.workspace = argv[++i];
42
+ else if (a === "--socket") out.socket = argv[++i];
43
+ else if (a === "--harness") out.harness = argv[++i];
44
+ else if (a === "--agent-dir") out.agentDir = argv[++i];
45
+ else if (a === "--root") out.root = true;
46
+ else if (a === "--user") out.root = false;
47
+ else if (a === "--no-monitor") out.monitor = false;
48
+ else if (a === "--no-watchdog") out.watchdog = false;
49
+ else if (a === "--no-companion") out.companion = false;
50
+ else if (a === "--create-habit") out.createHabit = true;
51
+ else if (a === "--habit-name") out.habitName = argv[++i];
52
+ else if (a === "--habit-prompt") out.habitPrompt = argv[++i];
53
+ else if (a === "--habit-logic") out.habitLogic = argv[++i];
54
+ else if (a === "--yes" || a === "-y") out.yes = true;
55
+ else if (a === "--all") out.all = true;
56
+ else if (a === "--hook-command") out.hookCommand = argv[++i];
57
+ }
58
+ return out;
59
+ }
60
+
61
+ // ─── prompt helper (safe: always resolves; never hangs) ───────────────────────
62
+ function ask(rl, q, def) {
63
+ const suffix = def !== undefined ? ` (default: ${def})` : "";
64
+ return new Promise((resolve) => {
65
+ rl.question(`${q}${suffix}\n> `, (ans) => {
66
+ const v = (ans || "").trim();
67
+ resolve(v === "" && def !== undefined ? def : v);
68
+ });
69
+ });
70
+ }
71
+ function yesNo(rl, q, def = true) {
72
+ return ask(rl, `${q} [y/n]`, def ? "y" : "n").then((a) => /^(y|yes)$/i.test(a || (def ? "y" : "n")));
73
+ }
74
+
75
+ // ─── socket resolution (shared by daemon, companion, monitor, watchdog) ───────
76
+ function resolveSocket(mode, ws) {
77
+ const s = String(mode || "unix").toLowerCase();
78
+ if (s === "tcp" || s === "2") return "tcp://127.0.0.1:8753";
79
+ if (s.startsWith("tcp://")) return s; // literal tcp url
80
+ if (s === "unix" || s === "1" || s === "") return path.join(ws, ".agent", "enforcer.sock");
81
+ return s; // literal unix path
82
+ }
83
+
84
+ // ─── component setup ───────────────────────────────────────────────────────────
85
+ function writeEnvFile(envPath, vars) {
86
+ const lines = Object.entries(vars).map(([k, v]) => `${k}=${v}`);
87
+ fs.writeFileSync(envPath, lines.join("\n") + "\n");
88
+ }
89
+
90
+ function seedHabits(ws) {
91
+ const src = path.join(REPO, "python", "example_workspace", ".agent", "habits");
92
+ const dst = path.join(ws, ".agent", "habits");
93
+ fs.mkdirSync(dst, { recursive: true });
94
+ if (fs.existsSync(src)) {
95
+ for (const f of fs.readdirSync(src)) {
96
+ if (f.endsWith(".yaml") && !fs.existsSync(path.join(dst, f))) {
97
+ fs.copyFileSync(path.join(src, f), path.join(dst, f));
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ function writeConstitution(ws) {
104
+ const dst = path.join(ws, ".agent", "constitution.yaml");
105
+ if (!fs.existsSync(dst)) {
106
+ fs.writeFileSync(dst, [
107
+ "# Agent Character Kit — constitution (hard constraints).",
108
+ "# The daemon embeds safe defaults; this file OVERRIDES/extends them.",
109
+ "hard_constraints:",
110
+ " - no_credential_leak: block any tool call that would expose a secret",
111
+ " - no_destructive_without_confirm: block rm -rf /, mkfs, dd on disks, etc. unless confirmed",
112
+ ].join("\n") + "\n");
113
+ }
114
+ }
115
+
116
+ function launchDaemon(vars) {
117
+ const child = spawn(process.execPath, [DAEMON], {
118
+ env: { ...process.env, ...vars },
119
+ detached: !vars.__root,
120
+ stdio: ["ignore", "pipe", "pipe"],
121
+ });
122
+ child.unref();
123
+
124
+ // Wait for daemon to signal it's ready by reading stdout
125
+ return new Promise((resolve, reject) => {
126
+ let output = '';
127
+ child.stdout.on('data', (data) => {
128
+ output += data.toString();
129
+ if (output.includes('listening on')) {
130
+ resolve(child.pid);
131
+ }
132
+ });
133
+ child.stderr.on('data', (data) => {
134
+ // Ignore stderr for now
135
+ });
136
+ child.on('error', (err) => reject(err));
137
+ child.on('exit', (code) => {
138
+ if (code !== 0) {
139
+ reject(new Error(`Daemon exited with code ${code}`));
140
+ }
141
+ });
142
+ // Timeout after 10 seconds
143
+ setTimeout(() => reject(new Error('Daemon startup timeout')), 10000);
144
+ });
145
+ }
146
+
147
+ function launchMonitorWatchdog(vars, asRoot) {
148
+ // Launch monitor + watchdog as detached background processes (user-mode).
149
+ // For root mode they are typically started via systemd by deploy-agent-enforcer.sh.
150
+ const m = spawn("/usr/bin/env", ["python3", MONITOR], {
151
+ env: { ...process.env, ...vars }, detached: true, stdio: ["ignore", "ignore", "ignore"],
152
+ });
153
+ m.unref();
154
+ const w = spawn("/usr/bin/env", ["python3", WATCHDOG], {
155
+ env: { ...process.env, ...vars }, detached: true, stdio: ["ignore", "ignore", "ignore"],
156
+ });
157
+ w.unref();
158
+ return { monitorPid: m.pid, watchdogPid: w.pid };
159
+ }
160
+
161
+ function createHabitDirect(ws, rawName, prompt, logic) {
162
+ const name = normalizeHabitName(rawName);
163
+ if (!name) throw new Error("habit name required");
164
+ if (!prompt || !prompt.trim()) throw new Error("prompt required");
165
+ if (!logic || !logic.trim()) throw new Error("reasoning required");
166
+ const habitsDir = path.join(ws, ".agent", "habits");
167
+ fs.mkdirSync(habitsDir, { recursive: true });
168
+ const file = path.join(habitsDir, `${name}.yaml`);
169
+ if (fs.existsSync(file)) {
170
+ throw new Error(`habit '${name}' already exists at ${file}`);
171
+ }
172
+ const yaml = [
173
+ `# Habit: ${name}`,
174
+ `# Source question: ${prompt.trim()}`,
175
+ `# Logic: ${logic.trim()}`,
176
+ `name: "${name}"`,
177
+ `prompt: ${JSON.stringify(prompt.trim())}`,
178
+ `enforcement:`,
179
+ ` level: "reminder"`,
180
+ `behavior:`,
181
+ ` kind: "standard"`,
182
+ ` assert: ${JSON.stringify(logic.trim())}`,
183
+ ` evidence: "The agent applies this habit consistently and can state WHY when held."`,
184
+ ` logic: ${JSON.stringify(logic.trim())}`,
185
+ "",
186
+ ].join("\n");
187
+ fs.writeFileSync(file, yaml);
188
+ return name;
189
+ }
190
+
191
+ function normalizeHabitName(raw) {
192
+ return raw
193
+ .trim()
194
+ .toLowerCase()
195
+ .replace(/[^a-z0-9]+/g, "_")
196
+ .replace(/^_+|_+$/g, "");
197
+ }
198
+
199
+ function createHabit(rl, ws) {
200
+ console.log("\n--- Create a new habit ---");
201
+ console.log("A habit is a principle you hold yourself to. The character kit");
202
+ console.log("enforces it on every hold. Created habits are indexed immediately");
203
+ console.log("into the habit database (the workspace habits dir) and take effect");
204
+ console.log("on the next triggering cycle.\n");
205
+
206
+ return ask(rl, "Habit name (becomes the file name, e.g. always-verify-before-ship)")
207
+ .then((rawName) => {
208
+ const name = normalizeHabitName(rawName);
209
+ if (!name) throw new Error("habit name required");
210
+
211
+ console.log("\nThe PROMPT is typically a question you ask YOURSELF to trigger");
212
+ console.log("recognition of the logic behind this habit (e.g. \"Did I verify");
213
+ console.log("this actually runs before claiming done?\"). It is what the");
214
+ console.log("enforcer shows you when this habit is up for acknowledgment.\n");
215
+
216
+ return ask(rl, "Prompt (the self-question that triggers recognition)").then((prompt) => {
217
+ if (!prompt.trim()) throw new Error("prompt required");
218
+ return ask(rl, "Reasoning / logic behind this habit (why it governs your actions)").then((logic) => {
219
+ if (!logic.trim()) throw new Error("reasoning required");
220
+
221
+ const habitsDir = path.join(ws, ".agent", "habits");
222
+ fs.mkdirSync(habitsDir, { recursive: true });
223
+ const file = path.join(habitsDir, `${name}.yaml`);
224
+ if (fs.existsSync(file)) {
225
+ console.log(`\nHabit '${name}' already exists at ${file} — not overwriting.`);
226
+ return name;
227
+ }
228
+ const yaml = [
229
+ `# Habit: ${name}`,
230
+ `# Source question: ${prompt.trim()}`,
231
+ `# Logic: ${logic.trim()}`,
232
+ `name: "${name}"`,
233
+ `prompt: ${JSON.stringify(prompt.trim())}`,
234
+ `enforcement:`,
235
+ ` level: "reminder"`,
236
+ `behavior:`,
237
+ ` kind: "standard"`,
238
+ ` assert: ${JSON.stringify(logic.trim())}`,
239
+ ` evidence: "The agent applies this habit consistently and can state WHY when held."`,
240
+ ` logic: ${JSON.stringify(logic.trim())}`,
241
+ "",
242
+ ].join("\n");
243
+ fs.writeFileSync(file, yaml);
244
+ console.log(`\nCreated habit: ${file}`);
245
+ console.log("Indexed into the habit database. It will be offered on the next");
246
+ console.log("acknowledgment cycle (the daemon loads habits from this dir).");
247
+ return name;
248
+ });
249
+ });
250
+ });
251
+ }
252
+
253
+ // ─── main flow ─────────────────────────────────────────────────────────────────
254
+ async function main() {
255
+ const opts = parseArgs(process.argv.slice(2));
256
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
257
+
258
+ // Non-interactive habit creation: write the file and exit (no daemon needed).
259
+ if (opts.createHabit) {
260
+ const ws = opts.workspace || path.join(os.homedir(), ".agent-character-kit", "workspace");
261
+ const absWs = path.resolve(ws);
262
+ if (!opts.habitName || !opts.habitPrompt || !opts.habitLogic) {
263
+ console.error("ERROR: --create-habit needs --habit-name, --habit-prompt, --habit-logic");
264
+ process.exit(1);
265
+ }
266
+ try {
267
+ const name = createHabitDirect(absWs, opts.habitName, opts.habitPrompt, opts.habitLogic);
268
+ console.log(`Created habit: ${name}`);
269
+ rl.close();
270
+ return;
271
+ } catch (e) {
272
+ console.error("Habit creation failed:", e.message);
273
+ process.exit(1);
274
+ }
275
+ }
276
+
277
+ let ws, socketMode, harness, agentDir, asRoot, doMonitor, doWatchdog, doCompanion;
278
+
279
+ // --all implies --yes (non-interactive) and enables everything
280
+ if (opts.all) {
281
+ opts.yes = true;
282
+ }
283
+
284
+ if (opts.yes) {
285
+ ws = opts.workspace || path.join(os.homedir(), ".agent-character-kit", "workspace");
286
+ socketMode = opts.socket || "unix";
287
+ harness = opts.harness || "generic";
288
+ agentDir = opts.agentDir || path.join(os.homedir(), ".config", "agent-character-kit");
289
+ asRoot = opts.root ?? false;
290
+ doMonitor = opts.monitor;
291
+ doWatchdog = opts.watchdog;
292
+ doCompanion = opts.companion;
293
+ } else {
294
+ console.log("\n=== Agent Character Kit — interactive install ===\n");
295
+ console.log("This sets up the enforcement daemon, your harness companion,");
296
+ console.log("and the acknowledgment monitor/watchdog. Every step is optional");
297
+ console.log("to skip; press Enter to accept the default.\n");
298
+
299
+ ws = await ask(rl, "Where should the agent workspace live? (habits, socket, constitution)",
300
+ path.join(os.homedir(), ".agent-character-kit", "workspace"));
301
+ const absWs = path.resolve(ws);
302
+ socketMode = await ask(rl, "Socket mode? [unix | tcp]", "unix");
303
+ harness = (await ask(rl, "Which harness? [claude | cursor | gemini | opencode | generic]", "generic")).toLowerCase();
304
+ agentDir = await ask(rl, "Where is your agent's config/home directory? (companion config gets dropped here)",
305
+ path.join(os.homedir(), ".config", "agent-character-kit"));
306
+ asRoot = await yesNo(rl, "Install as ROOT (system-wide, self-respawning)?", false);
307
+ doCompanion = await yesNo(rl, "Set up the harness companion (thin client hook config)?", true);
308
+ doMonitor = await yesNo(rl, "Set up the acknowledgment monitor (credits daemon from ack log)?", true);
309
+ doWatchdog = await yesNo(rl, "Set up the monitor watchdog (revives monitor if it dies)?", true);
310
+
311
+ // Habit creator — create as many as wanted, then continue the install.
312
+ while (await yesNo(rl, "Create a habit now (interactive)?", false)) {
313
+ try {
314
+ await createHabit(rl, absWs);
315
+ } catch (e) {
316
+ console.log("Habit not created:", e.message);
317
+ }
318
+ }
319
+ }
320
+
321
+ rl.close();
322
+
323
+ const absWs = path.resolve(ws);
324
+ const sock = resolveSocket(socketMode, absWs);
325
+ const ackLog = path.join(absWs, ".agent", "ack.jsonl");
326
+ const vars = {
327
+ AGENT_WORKSPACE: absWs,
328
+ ENFORCER_SOCKET: sock,
329
+ ACK_ACK_LOG: ackLog,
330
+ ACK_MONITOR_PID: path.join(absWs, ".agent", "ack-monitor.pid"),
331
+ ACK_MONITOR_STATE: path.join(absWs, ".agent", "ack-monitor.pos"),
332
+ ACK_WATCHDOG_PID: path.join(absWs, ".agent", "ack-watchdog.pid"),
333
+ ACK_MONITOR_BIN: MONITOR,
334
+ __root: asRoot,
335
+ };
336
+
337
+ // Handle --all flag: everything on, companion for generic harness, root mode
338
+ if (opts.all) {
339
+ asRoot = true;
340
+ doMonitor = true;
341
+ doWatchdog = true;
342
+ doCompanion = true;
343
+ harness = opts.harness || "generic";
344
+ agentDir = opts.agentDir || path.join(os.homedir(), ".config", "agent-character-kit");
345
+ }
346
+
347
+ // 1. workspace scaffold
348
+ fs.mkdirSync(path.join(absWs, ".agent", "habits"), { recursive: true });
349
+ seedHabits(absWs);
350
+ writeConstitution(absWs);
351
+
352
+ // 2. single .env every component reads
353
+ const repoEnv = path.join(REPO, ".env");
354
+ const wsEnv = path.join(absWs, ".env");
355
+ const envLines = {
356
+ AGENT_WORKSPACE: absWs,
357
+ ENFORCER_SOCKET: sock,
358
+ ACK_ACK_LOG: ackLog,
359
+ ACK_MONITOR_PID: vars.ACK_MONITOR_PID,
360
+ ACK_MONITOR_STATE: vars.ACK_MONITOR_STATE,
361
+ ACK_WATCHDOG_PID: vars.ACK_WATCHDOG_PID,
362
+ ACK_MONITOR_BIN: MONITOR,
363
+ };
364
+ writeEnvFile(repoEnv, envLines);
365
+ writeEnvFile(wsEnv, envLines);
366
+
367
+ // 3. daemon
368
+ const daemonPid = await launchDaemon(vars);
369
+
370
+ // 4. companion (thin client hook config for any harness)
371
+ let companionMsg = "skipped";
372
+ if (doCompanion) {
373
+ const { generateConfig } = await import("../src/index.js");
374
+ const hookCmd = opts.hookCommand || "npx aik hook";
375
+ const config = generateConfig(harness, hookCmd);
376
+ companionMsg = `Hook config for ${harness}:\n${JSON.stringify(config, null, 2)}`;
377
+ }
378
+
379
+ // 5. monitor + watchdog
380
+ let monitorMsg = "skipped";
381
+ if (doMonitor || doWatchdog) {
382
+ const procs = launchMonitorWatchdog(vars, asRoot);
383
+ monitorMsg = `monitor pid ${procs.monitorPid}, watchdog pid ${procs.watchdogPid}`;
384
+ }
385
+
386
+ // 6. summary (informative, no force-close)
387
+ console.log("\n=== Install summary ===");
388
+ console.log("Workspace: ", absWs);
389
+ console.log("Socket: ", sock);
390
+ console.log("Ack log: ", ackLog);
391
+ console.log("Habits seeded: ", path.join(absWs, ".agent", "habits"));
392
+ console.log(".env written: ", `${repoEnv} + ${wsEnv}`);
393
+ console.log("Daemon pid: ", daemonPid);
394
+ console.log("Companion: ", companionMsg);
395
+ console.log("Monitor/Watch: ", monitorMsg);
396
+ console.log("\nDone. Add the companion hook config to your harness to activate enforcement.");
397
+ console.log("The daemon holds every 5th call until you acknowledge 2 habits");
398
+ console.log("with a real, situation-tied reason. No filler, no reuse.\n");
399
+ }
400
+
401
+ const __isCLI = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
402
+ if (__isCLI) {
403
+ main().catch((e) => {
404
+ console.error("Install failed:", e.message);
405
+ process.exit(1);
406
+ });
407
+ }
408
+
409
+ export { parseArgs, resolveSocket };