arkaos 2.10.0 → 2.11.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.
Files changed (46) hide show
  1. package/README.md +318 -107
  2. package/VERSION +1 -1
  3. package/config/hooks/cwd-changed.ps1 +144 -0
  4. package/config/hooks/post-tool-use.ps1 +347 -0
  5. package/config/hooks/post-tool-use.sh +6 -6
  6. package/config/hooks/pre-compact.ps1 +238 -0
  7. package/config/hooks/pre-compact.sh +10 -6
  8. package/config/hooks/session-start.ps1 +109 -0
  9. package/config/hooks/session-start.sh +1 -1
  10. package/config/hooks/user-prompt-submit.ps1 +287 -0
  11. package/config/hooks/user-prompt-submit.sh +5 -2
  12. package/config/statusline.ps1 +160 -0
  13. package/core/cognition/__pycache__/__init__.cpython-313.pyc +0 -0
  14. package/core/cognition/capture/__pycache__/__init__.cpython-313.pyc +0 -0
  15. package/core/cognition/capture/__pycache__/collector.cpython-313.pyc +0 -0
  16. package/core/cognition/capture/__pycache__/store.cpython-313.pyc +0 -0
  17. package/core/cognition/insights/__pycache__/__init__.cpython-313.pyc +0 -0
  18. package/core/cognition/insights/__pycache__/store.cpython-313.pyc +0 -0
  19. package/core/cognition/memory/__pycache__/__init__.cpython-313.pyc +0 -0
  20. package/core/cognition/memory/__pycache__/obsidian.cpython-313.pyc +0 -0
  21. package/core/cognition/memory/__pycache__/schemas.cpython-313.pyc +0 -0
  22. package/core/cognition/memory/__pycache__/vector.cpython-313.pyc +0 -0
  23. package/core/cognition/memory/__pycache__/writer.cpython-313.pyc +0 -0
  24. package/core/cognition/research/__pycache__/__init__.cpython-313.pyc +0 -0
  25. package/core/cognition/research/__pycache__/profiler.cpython-313.pyc +0 -0
  26. package/core/cognition/scheduler/__pycache__/__init__.cpython-313.pyc +0 -0
  27. package/core/cognition/scheduler/__pycache__/cli.cpython-313.pyc +0 -0
  28. package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
  29. package/core/cognition/scheduler/__pycache__/platform.cpython-313.pyc +0 -0
  30. package/core/cognition/scheduler/daemon.py +77 -21
  31. package/core/cognition/scheduler/platform.py +43 -12
  32. package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
  33. package/core/knowledge/vector_store.py +50 -25
  34. package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
  35. package/core/synapse/layers.py +2 -2
  36. package/installer/adapters/claude-code.js +72 -45
  37. package/installer/cli.js +19 -6
  38. package/installer/doctor.js +130 -18
  39. package/installer/index.js +592 -149
  40. package/installer/platform.js +20 -0
  41. package/installer/prompts.js +109 -5
  42. package/installer/python-resolver.js +251 -0
  43. package/installer/update.js +497 -62
  44. package/package.json +1 -1
  45. package/pyproject.toml +2 -2
  46. package/scripts/start-dashboard.ps1 +271 -0
@@ -1,7 +1,11 @@
1
- import { existsSync, readFileSync, writeFileSync, copyFileSync, chmodSync, mkdirSync } from "node:fs";
1
+ import { existsSync, readFileSync, writeFileSync, copyFileSync, chmodSync, mkdirSync, readdirSync, cpSync, statSync } from "node:fs";
2
2
  import { join, dirname, resolve } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { execSync } from "node:child_process";
5
+ import { ensureVenv, getArkaosPython, pipInstall } from "./python-resolver.js";
6
+ import { getRuntimeConfig } from "./detect-runtime.js";
7
+ import { loadAdapter } from "./index.js";
8
+ import { IS_WINDOWS, HOOK_EXT } from "./platform.js";
5
9
  import { fileURLToPath } from "node:url";
6
10
 
7
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -18,13 +22,32 @@ export async function update() {
18
22
  process.exit(1);
19
23
  }
20
24
 
