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.
- package/README.md +318 -107
- package/VERSION +1 -1
- package/config/hooks/cwd-changed.ps1 +144 -0
- package/config/hooks/post-tool-use.ps1 +347 -0
- package/config/hooks/post-tool-use.sh +6 -6
- package/config/hooks/pre-compact.ps1 +238 -0
- package/config/hooks/pre-compact.sh +10 -6
- package/config/hooks/session-start.ps1 +109 -0
- package/config/hooks/session-start.sh +1 -1
- package/config/hooks/user-prompt-submit.ps1 +287 -0
- package/config/hooks/user-prompt-submit.sh +5 -2
- package/config/statusline.ps1 +160 -0
- package/core/cognition/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/collector.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/store.cpython-313.pyc +0 -0
- package/core/cognition/insights/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/insights/__pycache__/store.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/obsidian.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/schemas.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/vector.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/writer.cpython-313.pyc +0 -0
- package/core/cognition/research/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/research/__pycache__/profiler.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/cli.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/platform.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/daemon.py +77 -21
- package/core/cognition/scheduler/platform.py +43 -12
- package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/core/knowledge/vector_store.py +50 -25
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/layers.py +2 -2
- package/installer/adapters/claude-code.js +72 -45
- package/installer/cli.js +19 -6
- package/installer/doctor.js +130 -18
- package/installer/index.js +592 -149
- package/installer/platform.js +20 -0
- package/installer/prompts.js +109 -5
- package/installer/python-resolver.js +251 -0
- package/installer/update.js +497 -62
- package/package.json +1 -1
- package/pyproject.toml +2 -2
- package/scripts/start-dashboard.ps1 +271 -0
package/installer/update.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
+
// ── 1. Update Python deps (using venv) ──
|
|
94
|
+
console.log(" [1/8] Updating Python dependencies...");
|
|
46
95
|
|
|
47
|
-
//
|
|
48
|
-
console.log(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
} catch {}
|
|
70
|
-
}
|
|
71
|
-
|
|
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/
|
|
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/
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"
|
|
90
|
-
"
|
|
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
|
|
97
|
-
const
|
|
98
|
-
const
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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(" ✓
|
|
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/
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
|
134
|
-
console.log(" [5/
|
|
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(
|
|
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
|
-
//
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
// ──
|
|
151
|
-
console.log(" [
|
|
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
package/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "arkaos-core"
|
|
3
|
-
version = "2.10.
|
|
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-
|
|
33
|
+
"sqlite-vec>=0.1.1",
|
|
34
34
|
]
|
|
35
35
|
dashboard = [
|
|
36
36
|
"fastapi>=0.115.0",
|