@hybridlabor-api/aos 4.13.1 → 4.14.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/.agents/AGENTS.md +8 -0
- package/.agents/nodes.json +5 -2
- package/.claude/hooks/conventional-commits.mjs +14 -15
- package/.claude/hooks/env-file-protection.mjs +14 -15
- package/.claude/hooks/go-gate.mjs +157 -13
- package/.claude/hooks/go-token.mjs +55 -0
- package/.claude/hooks/memb-inject.mjs +75 -62
- package/.claude/hooks/trail-autostart.mjs +27 -0
- package/.claude/settings.json +18 -0
- package/.claude/workflows/startcycle-dispatch.mjs +11 -4
- package/.opencode/plugins/bdb-aos.js +98 -121
- package/.opencode/plugins/lib/trail-autostart.js +38 -0
- package/CLAUDE.md +1 -1
- package/README.de.md +6 -10
- package/README.md +6 -10
- package/README.pt.md +6 -10
- package/THIRD_PARTY_NOTICES.md +19 -3
- package/assets/header-v5.png +0 -0
- package/bin/aos-acp.mjs +211 -0
- package/bin/aos-doctor.mjs +1 -1
- package/bin/aos-uninstall.mjs +2 -2
- package/docs/master-session-acp.md +51 -0
- package/installer.js +398 -65
- package/mcps/mcsc/packages/mcp/server.js +6 -7
- package/package.json +4 -3
- package/scripts/validate-skills.mjs +76 -0
- package/skills/basic/bdbmediastorm/SKILL.md +1 -1
- package/skills/basic/godmode-shipping/SKILL.md +3 -0
- package/skills/basic/master-session/SKILL.md +89 -0
- package/skills/basic/startcycle/SKILL.md +1 -1
- package/skills/basic/startcycle-graph/SKILL.md +2 -2
- package/skills/basic/startcycle-graph-user/SKILL.md +1 -1
- package/skills/basic/teamwork-preview/SKILL.md +1 -1
- package/skills/bdbrainstorm/SKILL.md +7 -1
- package/skills/global_config/agentic-harness-patterns/SKILL.md +257 -0
- package/skills/global_config/agentic-harness-patterns/metadata.json +10 -0
- package/skills/global_config/agentic-harness-patterns/references/agent-orchestration-pattern.md +97 -0
- package/skills/global_config/agentic-harness-patterns/references/bootstrap-sequence-pattern.md +106 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/compress-pattern.md +78 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/isolate-pattern.md +82 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/select-pattern.md +86 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering-pattern.md +29 -0
- package/skills/global_config/agentic-harness-patterns/references/hook-lifecycle-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/memory-persistence-pattern.md +109 -0
- package/skills/global_config/agentic-harness-patterns/references/permission-gate-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/skill-runtime-pattern.md +104 -0
- package/skills/global_config/agentic-harness-patterns/references/task-decomposition-pattern.md +92 -0
- package/skills/global_config/agentic-harness-patterns/references/tool-registry-pattern.md +101 -0
- package/skills/global_config/agenttrail/SKILL.md +8 -0
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +14 -0
- package/skills/global_config/agenttrail/bin/ensure.mjs +118 -0
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
- package/skills/global_config/bdb-memb-mcp/SKILL.md +4 -5
- package/skills/global_config/bdb-visual-edit/SKILL.md +51 -0
- package/skills/global_config/bdb-visual-edit/references/vite-react-source-attr.md +59 -0
- package/skills/global_config/bdb-visual-edit/scripts/pick-snippet.js +27 -0
- package/skills/global_config/bdb-visual-edit/scripts/sanitize-element.mjs +123 -0
- package/skills/global_config/factory-collect/SKILL.md +74 -0
- package/skills/global_config/factory-human-digest/SKILL.md +92 -0
- package/skills/global_config/factory-lookback/SKILL.md +95 -0
- package/skills/global_config/factory-review-prs/SKILL.md +63 -0
- package/skills/global_config/git-pr-review/SKILL.md +3 -0
- package/skills/global_config/grilling/SKILL.md +2 -0
- package/skills/global_config/mcsc/SKILL.md +1 -1
- package/skills/global_config/plan-arbiter/SKILL.md +125 -0
- package/skills/global_config/plan-canvas/SKILL.md +63 -9
- package/skills/global_config/plan-canvas/scripts/lib/loopback-guard.js +19 -3
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/README.md +285 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/agent-trail.js +129 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/board-client.js +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/canvas.mdx +19 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/plan.mdx +18 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/recap-demo/plan.mdx +72 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/README.md +29 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/architecture.json +30 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/00_architecture.html +14950 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/canvas.mdx +511 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/plan.mdx +208 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/recap/plan.mdx +102 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/standard/plan.md +136 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/canvas.mdx +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/plan.mdx +37 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/index.js +188 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/kit.js +123 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/mdx.js +411 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/render.js +1291 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/plan.mdx +195 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/standard.md +95 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/plan.mdx +105 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/canvas.mdx +81 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/plan.mdx +145 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/plan.mdx +172 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/standard.md +100 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/plan.mdx +67 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/standard.md +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/canvas.mdx +63 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/plan.mdx +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/standard.md +39 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/plan.mdx +118 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/standard.md +57 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/plan.mdx +173 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/standard.md +96 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/plan.mdx +91 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/standard.md +54 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/canvas.mdx +53 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/plan.mdx +225 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/standard.md +111 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/theme.css +472 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/trail.js +216 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/markdown.js +1 -1
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +37 -4
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +125 -29
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +196 -8
- package/skills/global_config/pr-recap/SKILL.md +47 -0
- package/skills/global_config/pr-recap/scripts/pr-recap.mjs +200 -0
- package/skills/global_config/quick-recap/SKILL.md +55 -0
- package/skills/global_config/stay-within-limits/SKILL.md +85 -0
- package/skills/global_config/triage/SKILL.md +3 -0
- package/skills/global_config/visual-edit/README.md +96 -0
- package/skills/global_config/visual-edit/SKILL.md +615 -0
- package/skills/global_config/visual-plan/README.md +93 -0
- package/skills/global_config/visual-plan/SKILL.md +544 -0
- package/skills/global_config/visual-plan/references/canvas.md +139 -0
- package/skills/global_config/visual-plan/references/connection.md +51 -0
- package/skills/global_config/visual-plan/references/document-quality.md +186 -0
- package/skills/global_config/visual-plan/references/exemplar.md +62 -0
- package/skills/global_config/visual-plan/references/local-files.md +99 -0
- package/skills/global_config/visual-plan/references/wireframe.md +319 -0
- package/skills/global_config/visual-recap/README.md +103 -0
- package/skills/global_config/visual-recap/SKILL.md +560 -0
- package/skills/global_config/visual-recap/references/connection.md +51 -0
- package/skills/global_config/visual-recap/references/local-files.md +99 -0
- package/skills/global_config/visual-recap/references/wireframe.md +319 -0
- package/skills/playbooks/pb-ci-fix/SKILL.md +49 -0
- package/skills/playbooks/pb-event-tracker/SKILL.md +45 -0
- package/skills/playbooks/pb-meeting-actions/SKILL.md +42 -0
- package/skills/playbooks/pb-project-new/SKILL.md +48 -0
- package/skills/playbooks/pb-week-plan/SKILL.md +45 -0
- package/assets/header-v4.jpg +0 -0
package/installer.js
CHANGED
|
@@ -526,12 +526,24 @@ function loadInstallManifest(customPath = null) {
|
|
|
526
526
|
return {};
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
+
// Temp file in the same directory + rename: a crash mid-write cannot leave a truncated manifest.
|
|
530
|
+
function writeFileAtomic(filePath, data) {
|
|
531
|
+
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
532
|
+
try {
|
|
533
|
+
fs.writeFileSync(tmp, data);
|
|
534
|
+
fs.renameSync(tmp, filePath);
|
|
535
|
+
} catch (e) {
|
|
536
|
+
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
537
|
+
throw e;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
529
541
|
function saveInstallManifest(manifest, customPath = null) {
|
|
530
542
|
if (DRY_RUN) return;
|
|
531
543
|
const manifestPath = customPath || getInstallManifestPath();
|
|
532
544
|
try {
|
|
533
545
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
534
|
-
|
|
546
|
+
writeFileAtomic(manifestPath, JSON.stringify(manifest, null, 2));
|
|
535
547
|
} catch (e) {
|
|
536
548
|
log.warn(`Could not write install manifest: ${e.message}`);
|
|
537
549
|
}
|
|
@@ -978,7 +990,11 @@ function detectInstallState() {
|
|
|
978
990
|
} catch (e) { logDebug(e, 'operation'); }
|
|
979
991
|
}
|
|
980
992
|
}
|
|
981
|
-
if (!localVersion)
|
|
993
|
+
if (!localVersion) {
|
|
994
|
+
// ponytail: placeholder only feeds the version display/compare; pruning is driven by the file manifest, not this value.
|
|
995
|
+
log.step('Previous AOS version is unknown (no manifest); assuming 3.8.0 for the update comparison only.');
|
|
996
|
+
localVersion = '3.8.0';
|
|
997
|
+
}
|
|
982
998
|
}
|
|
983
999
|
|
|
984
1000
|
const basePath = scriptDir.includes('_npx') ? path.join(homeDir, '.agents') : path.dirname(srcDir);
|
|
@@ -1039,7 +1055,7 @@ function saveManifest(data = {}) {
|
|
|
1039
1055
|
platform: process.platform,
|
|
1040
1056
|
...data
|
|
1041
1057
|
});
|
|
1042
|
-
|
|
1058
|
+
writeFileAtomic(manifestPath, JSON.stringify(updated, null, 2));
|
|
1043
1059
|
} catch (e) { logDebug(e, 'manifest read/parse'); }
|
|
1044
1060
|
}
|
|
1045
1061
|
|
|
@@ -1218,6 +1234,20 @@ function syncSkillEntry(fullPath, dirName, targetSkillDir, excludeSkills) {
|
|
|
1218
1234
|
}
|
|
1219
1235
|
}
|
|
1220
1236
|
|
|
1237
|
+
function redactSecrets(text) {
|
|
1238
|
+
return String(text)
|
|
1239
|
+
.replace(/([\w.-]*(?:key|token|secret|passw(?:or)?d|auth|credential)[\w.-]*\s*[=:]\s*)("[^"]*"|'[^']*'|\S+)/gi, '$1[redacted]')
|
|
1240
|
+
.replace(/\bBearer\s+\S+/gi, 'Bearer [redacted]')
|
|
1241
|
+
.replace(/\b(?:sk|ghp|gho|ghs|xox[a-z])[-_][A-Za-z0-9_-]{16,}/g, '[redacted]');
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// execFileSync error -> one message that keeps the child's own stderr/stdout (last 2 KB, redacted).
|
|
1245
|
+
function describeExecError(e) {
|
|
1246
|
+
const out = [e && e.stderr, e && e.stdout].map((b) => (b ? b.toString() : '').trim()).filter(Boolean).join('\n');
|
|
1247
|
+
const head = redactSecrets(String((e && e.message) || e).split('\n')[0]).slice(0, 300);
|
|
1248
|
+
return out ? `${head}\n${redactSecrets(out.slice(-2048))}` : head;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1221
1251
|
function installStep(what, fn, hint) {
|
|
1222
1252
|
try {
|
|
1223
1253
|
return { ok: true, value: fn() };
|
|
@@ -1937,7 +1967,7 @@ async function installMemB(interactive) {
|
|
|
1937
1967
|
// banner. Stop at the module that could not be fetched.
|
|
1938
1968
|
if (!downloadOrUpdateModule('@hybridlabor-api/memb', membDir, 'memB Vector Engine')) {
|
|
1939
1969
|
log.warn('Skipping memB setup: the module could not be downloaded.');
|
|
1940
|
-
return;
|
|
1970
|
+
return false;
|
|
1941
1971
|
}
|
|
1942
1972
|
await installDeja();
|
|
1943
1973
|
if (DRY_RUN) {
|
|
@@ -1945,6 +1975,7 @@ async function installMemB(interactive) {
|
|
|
1945
1975
|
return;
|
|
1946
1976
|
}
|
|
1947
1977
|
|
|
1978
|
+
let membOk = true;
|
|
1948
1979
|
const reqFile = path.join(membDir, 'requirements.txt');
|
|
1949
1980
|
const serverPy = path.join(membDir, 'src', 'backend', 'server.py');
|
|
1950
1981
|
if (fs.existsSync(reqFile)) {
|
|
@@ -2017,10 +2048,12 @@ async function installMemB(interactive) {
|
|
|
2017
2048
|
);
|
|
2018
2049
|
if (repaired && !verifyPythonImports(venvPython, membDir, importCheck)) {
|
|
2019
2050
|
log.warn('memB dependency repair did not pass the import check; inspect the venv before starting the daemon.');
|
|
2051
|
+
membOk = false;
|
|
2020
2052
|
}
|
|
2021
2053
|
}
|
|
2022
2054
|
} catch (e) {
|
|
2023
2055
|
log.warn(`Failed memB standalone venv setup: ${e.message}`);
|
|
2056
|
+
membOk = false;
|
|
2024
2057
|
}
|
|
2025
2058
|
}
|
|
2026
2059
|
|
|
@@ -2082,13 +2115,14 @@ async function installMemB(interactive) {
|
|
|
2082
2115
|
} catch (e) { logDebug(e, 'windows memb daemon setup'); }
|
|
2083
2116
|
}
|
|
2084
2117
|
}
|
|
2118
|
+
return membOk;
|
|
2085
2119
|
}
|
|
2086
2120
|
|
|
2087
2121
|
async function installSynapse() {
|
|
2088
2122
|
const synapseDir = path.join(moduleBasePath(), 'bdb-synapse');
|
|
2089
2123
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-synapse', synapseDir, 'BDB Synapse')) {
|
|
2090
2124
|
log.warn('Skipping Synapse setup: the module could not be downloaded.');
|
|
2091
|
-
return;
|
|
2125
|
+
return false;
|
|
2092
2126
|
}
|
|
2093
2127
|
if (DRY_RUN) {
|
|
2094
2128
|
log.step('[dry-run] would link synapse binary into ~/.local/bin/synapse + setup background daemon');
|
|
@@ -2217,6 +2251,7 @@ async function installSynapse() {
|
|
|
2217
2251
|
}
|
|
2218
2252
|
} else {
|
|
2219
2253
|
log.warn(`No pre-built binary for this platform. Compile with: cd "${synapseDir}" && go build -o ${binaryName} ./cmd/synapse/`);
|
|
2254
|
+
return false;
|
|
2220
2255
|
}
|
|
2221
2256
|
}
|
|
2222
2257
|
|
|
@@ -2410,7 +2445,7 @@ async function installCreatorExtension() {
|
|
|
2410
2445
|
const creatorDir = path.join(moduleBasePath(), 'bdb-dev-creator-extension');
|
|
2411
2446
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-creator-extension', creatorDir, 'BDB Creator Extension')) {
|
|
2412
2447
|
log.warn('Skipping Creator Extension setup: the module could not be downloaded.');
|
|
2413
|
-
return;
|
|
2448
|
+
return false;
|
|
2414
2449
|
}
|
|
2415
2450
|
if (DRY_RUN) {
|
|
2416
2451
|
log.step('[dry-run] would run BDB Creator Extension setup');
|
|
@@ -2423,6 +2458,7 @@ async function installCreatorExtension() {
|
|
|
2423
2458
|
: spawnSync('node', [installerScript, '--auto'], { stdio: 'inherit', cwd: creatorDir });
|
|
2424
2459
|
if (setupResult.status !== 0) {
|
|
2425
2460
|
log.warn(`Creator Extension setup note: exit code ${setupResult.status}`);
|
|
2461
|
+
return false;
|
|
2426
2462
|
}
|
|
2427
2463
|
}
|
|
2428
2464
|
}
|
|
@@ -2431,7 +2467,7 @@ async function installHardwarePcb() {
|
|
|
2431
2467
|
const hwDir = path.join(moduleBasePath(), 'bdb-hardware-pcb');
|
|
2432
2468
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-hardware-pcb', hwDir, 'BDB Hardware & PCB (KiCad + OpenSCAD)')) {
|
|
2433
2469
|
log.warn('Skipping Hardware & PCB setup: the module could not be downloaded.');
|
|
2434
|
-
return;
|
|
2470
|
+
return false;
|
|
2435
2471
|
}
|
|
2436
2472
|
if (DRY_RUN) {
|
|
2437
2473
|
log.step('[dry-run] would run BDB Hardware & PCB setup');
|
|
@@ -2442,6 +2478,7 @@ async function installHardwarePcb() {
|
|
|
2442
2478
|
const setupResult = spawnSync('node', [installerScript, '--auto'], { stdio: 'inherit', cwd: hwDir });
|
|
2443
2479
|
if (setupResult.status !== 0) {
|
|
2444
2480
|
log.warn(`Hardware & PCB setup note: exit code ${setupResult.status}`);
|
|
2481
|
+
return false;
|
|
2445
2482
|
}
|
|
2446
2483
|
}
|
|
2447
2484
|
}
|
|
@@ -2450,7 +2487,7 @@ async function installOSRemoteGateway() {
|
|
|
2450
2487
|
const remoteDir = path.join(moduleBasePath(), 'bdb-os-remote');
|
|
2451
2488
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-os-remote', remoteDir, 'BDB OS Remote Gateway')) {
|
|
2452
2489
|
log.warn('Skipping OS Remote Gateway setup: the module could not be downloaded.');
|
|
2453
|
-
return;
|
|
2490
|
+
return false;
|
|
2454
2491
|
}
|
|
2455
2492
|
}
|
|
2456
2493
|
|
|
@@ -2458,7 +2495,7 @@ async function installDevToolInstaller() {
|
|
|
2458
2495
|
const toolInstallerDir = path.join(moduleBasePath(), 'bdb-dev-tool-installer');
|
|
2459
2496
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-dev-tool-installer', toolInstallerDir, 'BDB Dev Tool Installer')) {
|
|
2460
2497
|
log.warn('Skipping Dev Tool Installer setup: the module could not be downloaded.');
|
|
2461
|
-
return;
|
|
2498
|
+
return false;
|
|
2462
2499
|
}
|
|
2463
2500
|
}
|
|
2464
2501
|
|
|
@@ -2470,7 +2507,7 @@ function aoSupportedHere() {
|
|
|
2470
2507
|
async function installOSAgentWorkspace() {
|
|
2471
2508
|
if (!aoSupportedHere()) {
|
|
2472
2509
|
log.warn(`AO is not supported on ${process.platform}/${process.arch}.`);
|
|
2473
|
-
return;
|
|
2510
|
+
return false;
|
|
2474
2511
|
}
|
|
2475
2512
|
|
|
2476
2513
|
const isWin = process.platform === 'win32';
|
|
@@ -2491,7 +2528,7 @@ async function installOSAgentWorkspace() {
|
|
|
2491
2528
|
} else {
|
|
2492
2529
|
if (!downloadOrUpdateModule('@hybridlabor-api/bdb-agent-orchestrator', osAgentDir, 'BDB Agent Orchestrator')) {
|
|
2493
2530
|
log.warn('Skipping AO setup: the module could not be downloaded.');
|
|
2494
|
-
return;
|
|
2531
|
+
return false;
|
|
2495
2532
|
}
|
|
2496
2533
|
}
|
|
2497
2534
|
if (DRY_RUN) {
|
|
@@ -2566,16 +2603,13 @@ async function installOSAgentWorkspace() {
|
|
|
2566
2603
|
|
|
2567
2604
|
if (!daemonBin) {
|
|
2568
2605
|
log.warn(`AO binary missing in ${osAgentDir} — package layout changed or Go build needed.`);
|
|
2569
|
-
return;
|
|
2606
|
+
return false;
|
|
2570
2607
|
}
|
|
2571
2608
|
|
|
2572
2609
|
if (daemonBin !== binTarget) {
|
|
2573
2610
|
installStep('place the ao binary', () => {
|
|
2574
2611
|
fs.mkdirSync(localBinDir, { recursive: true });
|
|
2575
|
-
|
|
2576
|
-
if (!isWin) {
|
|
2577
|
-
fs.chmodSync(binTarget, 0o755);
|
|
2578
|
-
}
|
|
2612
|
+
installBinaryAtomically(daemonBin, binTarget);
|
|
2579
2613
|
log.step(`Installed ao to ${binTarget}`);
|
|
2580
2614
|
}, 'AO cannot be started without its binary.');
|
|
2581
2615
|
}
|
|
@@ -2602,9 +2636,14 @@ async function installOSAgentWorkspace() {
|
|
|
2602
2636
|
}
|
|
2603
2637
|
}
|
|
2604
2638
|
|
|
2605
|
-
installStep('register the AO service', () => {
|
|
2606
|
-
|
|
2639
|
+
const serviceStep = installStep('register the AO service', () => {
|
|
2640
|
+
try {
|
|
2641
|
+
execFileSync(binTarget, ['service', 'install'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
2642
|
+
} catch (e) {
|
|
2643
|
+
throw new Error(describeExecError(e));
|
|
2644
|
+
}
|
|
2607
2645
|
}, 'Start it by hand with: ao service install');
|
|
2646
|
+
if (!serviceStep.ok) return false;
|
|
2608
2647
|
|
|
2609
2648
|
// Windows startup entries fire asynchronously — give the service more time
|
|
2610
2649
|
const aoTimeout = process.platform === 'win32' ? 20000 : 8000;
|
|
@@ -2660,6 +2699,9 @@ async function promptMemBIngestion(mcpCodeTarget) {
|
|
|
2660
2699
|
}
|
|
2661
2700
|
|
|
2662
2701
|
function verifyEcosystemInstallation() {
|
|
2702
|
+
for (const f of moduleFailures) {
|
|
2703
|
+
console.log(` • ${colors.bold}${f.name}${colors.reset} ➔ ${colors.yellow}❌ NOT installed: ${f.reason.split('\n')[0]}${colors.reset}`);
|
|
2704
|
+
}
|
|
2663
2705
|
const modules = [
|
|
2664
2706
|
{ name: '1. bdb-synapse', pkg: '@hybridlabor-api/bdb-synapse', paths: [path.join(moduleBasePath(), 'bdb-synapse')] },
|
|
2665
2707
|
{ name: '2. memB', pkg: '@hybridlabor-api/memb', paths: [path.join(moduleBasePath(), 'memB'), path.join(geminiDir, 'config', 'mcps', 'memb-mcp')] },
|
|
@@ -2839,6 +2881,18 @@ function getTierExcludeSkills(tier) {
|
|
|
2839
2881
|
// primary write above owns its file wholesale, but a secondary store belongs
|
|
2840
2882
|
// to another product and is very likely to already hold the user's own
|
|
2841
2883
|
// servers -- so this merges per server key and never replaces the file.
|
|
2884
|
+
// An unattended run has no credentials of its own (empty strings); those must never
|
|
2885
|
+
// replace a value the user already has in a server's env block.
|
|
2886
|
+
function keepExistingEnvValues(incomingServers, previousServers) {
|
|
2887
|
+
for (const [key, incoming] of Object.entries(incomingServers || {})) {
|
|
2888
|
+
const prevEnv = previousServers && previousServers[key] && previousServers[key].env;
|
|
2889
|
+
if (!incoming || typeof incoming !== 'object' || !incoming.env || !prevEnv || typeof prevEnv !== 'object') continue;
|
|
2890
|
+
for (const [k, v] of Object.entries(incoming.env)) {
|
|
2891
|
+
if (v === '' && typeof prevEnv[k] === 'string' && prevEnv[k] !== '') incoming.env[k] = prevEnv[k];
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2842
2896
|
function mirrorMcpServersTo(extraPaths, mcpConfigStr) {
|
|
2843
2897
|
if (!Array.isArray(extraPaths) || extraPaths.length === 0) return;
|
|
2844
2898
|
|
|
@@ -2862,7 +2916,9 @@ function mirrorMcpServersTo(extraPaths, mcpConfigStr) {
|
|
|
2862
2916
|
}
|
|
2863
2917
|
const data = existing || {};
|
|
2864
2918
|
data.mcpServers = data.mcpServers && typeof data.mcpServers === 'object' ? data.mcpServers : {};
|
|
2865
|
-
|
|
2919
|
+
const mirrored = JSON.parse(JSON.stringify(servers));
|
|
2920
|
+
keepExistingEnvValues(mirrored, data.mcpServers);
|
|
2921
|
+
for (const [key, val] of Object.entries(mirrored)) data.mcpServers[key] = val;
|
|
2866
2922
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
2867
2923
|
// Carries injected API keys, same as the primary config. The `mode`
|
|
2868
2924
|
// option only applies when writeFileSync CREATES the file -- an
|
|
@@ -3182,6 +3238,7 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
3182
3238
|
if (!incoming || typeof incoming !== 'object') return;
|
|
3183
3239
|
if (!incoming.env && previous.env) incoming.env = previous.env;
|
|
3184
3240
|
});
|
|
3241
|
+
keepExistingEnvValues(newServers, oldServers);
|
|
3185
3242
|
oldConfig.mcpServers = Object.assign({}, oldServers, newServers);
|
|
3186
3243
|
fs.writeFileSync(paths.mcpConfigPath, JSON.stringify(oldConfig, null, 2), { mode: 0o600 });
|
|
3187
3244
|
try { fs.chmodSync(paths.mcpConfigPath, 0o600); } catch (e) { logDebug(e, 'chmod mcpConfigPath'); }
|
|
@@ -3260,7 +3317,16 @@ async function installMcpsForTarget(paths, ctx) {
|
|
|
3260
3317
|
// spaces) rather than matching the first alphanumeric run anywhere in the
|
|
3261
3318
|
// block, so it can't accidentally pick up a word from prose if the heading
|
|
3262
3319
|
// format changes.
|
|
3263
|
-
function
|
|
3320
|
+
function loadCommonSkills(dir = srcDir) {
|
|
3321
|
+
try {
|
|
3322
|
+
const reg = JSON.parse(fs.readFileSync(path.join(dir, '.agents', 'nodes.json'), 'utf8'));
|
|
3323
|
+
return Array.isArray(reg.common) ? reg.common.filter((s) => typeof s === 'string') : [];
|
|
3324
|
+
} catch {
|
|
3325
|
+
return [];
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
function parseAgentsMd(content, commonSkills = loadCommonSkills()) {
|
|
3264
3330
|
const blocks = content.split(/\n## /).slice(1);
|
|
3265
3331
|
const agents = [];
|
|
3266
3332
|
for (const raw of blocks) {
|
|
@@ -3290,7 +3356,7 @@ function parseAgentsMd(content) {
|
|
|
3290
3356
|
name,
|
|
3291
3357
|
role: roleMatch[1].trim(),
|
|
3292
3358
|
model: modelMatch ? modelMatch[1].trim() : null,
|
|
3293
|
-
skills: extractListAfter('Primary Skills'),
|
|
3359
|
+
skills: [...new Set([...extractListAfter('Primary Skills'), ...commonSkills])],
|
|
3294
3360
|
mcpServers: extractListAfter('MCP Servers'),
|
|
3295
3361
|
output: outputMatch ? outputMatch[1].trim() : null,
|
|
3296
3362
|
systemPrompt: raw.trim(),
|
|
@@ -3708,6 +3774,21 @@ function installOpencodePlugin({ targetHome = homeDir, configPath = null, data =
|
|
|
3708
3774
|
}
|
|
3709
3775
|
}
|
|
3710
3776
|
|
|
3777
|
+
// The plugin imports its decision logic from ./aos-hooks/ next to itself.
|
|
3778
|
+
const hooksSrc = path.join(srcDir, '.claude', 'hooks');
|
|
3779
|
+
if (fs.existsSync(hooksSrc)) {
|
|
3780
|
+
try {
|
|
3781
|
+
copyDirRecursiveSync(hooksSrc, path.join(opencodeDir, 'plugins', 'aos-hooks'));
|
|
3782
|
+
} catch (e) {
|
|
3783
|
+
log.error(`Could not install OpenCode plugin hooks (OpenCode gates inactive): ${e.message}`);
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
const trailLibSrc = path.join(srcDir, '.opencode', 'plugins', 'lib');
|
|
3788
|
+
if (fs.existsSync(trailLibSrc)) {
|
|
3789
|
+
try { copyDirRecursiveSync(trailLibSrc, path.join(opencodeDir, 'plugins', 'lib')); } catch (e) { log.warn(`Could not install OpenCode plugin lib: ${e.message}`); }
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3711
3792
|
// Slash-command payloads. Without these the /startcycle-graph command has no
|
|
3712
3793
|
// native resolution and only survives as a raw-text match in the plugin.
|
|
3713
3794
|
const commandsSrc = path.join(srcDir, '.opencode', 'commands');
|
|
@@ -3906,23 +3987,25 @@ function installGlobalBinaries() {
|
|
|
3906
3987
|
// merged result goes to a .bdb-new.json sidecar -- the same recovery pattern
|
|
3907
3988
|
// the MCP config merge in installMcpsForTarget uses.
|
|
3908
3989
|
function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
|
|
3909
|
-
const bdbHookScripts = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
3990
|
+
const bdbHookScripts = ['go-gate.mjs', 'go-token.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'trail-autostart.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
3910
3991
|
// memb-inject reads the machine-global memB store under $HOME and is
|
|
3911
3992
|
// installed once per machine, so it stays $HOME-anchored even inside a
|
|
3912
3993
|
// project harness -- unlike the two gate hooks, which are per-checkout by
|
|
3913
3994
|
// design. Pointing it at $CLAUDE_PROJECT_DIR would make it fail on every
|
|
3914
3995
|
// prompt in any project the harness was never installed into. trail-relay
|
|
3915
3996
|
// talks to machine-global live-map daemons, so it is anchored the same way.
|
|
3916
|
-
const machineGlobalHooks = ['memb-inject.mjs', 'trail-relay.mjs'];
|
|
3997
|
+
const machineGlobalHooks = ['memb-inject.mjs', 'trail-relay.mjs', 'trail-autostart.mjs'];
|
|
3917
3998
|
const isBdbEntry = (entry) => {
|
|
3918
3999
|
const cmds = (entry && Array.isArray(entry.hooks) ? entry.hooks : [])
|
|
3919
4000
|
.map((h) => (h && typeof h.command === 'string' ? h.command : ''))
|
|
3920
4001
|
.join(' ');
|
|
3921
4002
|
return bdbHookScripts.some((name) => cmds.includes(name));
|
|
3922
4003
|
};
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
4004
|
+
// Global installs must point at the copies installGlobalHooks put under
|
|
4005
|
+
// $HOME/.claude/hooks; $CLAUDE_PROJECT_DIR would not exist in other projects.
|
|
4006
|
+
const localize = (cmd) => (projectLocal
|
|
4007
|
+
? (machineGlobalHooks.some((n) => cmd.includes(n)) ? cmd : cmd.split('${HOME}').join('$CLAUDE_PROJECT_DIR'))
|
|
4008
|
+
: cmd.split('$CLAUDE_PROJECT_DIR/.claude/hooks/').join('$HOME/.claude/hooks/'));
|
|
3926
4009
|
const cloneBdbEntries = (entries) =>
|
|
3927
4010
|
JSON.parse(JSON.stringify(entries)).map((e) => ({
|
|
3928
4011
|
...e,
|
|
@@ -3969,7 +4052,16 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
|
|
|
3969
4052
|
|
|
3970
4053
|
try {
|
|
3971
4054
|
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
3972
|
-
|
|
4055
|
+
const tmpPath = `${settingsPath}.${process.pid}.tmp`;
|
|
4056
|
+
if (existing) {
|
|
4057
|
+
fs.copyFileSync(settingsPath, `${settingsPath}.${process.hrtime.bigint()}.bak`);
|
|
4058
|
+
const dir = path.dirname(settingsPath);
|
|
4059
|
+
const base = path.basename(settingsPath);
|
|
4060
|
+
const baks = fs.readdirSync(dir).filter((f) => f.startsWith(`${base}.`) && /\.\d{13,}\.bak$/.test(f)).sort();
|
|
4061
|
+
for (const old of baks.slice(0, -3)) fs.rmSync(path.join(dir, old), { force: true });
|
|
4062
|
+
}
|
|
4063
|
+
fs.writeFileSync(tmpPath, JSON.stringify(buildMerged(existing), null, 2) + '\n', { mode: existing ? fs.statSync(settingsPath).mode & 0o777 : 0o644 });
|
|
4064
|
+
fs.renameSync(tmpPath, settingsPath);
|
|
3973
4065
|
} catch (e) {
|
|
3974
4066
|
log.warn(`Could not write ${settingsPath}: ${e.message}`);
|
|
3975
4067
|
}
|
|
@@ -3979,60 +4071,64 @@ function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
|
|
|
3979
4071
|
// ~/.gemini/config/hooks.json), preserving user-defined foreign hooks.
|
|
3980
4072
|
function mergeAntigravityHooks(hooksPath, { projectLocal = false } = {}) {
|
|
3981
4073
|
const bdbHookScripts = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'startcycle-dispatch.mjs', 'trail-relay.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
3982
|
-
const
|
|
3983
|
-
const cmds = (entry && Array.isArray(entry.hooks) ? entry.hooks : [entry])
|
|
3984
|
-
.map((h) => (h && typeof h.command === 'string' ? h.command : (typeof h === 'string' ? h : '')))
|
|
3985
|
-
.join(' ');
|
|
3986
|
-
return bdbHookScripts.some((name) => cmds.includes(name));
|
|
3987
|
-
};
|
|
4074
|
+
const isBdbHandler = (h) => typeof h?.command === 'string' && bdbHookScripts.some((name) => h.command.includes(name));
|
|
3988
4075
|
|
|
3989
4076
|
const baseDir = path.dirname(hooksPath);
|
|
3990
4077
|
const hooksDir = projectLocal ? path.join(currentDir, '.agents', 'hooks') : path.join(baseDir, 'hooks');
|
|
3991
4078
|
const workflowsDir = projectLocal ? path.join(currentDir, '.agents', 'workflows') : path.join(baseDir, 'workflows');
|
|
3992
4079
|
const globalHooksDir = projectLocal ? path.join(homeDir, '.gemini', 'config', 'hooks') : hooksDir;
|
|
3993
4080
|
|
|
4081
|
+
const cmd = (dir, script, args = '') => `node "${path.join(dir, script)}"${args}`;
|
|
3994
4082
|
const bdbHooks = {
|
|
3995
4083
|
PreToolUse: [
|
|
3996
4084
|
{
|
|
3997
4085
|
matcher: "run_command|Bash",
|
|
3998
4086
|
hooks: [
|
|
3999
|
-
{ type: "command", command:
|
|
4000
|
-
{ type: "command", command:
|
|
4087
|
+
{ type: "command", command: cmd(hooksDir, 'go-gate.mjs'), timeout: 10 },
|
|
4088
|
+
{ type: "command", command: cmd(hooksDir, 'conventional-commits.mjs'), timeout: 10 }
|
|
4001
4089
|
]
|
|
4002
4090
|
},
|
|
4003
4091
|
{
|
|
4004
4092
|
matcher: "write_file|edit_file|replace|Write|Edit|MultiEdit",
|
|
4005
|
-
hooks: [{ type: "command", command:
|
|
4093
|
+
hooks: [{ type: "command", command: cmd(hooksDir, 'env-file-protection.mjs'), timeout: 10 }]
|
|
4006
4094
|
},
|
|
4007
4095
|
{
|
|
4008
|
-
|
|
4096
|
+
matcher: "*",
|
|
4097
|
+
hooks: [{ type: "command", command: cmd(globalHooksDir, 'trail-relay.mjs', ' --agent agy --event PreToolUse'), timeout: 2 }]
|
|
4009
4098
|
}
|
|
4010
4099
|
],
|
|
4011
4100
|
Stop: [
|
|
4012
|
-
{
|
|
4013
|
-
|
|
4014
|
-
},
|
|
4015
|
-
{
|
|
4016
|
-
hooks: [{ type: "command", command: `node "${path.join(globalHooksDir, 'trail-relay.mjs')}" --agent agy --event Stop`, timeout: 2000 }]
|
|
4017
|
-
}
|
|
4101
|
+
{ type: "command", command: cmd(hooksDir, 'graph-gate.mjs'), timeout: 10 },
|
|
4102
|
+
{ type: "command", command: cmd(globalHooksDir, 'trail-relay.mjs', ' --agent agy --event Stop'), timeout: 2 }
|
|
4018
4103
|
],
|
|
4019
4104
|
PreInvocation: [
|
|
4020
|
-
{
|
|
4021
|
-
|
|
4022
|
-
{ type: "command", command: `node "${path.join(globalHooksDir, 'memb-inject.mjs')}"`, timeout: 8000 },
|
|
4023
|
-
{ type: "command", command: `node "${path.join(workflowsDir, 'startcycle-dispatch.mjs')}"`, timeout: 30000 }
|
|
4024
|
-
]
|
|
4025
|
-
}
|
|
4105
|
+
{ type: "command", command: cmd(globalHooksDir, 'memb-inject.mjs'), timeout: 8 },
|
|
4106
|
+
{ type: "command", command: cmd(workflowsDir, 'startcycle-dispatch.mjs'), timeout: 30 }
|
|
4026
4107
|
]
|
|
4027
4108
|
};
|
|
4028
4109
|
|
|
4110
|
+
// Drops BDB handlers (old wrapped or current shape), keeps foreign ones, and
|
|
4111
|
+
// rewraps flat events so previously written `{ hooks: [...] }` entries migrate.
|
|
4112
|
+
const keepForeign = (event, entries) => {
|
|
4113
|
+
const out = [];
|
|
4114
|
+
for (const e of Array.isArray(entries) ? entries : []) {
|
|
4115
|
+
const nested = e && Array.isArray(e.hooks);
|
|
4116
|
+
if (bdbHooks[event][0].hooks) {
|
|
4117
|
+
if (!nested) { if (!isBdbHandler(e)) out.push(e); continue; }
|
|
4118
|
+
const kept = e.hooks.filter((h) => !isBdbHandler(h));
|
|
4119
|
+
if (kept.length) out.push({ ...e, hooks: kept });
|
|
4120
|
+
} else {
|
|
4121
|
+
out.push(...(nested ? e.hooks : [e]).filter((h) => !isBdbHandler(h)));
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
return out;
|
|
4125
|
+
};
|
|
4126
|
+
|
|
4029
4127
|
const buildMerged = (existing) => {
|
|
4030
4128
|
const merged = existing && typeof existing === 'object' ? existing : {};
|
|
4031
4129
|
merged.hooks = merged.hooks && typeof merged.hooks === 'object' ? merged.hooks : {};
|
|
4032
4130
|
for (const [event, entries] of Object.entries(bdbHooks)) {
|
|
4033
|
-
|
|
4034
|
-
.filter((e) => !isBdbEntry(e));
|
|
4035
|
-
merged.hooks[event] = [...foreignEntries, ...entries];
|
|
4131
|
+
merged.hooks[event] = [...keepForeign(event, merged.hooks[event]), ...entries];
|
|
4036
4132
|
}
|
|
4037
4133
|
return merged;
|
|
4038
4134
|
};
|
|
@@ -4064,7 +4160,9 @@ function mergeAntigravityHooks(hooksPath, { projectLocal = false } = {}) {
|
|
|
4064
4160
|
|
|
4065
4161
|
try {
|
|
4066
4162
|
fs.mkdirSync(path.dirname(hooksPath), { recursive: true });
|
|
4067
|
-
|
|
4163
|
+
const tmpPath = `${hooksPath}.${process.pid}.tmp`;
|
|
4164
|
+
fs.writeFileSync(tmpPath, JSON.stringify(buildMerged(existing), null, 2) + '\n');
|
|
4165
|
+
fs.renameSync(tmpPath, hooksPath);
|
|
4068
4166
|
} catch (e) {
|
|
4069
4167
|
log.warn(`Could not write ${hooksPath}: ${e.message}`);
|
|
4070
4168
|
}
|
|
@@ -4178,13 +4276,35 @@ function mergeCodexTomlHooks(configTomlPath, { projectLocal = false } = {}) {
|
|
|
4178
4276
|
}
|
|
4179
4277
|
}
|
|
4180
4278
|
|
|
4181
|
-
//
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4279
|
+
// Drop every AOS hook entry (marker block or not, duplicates included), then append one fresh block.
|
|
4280
|
+
const bdbCodexScripts = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'startcycle-dispatch.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
4281
|
+
const isAosMarker = (l) => /^\s*# AOS:HOOKS:(START|END)\s*$/.test(l);
|
|
4282
|
+
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
4283
|
+
const lines = content.split(/\r?\n/).filter((l) => !isAosMarker(l));
|
|
4284
|
+
const sections = [];
|
|
4285
|
+
for (const l of lines) {
|
|
4286
|
+
if (/^\s*\[/.test(l) || !sections.length) sections.push([l]);
|
|
4287
|
+
else sections[sections.length - 1].push(l);
|
|
4288
|
+
}
|
|
4289
|
+
const isHookHeader = (sec) => /^\s*\[\[hooks\.\w+\]\]\s*$/.test(sec[0]);
|
|
4290
|
+
const isHookInner = (sec) => /^\s*\[\[hooks\.\w+\.hooks\]\]\s*$/.test(sec[0]);
|
|
4291
|
+
const kept = [];
|
|
4292
|
+
for (let i = 0; i < sections.length; i++) {
|
|
4293
|
+
if (!isHookHeader(sections[i])) { kept.push(sections[i]); continue; }
|
|
4294
|
+
let j = i + 1;
|
|
4295
|
+
while (j < sections.length && isHookInner(sections[j])) j++;
|
|
4296
|
+
const group = sections.slice(i, j);
|
|
4297
|
+
const ours = group.some((sec) => sec.some((l) => /^\s*command\s*=/.test(l) && bdbCodexScripts.some((n) => l.includes(n))));
|
|
4298
|
+
if (ours) {
|
|
4299
|
+
// keep trailing blanks/comments (they may belong to another tool's marker)
|
|
4300
|
+
const tail = group.flat();
|
|
4301
|
+
let end = tail.length;
|
|
4302
|
+
while (end > 0 && /^\s*(#.*)?$/.test(tail[end - 1])) end--;
|
|
4303
|
+
if (end < tail.length) kept.push(tail.slice(end));
|
|
4304
|
+
} else kept.push(...group);
|
|
4305
|
+
i = j - 1;
|
|
4306
|
+
}
|
|
4307
|
+
content = `${kept.flat().join(eol).trimEnd()}${eol}${eol}${tomlSnippet.split('\n').join(eol)}${eol}`;
|
|
4188
4308
|
|
|
4189
4309
|
try {
|
|
4190
4310
|
fs.mkdirSync(path.dirname(configTomlPath), { recursive: true });
|
|
@@ -4350,13 +4470,33 @@ async function promptOptionalModules(installedModules) {
|
|
|
4350
4470
|
required: false
|
|
4351
4471
|
}));
|
|
4352
4472
|
|
|
4473
|
+
return (await runModuleInstalls(allModules, chosen, installedModules)).installed;
|
|
4474
|
+
}
|
|
4475
|
+
|
|
4476
|
+
// Module installers return false (or throw) on failure; undefined/true mean success.
|
|
4477
|
+
// Only successes are recorded, so the manifest never claims a module that is not there.
|
|
4478
|
+
const moduleFailures = [];
|
|
4479
|
+
async function runModuleInstalls(allModules, chosen, installedModules) {
|
|
4480
|
+
const installed = [];
|
|
4481
|
+
const failed = [];
|
|
4353
4482
|
for (const mod of allModules) {
|
|
4354
|
-
if (chosen.includes(mod.id))
|
|
4355
|
-
|
|
4356
|
-
|
|
4483
|
+
if (!chosen.includes(mod.id)) continue;
|
|
4484
|
+
let reason = null;
|
|
4485
|
+
try {
|
|
4486
|
+
if (await mod.fn() === false) reason = 'installation reported a failure (see warnings above)';
|
|
4487
|
+
} catch (e) {
|
|
4488
|
+
reason = e && e.message ? e.message : String(e);
|
|
4489
|
+
}
|
|
4490
|
+
if (reason === null) {
|
|
4491
|
+
installed.push(mod.id);
|
|
4492
|
+
if (!installedModules.includes(mod.id)) installedModules.push(mod.id);
|
|
4493
|
+
} else {
|
|
4494
|
+
failed.push({ id: mod.id, name: mod.name, reason });
|
|
4495
|
+
moduleFailures.push({ id: mod.id, name: mod.name, reason });
|
|
4496
|
+
log.warn(`${mod.name} was not installed: ${reason}`);
|
|
4357
4497
|
}
|
|
4358
4498
|
}
|
|
4359
|
-
return
|
|
4499
|
+
return { installed, failed };
|
|
4360
4500
|
}
|
|
4361
4501
|
|
|
4362
4502
|
// Module ids whose daemons ship a web interface or background service worth
|
|
@@ -4397,6 +4537,107 @@ function shouldAutostartLaunchpad(overrides = {}) {
|
|
|
4397
4537
|
return isDevEnvironment(overrides);
|
|
4398
4538
|
}
|
|
4399
4539
|
|
|
4540
|
+
// Plan Canvas autostart is strictly opt-in (--autostart-plan-canvas): unlike
|
|
4541
|
+
// the launchpad opener it is NOT implied by a dev checkout. The server never
|
|
4542
|
+
// idle-exits there (AOS_PLAN_CANVAS_IDLE_MS=0 is honoured by server.js).
|
|
4543
|
+
const PLAN_CANVAS_LABEL = 'com.bdb.plan-canvas';
|
|
4544
|
+
|
|
4545
|
+
function shouldAutostartPlanCanvas(overrides = {}) {
|
|
4546
|
+
const argv = overrides.argv || process.argv;
|
|
4547
|
+
return argv.includes('--autostart-plan-canvas');
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
function buildPlanCanvasPlist({ nodeBin, scriptPath, home, logDir }) {
|
|
4551
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4552
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4553
|
+
<plist version="1.0">
|
|
4554
|
+
<dict>
|
|
4555
|
+
<key>Label</key>
|
|
4556
|
+
<string>${PLAN_CANVAS_LABEL}</string>
|
|
4557
|
+
<key>ProgramArguments</key>
|
|
4558
|
+
<array>
|
|
4559
|
+
<string>${nodeBin}</string>
|
|
4560
|
+
<string>${scriptPath}</string>
|
|
4561
|
+
<string>server</string>
|
|
4562
|
+
</array>
|
|
4563
|
+
<key>EnvironmentVariables</key>
|
|
4564
|
+
<dict>
|
|
4565
|
+
<key>AOS_PLAN_CANVAS_IDLE_MS</key>
|
|
4566
|
+
<string>0</string>
|
|
4567
|
+
</dict>
|
|
4568
|
+
<key>WorkingDirectory</key>
|
|
4569
|
+
<string>${home}</string>
|
|
4570
|
+
<key>RunAtLoad</key>
|
|
4571
|
+
<true/>
|
|
4572
|
+
<key>KeepAlive</key>
|
|
4573
|
+
<true/>
|
|
4574
|
+
<key>ThrottleInterval</key>
|
|
4575
|
+
<integer>30</integer>
|
|
4576
|
+
<key>StandardOutPath</key>
|
|
4577
|
+
<string>${path.join(logDir, 'plan-canvas.stdout.log')}</string>
|
|
4578
|
+
<key>StandardErrorPath</key>
|
|
4579
|
+
<string>${path.join(logDir, 'plan-canvas.stderr.log')}</string>
|
|
4580
|
+
</dict>
|
|
4581
|
+
</plist>`;
|
|
4582
|
+
}
|
|
4583
|
+
|
|
4584
|
+
// Same bat + hidden-vbs-in-Startup mechanism as the OpenWiki visualizer.
|
|
4585
|
+
function buildPlanCanvasWinFiles({ nodeBin, scriptPath, home, logDir, batPath }) {
|
|
4586
|
+
const out = path.join(logDir, 'plan-canvas.stdout.log');
|
|
4587
|
+
const err = path.join(logDir, 'plan-canvas.stderr.log');
|
|
4588
|
+
return {
|
|
4589
|
+
bat: `@echo off\r\ncd /d "${home}"\r\nset AOS_PLAN_CANVAS_IDLE_MS=0\r\n"${nodeBin}" "${scriptPath}" server >> "${out}" 2>> "${err}"\r\n`,
|
|
4590
|
+
vbs: `Set WshShell = CreateObject("WScript.Shell")\r\nWshShell.CurrentDirectory = "${home}"\r\nWshShell.Run """${batPath}""", 0, False\r\n`,
|
|
4591
|
+
};
|
|
4592
|
+
}
|
|
4593
|
+
|
|
4594
|
+
function installPlanCanvasAutostart(overrides = {}) {
|
|
4595
|
+
if (!shouldAutostartPlanCanvas(overrides)) return false;
|
|
4596
|
+
if ((overrides.dryRun ?? DRY_RUN)) {
|
|
4597
|
+
log.step('[dry-run] would register the Plan Canvas background server (Port 4519, no idle exit)');
|
|
4598
|
+
return false;
|
|
4599
|
+
}
|
|
4600
|
+
const home = overrides.homeDir || homeDir;
|
|
4601
|
+
const platform = overrides.platform || process.platform;
|
|
4602
|
+
const scriptPath = [
|
|
4603
|
+
path.join(home, '.agents', 'skills', 'plan-canvas', 'scripts', 'plan-canvas.js'),
|
|
4604
|
+
path.join(srcDir, 'skills', 'global_config', 'plan-canvas', 'scripts', 'plan-canvas.js'),
|
|
4605
|
+
].find(p => fs.existsSync(p));
|
|
4606
|
+
if (!scriptPath) {
|
|
4607
|
+
log.warn('Plan Canvas autostart skipped: plan-canvas.js not found. Start it with: aos-plan-canvas server');
|
|
4608
|
+
return false;
|
|
4609
|
+
}
|
|
4610
|
+
const nodeBin = overrides.nodeBin || process.execPath;
|
|
4611
|
+
const logDir = path.join(home, '.agents', 'logs');
|
|
4612
|
+
try {
|
|
4613
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
4614
|
+
if (platform === 'darwin') {
|
|
4615
|
+
const plistPath = path.join(home, 'Library', 'LaunchAgents', `${PLAN_CANVAS_LABEL}.plist`);
|
|
4616
|
+
fs.mkdirSync(path.dirname(plistPath), { recursive: true });
|
|
4617
|
+
fs.writeFileSync(plistPath, buildPlanCanvasPlist({ nodeBin, scriptPath, home, logDir }));
|
|
4618
|
+
if (!overrides.skipLaunchctl) {
|
|
4619
|
+
execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
|
|
4620
|
+
execSync(`launchctl load -w "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
|
|
4621
|
+
}
|
|
4622
|
+
log.success('Plan Canvas LaunchAgent registered (Port 4519)');
|
|
4623
|
+
} else if (platform === 'win32') {
|
|
4624
|
+
const startupDir = path.join(overrides.appData || process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
|
|
4625
|
+
fs.mkdirSync(startupDir, { recursive: true });
|
|
4626
|
+
const batPath = path.join(home, '.agents', 'run-plan-canvas.bat');
|
|
4627
|
+
const vbsPath = path.join(startupDir, `${PLAN_CANVAS_LABEL}.vbs`);
|
|
4628
|
+
const files = buildPlanCanvasWinFiles({ nodeBin, scriptPath, home, logDir, batPath });
|
|
4629
|
+
fs.writeFileSync(batPath, files.bat, 'utf-8');
|
|
4630
|
+
fs.writeFileSync(vbsPath, files.vbs, 'utf-8');
|
|
4631
|
+
if (!overrides.skipLaunchctl) spawn('wscript.exe', [vbsPath], { detached: true, stdio: 'ignore' }).unref();
|
|
4632
|
+
log.success('Plan Canvas Startup entry registered (Port 4519)');
|
|
4633
|
+
} else {
|
|
4634
|
+
log.step('Plan Canvas autostart is only wired for macOS and Windows. Start it with: aos-plan-canvas server');
|
|
4635
|
+
return false;
|
|
4636
|
+
}
|
|
4637
|
+
return true;
|
|
4638
|
+
} catch (e) { logDebug(e, 'plan canvas autostart'); return false; }
|
|
4639
|
+
}
|
|
4640
|
+
|
|
4400
4641
|
function generateAndOpenLaunchpad(installedModules = []) {
|
|
4401
4642
|
if (DRY_RUN) {
|
|
4402
4643
|
log.step('[dry-run] would generate & open BDB Launchpad HTML');
|
|
@@ -4532,6 +4773,16 @@ function generateAndOpenLaunchpad(installedModules = []) {
|
|
|
4532
4773
|
.card.offline .card-hint {
|
|
4533
4774
|
display: block;
|
|
4534
4775
|
}
|
|
4776
|
+
.card-hint code {
|
|
4777
|
+
font-family: var(--mono);
|
|
4778
|
+
font-style: normal;
|
|
4779
|
+
color: #ffffff;
|
|
4780
|
+
background: #0a0a0a;
|
|
4781
|
+
border: 1px solid var(--accent);
|
|
4782
|
+
border-radius: 4px;
|
|
4783
|
+
padding: 1px 6px;
|
|
4784
|
+
cursor: copy;
|
|
4785
|
+
}
|
|
4535
4786
|
.footer {
|
|
4536
4787
|
margin-top: 28px;
|
|
4537
4788
|
text-align: center;
|
|
@@ -4722,6 +4973,25 @@ function generateAndOpenLaunchpad(installedModules = []) {
|
|
|
4722
4973
|
<span class="status-dot" id="dot-store" title="Checking..."></span>
|
|
4723
4974
|
</div>
|
|
4724
4975
|
</a>
|
|
4976
|
+
<a class="card" href="http://127.0.0.1:4519/" target="_blank">
|
|
4977
|
+
<div class="card-content">
|
|
4978
|
+
<div class="card-icon"><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
4979
|
+
<defs><linearGradient id="grad-plancanvas" x1="50%" y1="0%" x2="50%" y2="100%"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#9b30c4"/></linearGradient></defs>
|
|
4980
|
+
<rect x="18" y="22" width="64" height="56" rx="4" fill="url(#grad-plancanvas)" opacity="0.85"/>
|
|
4981
|
+
<rect x="26" y="30" width="22" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/><rect x="52" y="30" width="22" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/>
|
|
4982
|
+
<line x1="26" y1="54" x2="74" y2="54" stroke="#0a0a0a" stroke-width="2.5" opacity="0.5"/><line x1="26" y1="64" x2="56" y2="64" stroke="#0a0a0a" stroke-width="2.5" opacity="0.5"/>
|
|
4983
|
+
</svg></div>
|
|
4984
|
+
<div class="card-info">
|
|
4985
|
+
<h2>Plan Canvas</h2>
|
|
4986
|
+
<p>Review plans in the browser: annotate, chat, approve</p>
|
|
4987
|
+
<div class="card-hint">offline — run: <code data-copy="aos-plan-canvas server" title="Click to copy">aos-plan-canvas server</code></div>
|
|
4988
|
+
</div>
|
|
4989
|
+
</div>
|
|
4990
|
+
<div class="card-meta">
|
|
4991
|
+
<span class="port-pill">:4519</span>
|
|
4992
|
+
<span class="status-dot" id="dot-plancanvas" title="Checking..."></span>
|
|
4993
|
+
</div>
|
|
4994
|
+
</a>
|
|
4725
4995
|
</div>
|
|
4726
4996
|
<div class="footer">
|
|
4727
4997
|
<span>Autostart Daemons • 127.0.0.1</span>
|
|
@@ -4749,7 +5019,26 @@ function generateAndOpenLaunchpad(installedModules = []) {
|
|
|
4749
5019
|
checkHealth('http://127.0.0.1:9080', 'dot-remote');
|
|
4750
5020
|
checkHealth('http://127.0.0.1:4321', 'dot-openwiki');
|
|
4751
5021
|
checkHealth('http://127.0.0.1:4322', 'dot-store');
|
|
4752
|
-
|
|
5022
|
+
checkHealth('http://127.0.0.1:4519', 'dot-plancanvas');
|
|
5023
|
+
}
|
|
5024
|
+
document.addEventListener('click', (ev) => {
|
|
5025
|
+
const el = ev.target.closest && ev.target.closest('[data-copy]');
|
|
5026
|
+
if (!el) return;
|
|
5027
|
+
ev.preventDefault();
|
|
5028
|
+
ev.stopPropagation();
|
|
5029
|
+
const text = el.getAttribute('data-copy');
|
|
5030
|
+
const fallback = () => {
|
|
5031
|
+
const ta = document.createElement('textarea');
|
|
5032
|
+
ta.value = text;
|
|
5033
|
+
document.body.appendChild(ta);
|
|
5034
|
+
ta.select();
|
|
5035
|
+
try { document.execCommand('copy'); } catch (e) {}
|
|
5036
|
+
ta.remove();
|
|
5037
|
+
};
|
|
5038
|
+
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).catch(fallback);
|
|
5039
|
+
else fallback();
|
|
5040
|
+
el.title = 'Copied';
|
|
5041
|
+
}, true);
|
|
4753
5042
|
checkAllHealth();
|
|
4754
5043
|
setInterval(checkAllHealth, 5000);
|
|
4755
5044
|
</script>
|
|
@@ -4962,6 +5251,7 @@ async function universalHarnessSync(primaryMcpConfigPath, installedModules = [])
|
|
|
4962
5251
|
}
|
|
4963
5252
|
log.success('Universal Sync Complete!');
|
|
4964
5253
|
generateAndOpenLaunchpad(installedModules);
|
|
5254
|
+
installPlanCanvasAutostart();
|
|
4965
5255
|
}
|
|
4966
5256
|
|
|
4967
5257
|
async function runQuickUpdate(installState) {
|
|
@@ -5128,7 +5418,41 @@ function installAosCli() {
|
|
|
5128
5418
|
}
|
|
5129
5419
|
}
|
|
5130
5420
|
|
|
5421
|
+
// A running daemon may execute dest; overwriting its inode keeps stale code-signature
|
|
5422
|
+
// state on macOS and every new exec dies with SIGKILL. Rename gives dest a fresh inode.
|
|
5423
|
+
function installBinaryAtomically(src, dest) {
|
|
5424
|
+
const tmp = path.join(path.dirname(dest), `.${path.basename(dest)}.${process.pid}.${Date.now()}.tmp`);
|
|
5425
|
+
try {
|
|
5426
|
+
fs.copyFileSync(src, tmp);
|
|
5427
|
+
if (process.platform !== 'win32') fs.chmodSync(tmp, 0o755);
|
|
5428
|
+
fs.renameSync(tmp, dest);
|
|
5429
|
+
} catch (e) {
|
|
5430
|
+
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
5431
|
+
throw e;
|
|
5432
|
+
}
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5131
5435
|
async function main() {
|
|
5436
|
+
const args = process.argv.slice(2);
|
|
5437
|
+
if (args.includes('--version') || args.includes('-V')) {
|
|
5438
|
+
console.log(pkg.version);
|
|
5439
|
+
return;
|
|
5440
|
+
}
|
|
5441
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
5442
|
+
console.log(`Usage: aos [command] [options]
|
|
5443
|
+
|
|
5444
|
+
Commands:
|
|
5445
|
+
store Browse and install skills
|
|
5446
|
+
doctor|checkup Check the installation
|
|
5447
|
+
|
|
5448
|
+
Options:
|
|
5449
|
+
-y, --yes Non-interactive install (implied without a TTY)
|
|
5450
|
+
--dry-run Show what would change
|
|
5451
|
+
--verbose, -v Verbose output
|
|
5452
|
+
-V, --version Print the version and exit
|
|
5453
|
+
-h, --help Print this help and exit`);
|
|
5454
|
+
return;
|
|
5455
|
+
}
|
|
5132
5456
|
if (process.argv[2] === 'store') {
|
|
5133
5457
|
const storeScript = path.join(srcDir, 'bin', 'aos-store.mjs');
|
|
5134
5458
|
const result = spawnSync(process.execPath, [storeScript, ...process.argv.slice(3)], { stdio: 'inherit' });
|
|
@@ -5582,10 +5906,14 @@ if (require.main === module) {
|
|
|
5582
5906
|
|
|
5583
5907
|
// Exported for tests -- requiring installer.js must not launch the TUI.
|
|
5584
5908
|
module.exports = {
|
|
5909
|
+
installBinaryAtomically,
|
|
5585
5910
|
installOSAgentWorkspace,
|
|
5586
5911
|
downloadOrUpdateModule,
|
|
5587
5912
|
detectPlatforms,
|
|
5588
5913
|
markPlatformsExplicit,
|
|
5914
|
+
runModuleInstalls,
|
|
5915
|
+
describeExecError,
|
|
5916
|
+
keepExistingEnvValues,
|
|
5589
5917
|
mergeBdbSettingsHooks,
|
|
5590
5918
|
mergeAntigravityHooks,
|
|
5591
5919
|
mergeCodexTomlHooks,
|
|
@@ -5610,6 +5938,7 @@ module.exports = {
|
|
|
5610
5938
|
INSTALL_MANIFEST_PATH,
|
|
5611
5939
|
// Agent compilers & pipeline helpers
|
|
5612
5940
|
parseAgentsMd,
|
|
5941
|
+
loadCommonSkills,
|
|
5613
5942
|
compileClaudeAgents,
|
|
5614
5943
|
compileOpenCodeAgents,
|
|
5615
5944
|
compileCodexAgents,
|
|
@@ -5620,6 +5949,10 @@ module.exports = {
|
|
|
5620
5949
|
generateAndOpenLaunchpad,
|
|
5621
5950
|
shouldOpenLaunchpad,
|
|
5622
5951
|
shouldAutostartLaunchpad,
|
|
5952
|
+
shouldAutostartPlanCanvas,
|
|
5953
|
+
buildPlanCanvasPlist,
|
|
5954
|
+
buildPlanCanvasWinFiles,
|
|
5955
|
+
installPlanCanvasAutostart,
|
|
5623
5956
|
isDevEnvironment,
|
|
5624
5957
|
LAUNCHPAD_WEB_MODULES,
|
|
5625
5958
|
LAUNCHPAD_OPEN_FLAGS,
|