25
+ // ── Legacy path migration ──────────────────────────────────────────────
26
+ // Older v2 hooks wrote their state (gotchas, hook metrics, session
27
+ // digests) into `~/.arka-os/` — the v1 path — instead of the canonical
28
+ // v2 runtime directory `~/.arkaos/`. Post-fix the hooks now write to
29
+ // `.arkaos`, so any existing data in `.arka-os` needs to be migrated
30
+ // forward on the next update. We COPY (not move) because v1 tooling
31
+ // such as `bin/arka*` and the kb/ scripts also read from `.arka-os`
32
+ // and must keep working during co-existence; the migrate command
33
+ // remains the way to do a destructive v1 -> v2 cutover.
34
+ try {
35
+ migrateLegacyHookState(homedir(), installDir);
36
+ } catch (err) {
37
+ console.log(` \u26a0 Legacy hook state migration skipped: ${err.message}`);
38
+ }
39
+
21
40
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
22
41
  const profile = existsSync(profilePath) ? JSON.parse(readFileSync(profilePath, "utf-8")) : {};
23
42
 
24
43
  // Check latest version
25
44
  let latestVersion = VERSION;
26
45
  try {
27
- latestVersion = execSync("npm view arkaos version 2>/dev/null", { stdio: "pipe" }).toString().trim();
46
+ // stderr is suppressed via stdio: "ignore" rather than `2>/dev/null`
47
+ // so the command works under cmd.exe on Windows just as well as bash.
48
+ latestVersion = execSync("npm view arkaos version", {
49
+ stdio: ["pipe", "pipe", "ignore"],
50
+ }).toString().trim();
28
51
  } catch {}
29
52
 
30
53
  console.log(`
@@ -35,92 +58,186 @@ export async function update() {
35
58
  Latest: v${latestVersion}
36
59
  `);
37
60
 
38
- if (manifest.version === latestVersion && manifest.version === VERSION && !process.argv.includes("--force")) {
61
+ // Dev-checkout detection: when ArkaOS is being run from a local git
62
+ // clone (as opposed to a published `npx arkaos@latest` install), the
63
+ // version number in package.json is a poor signal of whether there's
64
+ // anything to re-deploy. A contributor working on a feature branch
65
+ // can have 20+ commits of real code changes with the version string
66
+ // still at the last release — every `npx arkaos@file:. update` would
67
+ // refuse to run with a misleading "already up to date" message.
68
+ //
69
+ // Signal: ARKAOS_ROOT is a dev checkout iff it contains a `.git/`
70
+ // directory. A published npm package never ships `.git/`; a local
71
+ // `git clone` always has it. This is robust across install methods
72
+ // (npx cache, local file path, global install) because none of them
73
+ // preserve `.git` in their extraction, but a bare clone always does.
74
+ //
75
+ // When detected, we skip the version-equality gate entirely and
76
+ // always run the full update sequence. Published installs still get
77
+ // the happy-path "already up to date" when the version matches.
78
+ const isDevCheckout = existsSync(join(ARKAOS_ROOT, ".git"));
79
+ const versionsMatch = manifest.version === latestVersion && manifest.version === VERSION;
80
+ const forceRequested = process.argv.includes("--force");
81
+
82
+ if (versionsMatch && !forceRequested && !isDevCheckout) {
39
83
  console.log(" ✓ Already up to date.\n");
40
84
  return;
41
85
  }
42
86
 
87
+ if (isDevCheckout && versionsMatch && !forceRequested) {
88
+ console.log(" ℹ Dev checkout detected — running update even though version matches.\n");
89
+ }
90
+
43
91
  console.log(" Updating (keeping your configuration)...\n");
44
92
 
45
- const pythonCmd = manifest.pythonCmd || "python3";
93
+ // ── 1. Update Python deps (using venv) ──
94
+ console.log(" [1/8] Updating Python dependencies...");
46
95
 
47
- // ── 1. Update Python deps ──
48
- console.log(" [1/6] Updating Python dependencies...");
49
- try {
50
- const coreDeps = "pyyaml pydantic rich click jinja2";
51
- execSync(`${pythonCmd} -m pip install --upgrade ${coreDeps} --quiet`, { stdio: "pipe", timeout: 120000 });
52
- console.log(" ✓ Core deps updated");
96
+ // Ensure venv exists (creates one if missing — fixes PEP 668)
97
+ const venvOk = ensureVenv((msg) => console.log(msg));
98
+ if (!venvOk) {
99
+ console.log(" \u26a0 Could not create venv trying system Python with PEP 668 handling");
100
+ }
53
101
 
54
- // Only update optional deps if they were installed before
55
- try {
56
- execSync(`${pythonCmd} -c "import fastembed"`, { stdio: "pipe" });
57
- execSync(`${pythonCmd} -m pip install --upgrade fastembed sqlite-vss --quiet`, { stdio: "pipe", timeout: 180000 });
58
- console.log(" ✓ Knowledge deps updated");
59
- } catch { /* not installed, skip */ }
102
+ const pythonCmd = getArkaosPython();
103
+ const log = (msg) => console.log(msg);
60
104
 
61
- try {
62
- execSync(`${pythonCmd} -c "import fastapi"`, { stdio: "pipe" });
63
- execSync(`${pythonCmd} -m pip install --upgrade fastapi uvicorn --quiet`, { stdio: "pipe", timeout: 60000 });
64
- console.log(" ✓ Dashboard deps updated");
65
- } catch { /* not installed, skip */ }
105
+ // Core deps (always upgrade)
106
+ if (pipInstall("pyyaml pydantic rich click jinja2", { upgrade: true, log })) {
107
+ console.log(" \u2713 Core deps updated");
108
+ } else {
109
+ console.log(" \u26a0 Core deps update failed");
110
+ }
66
111
 
67
- try {
68
- execSync(`${pythonCmd} -m pip install -e "${ARKAOS_ROOT}" --quiet`, { stdio: "pipe", timeout: 60000 });
69
- } catch {}
70
- } catch (err) {
71
- console.log(` ⚠ Some deps failed: ${err.message.slice(0, 80)}`);
112
+ // Only update optional deps if they were installed before
113
+ const pyCheck = (mod) => {
114
+ try { execSync(`"${pythonCmd}" -c "import ${mod}"`, { stdio: "pipe" }); return true; } catch { return false; }
115
+ };
116
+
117
+ // Knowledge deps (fastembed + sqlite-vec) are upgraded one-at-a-time
118
+ // so a failure of one cannot drag the other down with it.
119
+ if (pyCheck("fastembed")) {
120
+ const fastembedOk = pipInstall("fastembed", { upgrade: true, log, timeout: 180000 });
121
+ const sqliteVssOk = pipInstall("sqlite-vec", { upgrade: true, log, timeout: 180000 });
122
+ if (fastembedOk && sqliteVssOk) {
123
+ console.log(" \u2713 Knowledge deps updated (fastembed, sqlite-vec)");
124
+ } else if (fastembedOk) {
125
+ console.log(" \u26a0 fastembed upgraded but sqlite-vec failed \u2014 semantic search degraded");
126
+ } else if (sqliteVssOk) {
127
+ console.log(" \u26a0 sqlite-vec upgraded but fastembed failed \u2014 embedding pipeline degraded");
128
+ } else {
129
+ console.log(" \u26a0 Knowledge deps upgrade failed \u2014 run: npx arkaos doctor");
130
+ }
131
+ }
132
+
133
+ if (pyCheck("fastapi")) {
134
+ if (pipInstall("fastapi uvicorn", { upgrade: true, log, timeout: 60000 })) {
135
+ console.log(" \u2713 Dashboard deps updated");
136
+ }
137
+ }
138
+
139
+ // Always install ArkaOS core engine
140
+ if (pipInstall("", { editable: ARKAOS_ROOT, log, timeout: 60000 })) {
141
+ console.log(" \u2713 ArkaOS core engine installed");
142
+ } else {
143
+ console.log(" \u26a0 Core engine install failed — run: npx arkaos doctor");
72
144
  }
73
145
 
74
146
  // ── 2. Update config files ──
75
- console.log(" [2/6] Updating configuration...");
147
+ console.log(" [2/8] Updating configuration...");
76
148
  const constitutionSrc = join(ARKAOS_ROOT, "config", "constitution.yaml");
149
+ mkdirSync(join(installDir, "config"), { recursive: true });
77
150
  if (existsSync(constitutionSrc)) {
78
- mkdirSync(join(installDir, "config"), { recursive: true });
79
151
  copyFileSync(constitutionSrc, join(installDir, "config", "constitution.yaml"));
80
152
  console.log(" ✓ Constitution updated");
81
153
  }
154
+ const statuslineFile = IS_WINDOWS ? "statusline.ps1" : "statusline.sh";
155
+ const statuslineSrc = join(ARKAOS_ROOT, "config", statuslineFile);
156
+ if (existsSync(statuslineSrc)) {
157
+ copyFileSync(statuslineSrc, join(installDir, "config", statuslineFile));
158
+ console.log(" ✓ Statusline updated");
159
+ }
82
160
 
83
161
  // ── 3. Update hooks ──
84
- console.log(" [3/6] Updating hooks...");
85
- const hookMap = {
86
- "session-start.sh": "session-start.sh",
87
- "user-prompt-submit.sh": "user-prompt-submit.sh",
88
- "post-tool-use.sh": "post-tool-use.sh",
89
- "pre-compact.sh": "pre-compact.sh",
90
- "cwd-changed.sh": "cwd-changed.sh",
91
- };
162
+ console.log(" [3/8] Updating hooks...");
163
+ // Keep this list in lockstep with installer/index.js::installHooks and
164
+ // installer/adapters/claude-code.js::hookCommand. Platform-aware: .ps1
165
+ // on Windows, .sh everywhere else.
166
+ const hookNames = [
167
+ "session-start",
168
+ "user-prompt-submit",
169
+ "post-tool-use",
170
+ "pre-compact",
171
+ "cwd-changed",
172
+ ];
173
+ const hookExt = HOOK_EXT;
92
174
  const srcHooksDir = join(ARKAOS_ROOT, "config", "hooks");
93
175
  const destHooksDir = join(installDir, "config", "hooks");
94
176
  mkdirSync(destHooksDir, { recursive: true });
95
177
 
96
- for (const [src, dest] of Object.entries(hookMap)) {
97
- const srcPath = join(srcHooksDir, src);
98
- const destPath = join(destHooksDir, dest);
178
+ for (const name of hookNames) {
179
+ const filename = `${name}${hookExt}`;
180
+ const srcPath = join(srcHooksDir, filename);
181
+ const destPath = join(destHooksDir, filename);
99
182
  if (existsSync(srcPath)) {
100
183
  let content = readFileSync(srcPath, "utf-8");
101
- content = content.replace(
102
- /ARKAOS_ROOT="\$\{ARKA_OS:-\$HOME\/\.claude\/skills\/arkaos\}"/g,
103
- `ARKAOS_ROOT="${ARKAOS_ROOT}"`
104
- );
105
- content = content.replace(
106
- /ARKAOS_HOME="\$\{HOME\}\/\.arkaos"/g,
107
- `ARKAOS_HOME="${installDir}"`
108
- );
184
+ // Legacy ARKAOS_ROOT/ARKAOS_HOME text injection only applies to
185
+ // the bash hooks; the PowerShell ports resolve paths at runtime
186
+ // from the install manifest.
187
+ if (hookExt === ".sh") {
188
+ content = content.replace(
189
+ /ARKAOS_ROOT="\$\{ARKA_OS:-\$HOME\/\.claude\/skills\/arkaos\}"/g,
190
+ `ARKAOS_ROOT="${ARKAOS_ROOT}"`
191
+ );
192
+ content = content.replace(
193
+ /ARKAOS_HOME="\$\{HOME\}\/\.arkaos"/g,
194
+ `ARKAOS_HOME="${installDir}"`
195
+ );
196
+ }
109
197
  writeFileSync(destPath, content);
110
198
  try { chmodSync(destPath, 0o755); } catch {}
111
199
  }
112
200
  }
113
- console.log(" ✓ Hooks updated");
201
+ console.log(" ✓ Hook scripts updated");
202
+
203
+ // Re-register hooks in the runtime's settings file.
204
+ // Without this, updating from an older version leaves settings.json
205
+ // frozen at the previous hook spec (missing new hooks, stale timeouts).
206
+ // Safe on all platforms: identical to the call init.js makes on fresh
207
+ // install, which is already validated on macOS and Linux.
208
+ try {
209
+ const runtimeId = manifest.runtime || "claude-code";
210
+ const runtimeConfig = getRuntimeConfig(runtimeId);
211
+ if (runtimeConfig) {
212
+ const adapter = await loadAdapter(runtimeId);
213
+ adapter.configureHooks(runtimeConfig, installDir);
214
+ console.log(" ✓ Hooks re-registered in settings");
215
+ }
216
+ } catch (err) {
217
+ console.log(` ⚠ Could not re-register hooks: ${err.message}`);
218
+ }
114
219
 
115
220
  // ── 4. Update CLI wrapper + user CLAUDE.md ──
116
- console.log(" [4/7] Updating CLI wrapper and user instructions...");
221
+ console.log(" [4/8] Updating CLI wrapper and user instructions...");
117
222
  const binDir = join(installDir, "bin");
118
223
  mkdirSync(binDir, { recursive: true });
119
- const wrapperSrc = join(ARKAOS_ROOT, "bin", "arka-claude");
120
- if (existsSync(wrapperSrc)) {
121
- copyFileSync(wrapperSrc, join(binDir, "arka-claude"));
122
- try { chmodSync(join(binDir, "arka-claude"), 0o755); } catch {}
123
- console.log(" arka-claude wrapper updated");
224
+
225
+ // Platform-aware: bash wrapper on Unix; .ps1 + .cmd shim on Windows.
226
+ if (IS_WINDOWS) {
227
+ const psSrc = join(ARKAOS_ROOT, "bin", "arka-claude.ps1");
228
+ const cmdSrc = join(ARKAOS_ROOT, "bin", "arka-claude.cmd");
229
+ if (existsSync(psSrc)) copyFileSync(psSrc, join(binDir, "arka-claude.ps1"));
230
+ if (existsSync(cmdSrc)) copyFileSync(cmdSrc, join(binDir, "arka-claude.cmd"));
231
+ if (existsSync(psSrc) || existsSync(cmdSrc)) {
232
+ console.log(" ✓ arka-claude wrapper updated (.cmd + .ps1)");
233
+ }
234
+ } else {
235
+ const wrapperSrc = join(ARKAOS_ROOT, "bin", "arka-claude");
236
+ if (existsSync(wrapperSrc)) {
237
+ copyFileSync(wrapperSrc, join(binDir, "arka-claude"));
238
+ try { chmodSync(join(binDir, "arka-claude"), 0o755); } catch {}
239
+ console.log(" ✓ arka-claude wrapper updated");
240
+ }
124
241
  }
125
242
  const userClaudeMd = join(homedir(), ".claude", "CLAUDE.md");
126
243
  const claudeMdSrc = join(ARKAOS_ROOT, "config", "user-claude.md");
@@ -130,10 +247,25 @@ export async function update() {
130
247
  console.log(" ✓ ~/.claude/CLAUDE.md updated");
131
248
  }
132
249
 
133
- // ── 5. Update /arka skill ──
134
- console.log(" [5/7] Updating /arka skill...");
250
+ // ── 5. Update Cognitive Scheduler ──
251
+ console.log(" [5/8] Updating cognitive scheduler...");
252
+ updateCognitiveScheduler(installDir, ARKAOS_ROOT);
253
+
254
+ // ── 6. Update /arka skill + department skills + sub-skills + agents ──
255
+ // Mirrors the full deployment in installer/index.js::installSkill so
256
+ // that `npx arkaos update` re-deploys the same surface area a fresh
257
+ // install creates. Before this change, update.js only refreshed the
258
+ // main `/arka` skill, so any department skill (arka-dev, arka-brand,
259
+ // etc.) or sub-skill (arka-code-review, arka-viral, etc.) or agent
260
+ // persona added after the original install was silently missing on
261
+ // upgrade. Discovered during Marlon's bake-in: 233 top-level arka-*
262
+ // skills on his WSL (deployed long ago by install.sh) vs 1 skill on
263
+ // his Windows install (only the main /arka). The Node installer
264
+ // never deployed anything else.
265
+ console.log(" [6/8] Updating /arka skill...");
266
+ const skillsBase = join(homedir(), ".claude", "skills");
135
267
  const skillSrc = join(ARKAOS_ROOT, "arka", "SKILL.md");
136
- const skillDest = join(homedir(), ".claude", "skills", "arka");
268
+ const skillDest = join(skillsBase, "arka");
137
269
  mkdirSync(skillDest, { recursive: true });
138
270
  if (existsSync(skillSrc)) {
139
271
  copyFileSync(skillSrc, join(skillDest, "SKILL.md"));
@@ -142,15 +274,107 @@ export async function update() {
142
274
  console.log(" ✓ /arka skill updated");
143
275
  }
144
276
 
145
- // ── 6. Update .repo-path ──
146
- console.log(" [6/7] Updating references...");
277
+ // Department skills + sub-skills + agent personas.
278
+ // Keep in sync with installer/index.js::installSkill if anything
279
+ // changes about how top-level skills or agents are structured, both
280
+ // functions must be updated together.
281
+ const listSubdirs = (parent) => {
282
+ if (!existsSync(parent)) return [];
283
+ try {
284
+ return readdirSync(parent, { withFileTypes: true })
285
+ .filter((e) => e.isDirectory())
286
+ .map((e) => e.name);
287
+ } catch {
288
+ return [];
289
+ }
290
+ };
291
+ const copyResources = (src, dest) => {
292
+ for (const res of ["scripts", "references", "assets"]) {
293
+ const s = join(src, res);
294
+ if (!existsSync(s)) continue;
295
+ try { cpSync(s, join(dest, res), { recursive: true }); } catch {}
296
+ }
297
+ };
298
+ const deployTop = (src, arkaName) => {
299
+ const md = join(src, "SKILL.md");
300
+ if (!existsSync(md)) return false;
301
+ const dest = join(skillsBase, arkaName);
302
+ mkdirSync(dest, { recursive: true });
303
+ copyFileSync(md, join(dest, "SKILL.md"));
304
+ copyResources(src, dest);
305
+ return true;
306
+ };
307
+
308
+ const deptRoot = join(ARKAOS_ROOT, "departments");
309
+ let deptCount = 0;
310
+ let subCount = 0;
311
+ for (const dept of listSubdirs(deptRoot)) {
312
+ if (deployTop(join(deptRoot, dept), `arka-${dept}`)) deptCount++;
313
+ for (const sub of listSubdirs(join(deptRoot, dept, "skills"))) {
314
+ if (deployTop(join(deptRoot, dept, "skills", sub), `arka-${sub}`)) subCount++;
315
+ }
316
+ }
317
+ if (deptCount > 0) {
318
+ console.log(` ✓ ${deptCount} department skills updated`);
319
+ }
320
+ if (subCount > 0) {
321
+ console.log(` ✓ ${subCount} sub-skills updated`);
322
+ }
323
+
324
+ const agentsBase = join(homedir(), ".claude", "agents");
325
+ mkdirSync(agentsBase, { recursive: true });
326
+ let agentCount = 0;
327
+ for (const dept of listSubdirs(deptRoot)) {
328
+ const agentsSrc = join(deptRoot, dept, "agents");
329
+ if (!existsSync(agentsSrc)) continue;
330
+ try {
331
+ for (const file of readdirSync(agentsSrc)) {
332
+ if (!file.endsWith(".md")) continue;
333
+ const srcFile = join(agentsSrc, file);
334
+ try { if (!statSync(srcFile).isFile()) continue; } catch { continue; }
335
+ const base = file.replace(/\.md$/, "");
336
+ copyFileSync(srcFile, join(agentsBase, `arka-${base}.md`));
337
+ agentCount++;
338
+ }
339
+ } catch {}
340
+ }
341
+ if (agentCount > 0) {
342
+ console.log(` ✓ ${agentCount} agent personas updated`);
343
+ }
344
+
345
+ // ── 7. Update .repo-path + .arkaos-root ──
346
+ // Two references point at the source repo. Both MUST be updated on
347
+ // every update pass, otherwise running `npx arkaos update` from a
348
+ // new source directory leaves one file still pointing at the old
349
+ // location and the hooks get confused about which repo to read
350
+ // VERSION from.
351
+ //
352
+ // - `~/.arkaos/.repo-path`: read by session-start.ps1 / .sh to
353
+ // find the VERSION file for the drift banner.
354
+ // - `~/.claude/skills/arkaos/.arkaos-root`: used by the skills
355
+ // alias to locate the source repo root from inside Claude Code.
356
+ //
357
+ // installer/index.js writes both in step 11 of the fresh install
358
+ // flow; update.js previously only wrote the first, which is a
359
+ // latent bug that surfaces any time a user runs update from a
360
+ // different clone than the original install.
361
+ console.log(" [7/8] Updating references...");
147
362
  writeFileSync(join(installDir, ".repo-path"), ARKAOS_ROOT);
148
- console.log(" Repo path updated");
363
+ const skillsArkaosDir = join(homedir(), ".claude", "skills", "arkaos");
364
+ if (existsSync(skillsArkaosDir)) {
365
+ writeFileSync(join(skillsArkaosDir, ".arkaos-root"), ARKAOS_ROOT);
366
+ console.log(" ✓ Repo path + skills alias updated");
367
+ } else {
368
+ // Fresh install never ran (or user deleted the skills alias);
369
+ // don't recreate it here — that's the install flow's job.
370
+ console.log(" ✓ Repo path updated (skills alias not present)");
371
+ }
149
372
 
150
- // ── 7. Update manifest ──
151
- console.log(" [7/7] Finalizing...");
373
+ // ── 8. Update manifest ──
374
+ console.log(" [8/8] Finalizing...");
152
375
  manifest.version = VERSION;
153
376
  manifest.repoRoot = ARKAOS_ROOT;
377
+ manifest.pythonCmd = pythonCmd;
154
378
  manifest.updatedAt = new Date().toISOString();
155
379
  writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
156
380
  console.log(" ✓ Manifest updated");
@@ -184,3 +408,214 @@ export async function update() {
184
408
  detect the update and sync all your projects.
185
409
  `);
186
410
  }
411
+
412
+ function ensureDir(dir) {
413
+ if (!existsSync(dir)) {
414
+ mkdirSync(dir, { recursive: true });
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Copy v2 hook state files from the legacy ~/.arka-os/ directory into
420
+ * the canonical ~/.arkaos/ runtime directory. Safe to run every update:
421
+ * - If ~/.arka-os/ does not exist, does nothing.
422
+ * - If a destination file already exists and is non-empty, it is NOT
423
+ * overwritten (user data wins).
424
+ * - Source files are left in place so that v1 tooling (bin/arka*, the
425
+ * kb/ scripts, and `arkaos migrate`) keeps working during
426
+ * coexistence. A destructive cleanup happens only via
427
+ * `arkaos migrate` which is the documented one-way cutover.
428
+ *
429
+ * State migrated: gotchas.json, hook-metrics.json, session-digests/*.md.
430
+ * Everything else in ~/.arka-os/ (v1 profile, capabilities, kb-jobs,
431
+ * env file, media, pro content, ...) is intentionally ignored.
432
+ */
433
+ function migrateLegacyHookState(homeDir, installDir) {
434
+ const legacyDir = join(homeDir, ".arka-os");
435
+ if (!existsSync(legacyDir)) return;
436
+
437
+ const targetDir = installDir;
438
+ ensureDir(targetDir);
439
+
440
+ let migrated = 0;
441
+
442
+ // Simple files: only copy if the target is missing or empty.
443
+ for (const name of ["gotchas.json", "hook-metrics.json"]) {
444
+ const src = join(legacyDir, name);
445
+ if (!existsSync(src)) continue;
446
+ const dst = join(targetDir, name);
447
+ let dstEmpty = true;
448
+ if (existsSync(dst)) {
449
+ try {
450
+ const content = readFileSync(dst, "utf-8").trim();
451
+ dstEmpty = content.length === 0 || content === "[]" || content === "{}";
452
+ } catch {
453
+ dstEmpty = true;
454
+ }
455
+ }
456
+ if (dstEmpty) {
457
+ try {
458
+ copyFileSync(src, dst);
459
+ migrated++;
460
+ } catch {}
461
+ }
462
+ }
463
+
464
+ // Session digests: copy each .md file that isn't already present in
465
+ // the target. This preserves history but avoids clobbering anything
466
+ // the v2 runtime may have already written.
467
+ const srcDigests = join(legacyDir, "session-digests");
468
+ if (existsSync(srcDigests)) {
469
+ const dstDigests = join(targetDir, "session-digests");
470
+ ensureDir(dstDigests);
471
+ try {
472
+ for (const f of readdirSync(srcDigests)) {
473
+ if (!f.endsWith(".md")) continue;
474
+ const srcFile = join(srcDigests, f);
475
+ const dstFile = join(dstDigests, f);
476
+ if (!existsSync(dstFile)) {
477
+ try { copyFileSync(srcFile, dstFile); migrated++; } catch {}
478
+ }
479
+ }
480
+ } catch {}
481
+ }
482
+
483
+ if (migrated > 0) {
484
+ console.log(` \u2713 Migrated ${migrated} legacy hook state file(s) from ~/.arka-os/`);
485
+ console.log(` (original files left in place for v1 coexistence)`);
486
+ }
487
+ }
488
+
489
+ function updateCognitiveScheduler(installDir, arkaosRoot) {
490
+ const platform = process.platform;
491
+
492
+ // 1. Update schedule config
493
+ const schedSrc = join(arkaosRoot, "config", "cognition", "schedules.yaml");
494
+ if (existsSync(schedSrc)) {
495
+ copyFileSync(schedSrc, join(installDir, "schedules.yaml"));
496
+ console.log(" \u2713 Schedule config updated");
497
+ }
498
+
499
+ // 2. Update prompt files
500
+ const promptsDir = join(installDir, "cognition", "prompts");
501
+ ensureDir(promptsDir);
502
+ const promptsSrc = join(arkaosRoot, "config", "cognition", "prompts");
503
+ if (existsSync(promptsSrc)) {
504
+ for (const f of readdirSync(promptsSrc)) {
505
+ copyFileSync(join(promptsSrc, f), join(promptsDir, f));
506
+ }
507
+ console.log(" \u2713 Cognitive prompts updated");
508
+ }
509
+
510
+ // 3. Update daemon script and core modules
511
+ const daemonSrc = join(arkaosRoot, "bin", "scheduler-daemon.py");
512
+ const binDir = join(installDir, "bin");
513
+ ensureDir(binDir);
514
+ if (existsSync(daemonSrc)) {
515
+ copyFileSync(daemonSrc, join(binDir, "scheduler-daemon.py"));
516
+ try { chmodSync(join(binDir, "scheduler-daemon.py"), 0o755); } catch {}
517
+ console.log(" \u2713 Scheduler daemon updated");
518
+ }
519
+
520
+ // 3b. Update scheduler core modules (daemon imports these at runtime)
521
+ const schedulerModules = [
522
+ "core/cognition/scheduler/__init__.py",
523
+ "core/cognition/scheduler/daemon.py",
524
+ "core/cognition/scheduler/platform.py",
525
+ "core/cognition/scheduler/cli.py",
526
+ ];
527
+ for (const mod of schedulerModules) {
528
+ const src = join(arkaosRoot, mod);
529
+ const dest = join(installDir, mod);
530
+ if (existsSync(src)) {
531
+ ensureDir(dirname(dest));
532
+ copyFileSync(src, dest);
533
+ }
534
+ }
535
+ // Write minimal __init__.py files (don't copy full cognition init — it
536
+ // imports modules not deployed here like capture, insights, memory)
537
+ for (const init of ["core/__init__.py", "core/cognition/__init__.py"]) {
538
+ const dest = join(installDir, init);
539
+ ensureDir(dirname(dest));
540
+ writeFileSync(dest, '"""ArkaOS — deployed subset for scheduler."""\n');
541
+ }
542
+ console.log(" \u2713 Scheduler core modules updated");
543
+
544
+ // 4. Ensure log directories
545
+ ensureDir(join(installDir, "logs", "dreaming"));
546
+ ensureDir(join(installDir, "logs", "research"));
547
+
548
+ // 5. Restart platform service if installed
549
+ const daemonPath = join(binDir, "scheduler-daemon.py");
550
+ if (platform === "darwin") {
551
+ const plistPath = join(homedir(), "Library", "LaunchAgents", "com.arkaos.scheduler.plist");
552
+ if (existsSync(plistPath)) {
553
+ // Reload to pick up updated daemon
554
+ try {
555
+ execSync(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "pipe" });
556
+ execSync(`launchctl load "${plistPath}"`, { stdio: "pipe" });
557
+ console.log(" \u2713 Scheduler service restarted (launchd)");
558
+ } catch {
559
+ console.log(" \u26a0 Scheduler reload failed — restart manually");
560
+ }
561
+ } else {
562
+ // First time — install the service
563
+ const home = homedir();
564
+ let pythonPath;
565
+ try {
566
+ pythonPath = getArkaosPython();
567
+ } catch {
568
+ try {
569
+ pythonPath = execSync("which python3", { stdio: "pipe" }).toString().trim();
570
+ } catch {
571
+ pythonPath = "python3";
572
+ }
573
+ }
574
+ const logDir = join(installDir, "logs");
575
+ const pathValue = `${home}/.local/bin:${home}/.arkaos/bin:/usr/local/bin:/usr/bin:/bin`;
576
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
577
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
578
+ <plist version="1.0">
579
+ <dict>
580
+ \t<key>Label</key>
581
+ \t<string>com.arkaos.scheduler</string>
582
+ \t<key>ProgramArguments</key>
583
+ \t<array>
584
+ \t\t<string>${pythonPath}</string>
585
+ \t\t<string>${daemonPath}</string>
586
+ \t</array>
587
+ \t<key>EnvironmentVariables</key>
588
+ \t<dict>
589
+ \t\t<key>PATH</key>
590
+ \t\t<string>${pathValue}</string>
591
+ \t\t<key>HOME</key>
592
+ \t\t<string>${home}</string>
593
+ \t</dict>
594
+ \t<key>RunAtLoad</key>
595
+ \t<true/>
596
+ \t<key>KeepAlive</key>
597
+ \t<true/>
598
+ \t<key>StandardOutPath</key>
599
+ \t<string>${join(logDir, "scheduler-stdout.log")}</string>
600
+ \t<key>StandardErrorPath</key>
601
+ \t<string>${join(logDir, "scheduler-stderr.log")}</string>
602
+ </dict>
603
+ </plist>`;
604
+ ensureDir(join(homedir(), "Library", "LaunchAgents"));
605
+ writeFileSync(plistPath, plist);
606
+ try {
607
+ execSync(`launchctl load "${plistPath}"`, { stdio: "pipe" });
608
+ console.log(" \u2713 Scheduler service installed and started (launchd)");
609
+ } catch {
610
+ console.log(" \u26a0 Scheduler plist written but load failed");
611
+ }
612
+ }
613
+ } else if (platform === "linux") {
614
+ try {
615
+ execSync("systemctl --user restart arkaos-scheduler.service 2>/dev/null", { stdio: "pipe" });
616
+ console.log(" \u2713 Scheduler service restarted (systemd)");
617
+ } catch {
618
+ console.log(" \u26a0 Scheduler not running — install with: npx arkaos install");
619
+ }
620
+ }
621
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "2.10.0"
3
+ version = "2.10.1"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -30,7 +30,7 @@ dependencies = [
30
30
  [project.optional-dependencies]
31
31
  knowledge = [
32
32
  "fastembed>=0.8.0",
33
- "sqlite-vss>=0.1.2",
33
+ "sqlite-vec>=0.1.1",
34
34
  ]
35
35
  dashboard = [
36
36
  "fastapi>=0.115.0",