aether-colony 5.0.0 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aether/aether-utils.sh +3226 -3345
- package/.aether/agents-claude/aether-ambassador.md +265 -0
- package/.aether/agents-claude/aether-archaeologist.md +327 -0
- package/.aether/agents-claude/aether-architect.md +236 -0
- package/.aether/agents-claude/aether-auditor.md +271 -0
- package/.aether/agents-claude/aether-builder.md +224 -0
- package/.aether/agents-claude/aether-chaos.md +269 -0
- package/.aether/agents-claude/aether-chronicler.md +305 -0
- package/.aether/agents-claude/aether-gatekeeper.md +330 -0
- package/.aether/agents-claude/aether-includer.md +374 -0
- package/.aether/agents-claude/aether-keeper.md +272 -0
- package/.aether/agents-claude/aether-measurer.md +322 -0
- package/.aether/agents-claude/aether-oracle.md +237 -0
- package/.aether/agents-claude/aether-probe.md +211 -0
- package/.aether/agents-claude/aether-queen.md +330 -0
- package/.aether/agents-claude/aether-route-setter.md +178 -0
- package/.aether/agents-claude/aether-sage.md +418 -0
- package/.aether/agents-claude/aether-scout.md +179 -0
- package/.aether/agents-claude/aether-surveyor-disciplines.md +417 -0
- package/.aether/agents-claude/aether-surveyor-nest.md +355 -0
- package/.aether/agents-claude/aether-surveyor-pathogens.md +289 -0
- package/.aether/agents-claude/aether-surveyor-provisions.md +360 -0
- package/.aether/agents-claude/aether-tracker.md +270 -0
- package/.aether/agents-claude/aether-watcher.md +280 -0
- package/.aether/agents-claude/aether-weaver.md +248 -0
- package/.aether/commands/archaeology.yaml +653 -0
- package/.aether/commands/build.yaml +1221 -0
- package/.aether/commands/chaos.yaml +653 -0
- package/.aether/commands/colonize.yaml +442 -0
- package/.aether/commands/continue.yaml +1484 -0
- package/.aether/commands/council.yaml +509 -0
- package/.aether/commands/data-clean.yaml +80 -0
- package/.aether/commands/dream.yaml +275 -0
- package/.aether/commands/entomb.yaml +863 -0
- package/.aether/commands/export-signals.yaml +64 -0
- package/.aether/commands/feedback.yaml +158 -0
- package/.aether/commands/flag.yaml +160 -0
- package/.aether/commands/flags.yaml +177 -0
- package/.aether/commands/focus.yaml +112 -0
- package/.aether/commands/help.yaml +167 -0
- package/.aether/commands/history.yaml +137 -0
- package/.aether/commands/import-signals.yaml +79 -0
- package/.aether/commands/init.yaml +502 -0
- package/.aether/commands/insert-phase.yaml +102 -0
- package/.aether/commands/interpret.yaml +285 -0
- package/.aether/commands/lay-eggs.yaml +224 -0
- package/.aether/commands/maturity.yaml +122 -0
- package/.aether/commands/memory-details.yaml +74 -0
- package/.aether/commands/migrate-state.yaml +174 -0
- package/.aether/commands/oracle.yaml +1224 -0
- package/.aether/commands/organize.yaml +446 -0
- package/.aether/commands/patrol.yaml +621 -0
- package/.aether/commands/pause-colony.yaml +424 -0
- package/.aether/commands/phase.yaml +124 -0
- package/.aether/commands/pheromones.yaml +153 -0
- package/.aether/commands/plan.yaml +1364 -0
- package/.aether/commands/preferences.yaml +63 -0
- package/.aether/commands/quick.yaml +104 -0
- package/.aether/commands/redirect.yaml +123 -0
- package/.aether/commands/resume-colony.yaml +375 -0
- package/.aether/commands/resume.yaml +407 -0
- package/.aether/commands/run.yaml +229 -0
- package/.aether/commands/seal.yaml +1214 -0
- package/.aether/commands/skill-create.yaml +337 -0
- package/.aether/commands/status.yaml +408 -0
- package/.aether/commands/swarm.yaml +352 -0
- package/.aether/commands/tunnels.yaml +814 -0
- package/.aether/commands/update.yaml +131 -0
- package/.aether/commands/verify-castes.yaml +159 -0
- package/.aether/commands/watch.yaml +454 -0
- package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
- package/.aether/docs/QUEEN-SYSTEM.md +11 -11
- package/.aether/docs/README.md +32 -2
- package/.aether/docs/command-playbooks/README.md +23 -0
- package/.aether/docs/command-playbooks/build-complete.md +349 -0
- package/.aether/docs/command-playbooks/build-context.md +282 -0
- package/.aether/docs/command-playbooks/build-full.md +1683 -0
- package/.aether/docs/command-playbooks/build-prep.md +284 -0
- package/.aether/docs/command-playbooks/build-verify.md +405 -0
- package/.aether/docs/command-playbooks/build-wave.md +749 -0
- package/.aether/docs/command-playbooks/continue-advance.md +524 -0
- package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
- package/.aether/docs/command-playbooks/continue-full.md +1725 -0
- package/.aether/docs/command-playbooks/continue-gates.md +686 -0
- package/.aether/docs/command-playbooks/continue-verify.md +407 -0
- package/.aether/docs/context-continuity.md +84 -0
- package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
- package/.aether/docs/error-codes.md +1 -1
- package/.aether/docs/known-issues.md +34 -173
- package/.aether/docs/pheromones.md +86 -6
- package/.aether/docs/plans/pheromone-display-plan.md +257 -0
- package/.aether/docs/queen-commands.md +10 -9
- package/.aether/docs/source-of-truth-map.md +132 -0
- package/.aether/docs/xml-utilities.md +47 -0
- package/.aether/rules/aether-colony.md +23 -13
- package/.aether/scripts/incident-test-add.sh +47 -0
- package/.aether/scripts/weekly-audit.sh +79 -0
- package/.aether/skills/.index.json +649 -0
- package/.aether/skills/colony/.manifest.json +16 -0
- package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
- package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
- package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
- package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
- package/.aether/skills/colony/context-management/SKILL.md +80 -0
- package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
- package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
- package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
- package/.aether/skills/colony/state-safety/SKILL.md +84 -0
- package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
- package/.aether/skills/domain/.manifest.json +24 -0
- package/.aether/skills/domain/README.md +33 -0
- package/.aether/skills/domain/django/SKILL.md +49 -0
- package/.aether/skills/domain/docker/SKILL.md +52 -0
- package/.aether/skills/domain/golang/SKILL.md +52 -0
- package/.aether/skills/domain/graphql/SKILL.md +51 -0
- package/.aether/skills/domain/html-css/SKILL.md +48 -0
- package/.aether/skills/domain/nextjs/SKILL.md +45 -0
- package/.aether/skills/domain/nodejs/SKILL.md +53 -0
- package/.aether/skills/domain/postgresql/SKILL.md +53 -0
- package/.aether/skills/domain/prisma/SKILL.md +59 -0
- package/.aether/skills/domain/python/SKILL.md +50 -0
- package/.aether/skills/domain/rails/SKILL.md +52 -0
- package/.aether/skills/domain/react/SKILL.md +45 -0
- package/.aether/skills/domain/rest-api/SKILL.md +58 -0
- package/.aether/skills/domain/svelte/SKILL.md +47 -0
- package/.aether/skills/domain/tailwind/SKILL.md +45 -0
- package/.aether/skills/domain/testing/SKILL.md +53 -0
- package/.aether/skills/domain/typescript/SKILL.md +58 -0
- package/.aether/skills/domain/vue/SKILL.md +49 -0
- package/.aether/templates/QUEEN.md.template +23 -41
- package/.aether/templates/colony-state-reset.jq.template +1 -0
- package/.aether/templates/colony-state.template.json +4 -0
- package/.aether/templates/learning-observations.template.json +6 -0
- package/.aether/templates/midden.template.json +13 -0
- package/.aether/templates/pheromones.template.json +6 -0
- package/.aether/templates/session.template.json +9 -0
- package/.aether/utils/atomic-write.sh +63 -17
- package/.aether/utils/chamber-utils.sh +145 -2
- package/.aether/utils/council.sh +425 -0
- package/.aether/utils/emoji-audit.sh +166 -0
- package/.aether/utils/error-handler.sh +21 -7
- package/.aether/utils/file-lock.sh +182 -27
- package/.aether/utils/flag.sh +278 -0
- package/.aether/utils/hive.sh +572 -0
- package/.aether/utils/immune.sh +508 -0
- package/.aether/utils/learning.sh +1928 -0
- package/.aether/utils/midden.sh +520 -0
- package/.aether/utils/oracle/oracle.md +168 -0
- package/.aether/utils/oracle/oracle.sh +1023 -0
- package/.aether/utils/pheromone.sh +2029 -0
- package/.aether/utils/queen.sh +1710 -0
- package/.aether/utils/scan.sh +860 -0
- package/.aether/utils/semantic-cli.sh +10 -8
- package/.aether/utils/session.sh +816 -0
- package/.aether/utils/skills.sh +509 -0
- package/.aether/utils/spawn-tree.sh +103 -271
- package/.aether/utils/spawn.sh +260 -0
- package/.aether/utils/state-api.sh +389 -0
- package/.aether/utils/state-loader.sh +8 -6
- package/.aether/utils/suggest.sh +611 -0
- package/.aether/utils/swarm-display.sh +10 -1
- package/.aether/utils/swarm.sh +1004 -0
- package/.aether/utils/watch-spawn-tree.sh +11 -2
- package/.aether/utils/xml-compose.sh +2 -2
- package/.aether/utils/xml-convert.sh +9 -5
- package/.aether/utils/xml-core.sh +5 -9
- package/.aether/utils/xml-query.sh +4 -4
- package/.aether/workers.md +86 -67
- package/.claude/agents/ant/aether-ambassador.md +2 -1
- package/.claude/agents/ant/aether-archaeologist.md +6 -1
- package/.claude/agents/ant/aether-architect.md +236 -0
- package/.claude/agents/ant/aether-auditor.md +6 -1
- package/.claude/agents/ant/aether-builder.md +38 -1
- package/.claude/agents/ant/aether-chaos.md +2 -1
- package/.claude/agents/ant/aether-chronicler.md +1 -0
- package/.claude/agents/ant/aether-gatekeeper.md +6 -1
- package/.claude/agents/ant/aether-includer.md +1 -0
- package/.claude/agents/ant/aether-keeper.md +1 -0
- package/.claude/agents/ant/aether-measurer.md +6 -1
- package/.claude/agents/ant/aether-oracle.md +237 -0
- package/.claude/agents/ant/aether-probe.md +2 -1
- package/.claude/agents/ant/aether-queen.md +6 -1
- package/.claude/agents/ant/aether-route-setter.md +6 -1
- package/.claude/agents/ant/aether-sage.md +68 -3
- package/.claude/agents/ant/aether-scout.md +38 -1
- package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
- package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
- package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
- package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
- package/.claude/agents/ant/aether-tracker.md +6 -1
- package/.claude/agents/ant/aether-watcher.md +37 -1
- package/.claude/agents/ant/aether-weaver.md +2 -1
- package/.claude/commands/ant/archaeology.md +1 -8
- package/.claude/commands/ant/build.md +43 -1159
- package/.claude/commands/ant/chaos.md +1 -14
- package/.claude/commands/ant/colonize.md +3 -14
- package/.claude/commands/ant/continue.md +40 -1026
- package/.claude/commands/ant/council.md +213 -15
- package/.claude/commands/ant/data-clean.md +81 -0
- package/.claude/commands/ant/dream.md +12 -9
- package/.claude/commands/ant/entomb.md +62 -87
- package/.claude/commands/ant/export-signals.md +57 -0
- package/.claude/commands/ant/feedback.md +18 -0
- package/.claude/commands/ant/flag.md +12 -0
- package/.claude/commands/ant/flags.md +22 -8
- package/.claude/commands/ant/focus.md +18 -0
- package/.claude/commands/ant/help.md +40 -8
- package/.claude/commands/ant/history.md +3 -0
- package/.claude/commands/ant/import-signals.md +71 -0
- package/.claude/commands/ant/init.md +349 -191
- package/.claude/commands/ant/insert-phase.md +105 -0
- package/.claude/commands/ant/interpret.md +11 -0
- package/.claude/commands/ant/lay-eggs.md +167 -158
- package/.claude/commands/ant/maturity.md +22 -11
- package/.claude/commands/ant/memory-details.md +77 -0
- package/.claude/commands/ant/migrate-state.md +6 -0
- package/.claude/commands/ant/oracle.md +317 -62
- package/.claude/commands/ant/organize.md +10 -5
- package/.claude/commands/ant/patrol.md +620 -0
- package/.claude/commands/ant/pause-colony.md +8 -22
- package/.claude/commands/ant/phase.md +26 -37
- package/.claude/commands/ant/pheromones.md +156 -0
- package/.claude/commands/ant/plan.md +199 -50
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/quick.md +100 -0
- package/.claude/commands/ant/redirect.md +18 -0
- package/.claude/commands/ant/resume-colony.md +37 -22
- package/.claude/commands/ant/resume.md +60 -7
- package/.claude/commands/ant/run.md +231 -0
- package/.claude/commands/ant/seal.md +506 -78
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +171 -1
- package/.claude/commands/ant/swarm.md +11 -23
- package/.claude/commands/ant/tunnels.md +1 -0
- package/.claude/commands/ant/update.md +58 -135
- package/.claude/commands/ant/verify-castes.md +90 -42
- package/.claude/commands/ant/watch.md +1 -0
- package/.opencode/agents/aether-ambassador.md +1 -1
- package/.opencode/agents/aether-architect.md +133 -0
- package/.opencode/agents/aether-builder.md +3 -3
- package/.opencode/agents/aether-oracle.md +137 -0
- package/.opencode/agents/aether-queen.md +1 -1
- package/.opencode/agents/aether-route-setter.md +1 -1
- package/.opencode/agents/aether-scout.md +1 -1
- package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
- package/.opencode/agents/aether-surveyor-nest.md +6 -1
- package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
- package/.opencode/agents/aether-surveyor-provisions.md +6 -1
- package/.opencode/agents/aether-tracker.md +1 -1
- package/.opencode/agents/aether-watcher.md +1 -1
- package/.opencode/agents/aether-weaver.md +1 -1
- package/.opencode/commands/ant/archaeology.md +7 -14
- package/.opencode/commands/ant/build.md +54 -88
- package/.opencode/commands/ant/chaos.md +7 -24
- package/.opencode/commands/ant/colonize.md +10 -17
- package/.opencode/commands/ant/continue.md +595 -66
- package/.opencode/commands/ant/council.md +150 -18
- package/.opencode/commands/ant/data-clean.md +77 -0
- package/.opencode/commands/ant/dream.md +15 -17
- package/.opencode/commands/ant/entomb.md +28 -18
- package/.opencode/commands/ant/export-signals.md +54 -0
- package/.opencode/commands/ant/feedback.md +24 -5
- package/.opencode/commands/ant/flag.md +16 -4
- package/.opencode/commands/ant/flags.md +24 -10
- package/.opencode/commands/ant/focus.md +22 -5
- package/.opencode/commands/ant/help.md +41 -8
- package/.opencode/commands/ant/history.md +9 -0
- package/.opencode/commands/ant/import-signals.md +68 -0
- package/.opencode/commands/ant/init.md +396 -154
- package/.opencode/commands/ant/insert-phase.md +111 -0
- package/.opencode/commands/ant/interpret.md +16 -0
- package/.opencode/commands/ant/lay-eggs.md +184 -112
- package/.opencode/commands/ant/maturity.md +18 -2
- package/.opencode/commands/ant/memory-details.md +83 -0
- package/.opencode/commands/ant/migrate-state.md +12 -0
- package/.opencode/commands/ant/oracle.md +322 -67
- package/.opencode/commands/ant/organize.md +14 -12
- package/.opencode/commands/ant/patrol.md +626 -0
- package/.opencode/commands/ant/pause-colony.md +12 -29
- package/.opencode/commands/ant/phase.md +30 -40
- package/.opencode/commands/ant/pheromones.md +162 -0
- package/.opencode/commands/ant/plan.md +210 -57
- package/.opencode/commands/ant/preferences.md +71 -0
- package/.opencode/commands/ant/quick.md +91 -0
- package/.opencode/commands/ant/redirect.md +22 -5
- package/.opencode/commands/ant/resume-colony.md +41 -29
- package/.opencode/commands/ant/resume.md +80 -20
- package/.opencode/commands/ant/run.md +237 -0
- package/.opencode/commands/ant/seal.md +230 -25
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +125 -30
- package/.opencode/commands/ant/swarm.md +3 -345
- package/.opencode/commands/ant/tunnels.md +3 -9
- package/.opencode/commands/ant/update.md +63 -127
- package/.opencode/commands/ant/verify-castes.md +96 -42
- package/.opencode/commands/ant/watch.md +7 -0
- package/CHANGELOG.md +368 -1
- package/README.md +195 -324
- package/bin/cli.js +236 -429
- package/bin/generate-commands.js +186 -0
- package/bin/generate-commands.sh +128 -89
- package/bin/lib/spawn-logger.js +0 -15
- package/bin/lib/update-transaction.js +285 -35
- package/bin/npx-install.js +178 -0
- package/bin/validate-package.sh +85 -3
- package/package.json +16 -4
- package/.aether/CONTEXT.md +0 -160
- package/.aether/docs/QUEEN.md +0 -84
- package/.aether/exchange/colony-registry.xml +0 -11
- package/.aether/exchange/pheromones.xml +0 -87
- package/.aether/exchange/queen-wisdom.xml +0 -14
- package/.aether/model-profiles.yaml +0 -100
- package/.aether/utils/spawn-with-model.sh +0 -56
- package/bin/lib/model-profiles.js +0 -445
- package/bin/lib/model-verify.js +0 -288
- package/bin/lib/proxy-health.js +0 -253
- package/bin/lib/telemetry.js +0 -441
package/bin/cli.js
CHANGED
|
@@ -22,31 +22,8 @@ const { logError, logActivity } = require('./lib/logger');
|
|
|
22
22
|
const { UpdateTransaction, UpdateError, UpdateErrorCodes } = require('./lib/update-transaction');
|
|
23
23
|
const { initializeRepo, isInitialized } = require('./lib/init');
|
|
24
24
|
const { syncStateFromPlanning, reconcileStates } = require('./lib/state-sync');
|
|
25
|
-
const { createVerificationReport } = require('./lib/model-verify');
|
|
26
|
-
const {
|
|
27
|
-
loadModelProfiles,
|
|
28
|
-
getAllAssignments,
|
|
29
|
-
getProviderForModel,
|
|
30
|
-
validateCaste,
|
|
31
|
-
validateModel,
|
|
32
|
-
setModelOverride,
|
|
33
|
-
resetModelOverride,
|
|
34
|
-
getEffectiveModel,
|
|
35
|
-
getUserOverrides,
|
|
36
|
-
getModelMetadata,
|
|
37
|
-
getProxyConfig,
|
|
38
|
-
} = require('./lib/model-profiles');
|
|
39
|
-
const {
|
|
40
|
-
checkProxyHealth,
|
|
41
|
-
verifyModelRouting,
|
|
42
|
-
formatProxyStatusColored,
|
|
43
|
-
} = require('./lib/proxy-health');
|
|
44
25
|
const { findNestmates, formatNestmates, loadNestmateTodos } = require('./lib/nestmate-loader');
|
|
45
26
|
const { logSpawn, formatSpawnTree } = require('./lib/spawn-logger');
|
|
46
|
-
const {
|
|
47
|
-
getTelemetrySummary,
|
|
48
|
-
getModelPerformance,
|
|
49
|
-
} = require('./lib/telemetry');
|
|
50
27
|
|
|
51
28
|
// Color palette
|
|
52
29
|
const c = require('./lib/colors');
|
|
@@ -65,10 +42,14 @@ if (!HOME) {
|
|
|
65
42
|
}
|
|
66
43
|
|
|
67
44
|
// Claude Code paths (global)
|
|
68
|
-
const COMMANDS_SRC = path.join(PACKAGE_DIR, 'commands', 'ant');
|
|
45
|
+
const COMMANDS_SRC = path.join(PACKAGE_DIR, '.claude', 'commands', 'ant');
|
|
69
46
|
const COMMANDS_DEST = path.join(HOME, '.claude', 'commands', 'ant');
|
|
70
47
|
const AGENTS_DEST = path.join(HOME, '.claude', 'agents', 'ant');
|
|
71
48
|
|
|
49
|
+
// OpenCode paths (global)
|
|
50
|
+
const OPENCODE_COMMANDS_DEST = path.join(HOME, '.opencode', 'command');
|
|
51
|
+
const OPENCODE_AGENTS_DEST = path.join(HOME, '.opencode', 'agent');
|
|
52
|
+
|
|
72
53
|
// Hub paths
|
|
73
54
|
const HUB_DIR = path.join(HOME, '.aether');
|
|
74
55
|
const HUB_SYSTEM_DIR = path.join(HUB_DIR, 'system');
|
|
@@ -378,6 +359,21 @@ function removeDirSync(dir) {
|
|
|
378
359
|
return count;
|
|
379
360
|
}
|
|
380
361
|
|
|
362
|
+
// Remove only files from dest that exist in source (safe for shared directories)
|
|
363
|
+
function removeFilesFromSource(sourceDir, destDir) {
|
|
364
|
+
if (!fs.existsSync(sourceDir) || !fs.existsSync(destDir)) return 0;
|
|
365
|
+
let count = 0;
|
|
366
|
+
const sourceFiles = fs.readdirSync(sourceDir).filter(f => f.endsWith('.md'));
|
|
367
|
+
for (const file of sourceFiles) {
|
|
368
|
+
const destPath = path.join(destDir, file);
|
|
369
|
+
if (fs.existsSync(destPath)) {
|
|
370
|
+
fs.unlinkSync(destPath);
|
|
371
|
+
count++;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return count;
|
|
375
|
+
}
|
|
376
|
+
|
|
381
377
|
function readJsonSafe(filePath) {
|
|
382
378
|
try {
|
|
383
379
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
@@ -718,6 +714,64 @@ function isGitRepo(repoPath) {
|
|
|
718
714
|
}
|
|
719
715
|
}
|
|
720
716
|
|
|
717
|
+
// Paths that updates should preserve (never overwrite/stash as "managed")
|
|
718
|
+
const UPDATE_PROTECTED_AETHER_DIRS = new Set([
|
|
719
|
+
'data',
|
|
720
|
+
'dreams',
|
|
721
|
+
'oracle',
|
|
722
|
+
'midden',
|
|
723
|
+
'checkpoints',
|
|
724
|
+
'locks',
|
|
725
|
+
'temp',
|
|
726
|
+
'archive',
|
|
727
|
+
'chambers',
|
|
728
|
+
'exchange',
|
|
729
|
+
]);
|
|
730
|
+
|
|
731
|
+
const UPDATE_MANAGED_PREFIXES = [
|
|
732
|
+
'.claude/commands/ant',
|
|
733
|
+
'.claude/agents/ant',
|
|
734
|
+
'.claude/rules',
|
|
735
|
+
'.opencode/commands/ant',
|
|
736
|
+
'.opencode/agents',
|
|
737
|
+
];
|
|
738
|
+
|
|
739
|
+
function normalizePorcelainPath(filePath) {
|
|
740
|
+
let normalized = filePath;
|
|
741
|
+
|
|
742
|
+
// For rename entries, keep only destination path: "old -> new"
|
|
743
|
+
if (normalized.includes(' -> ')) {
|
|
744
|
+
normalized = normalized.split(' -> ').pop();
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Handle quoted porcelain paths (spaces, escaped chars)
|
|
748
|
+
if (normalized.startsWith('"') && normalized.endsWith('"')) {
|
|
749
|
+
normalized = normalized
|
|
750
|
+
.slice(1, -1)
|
|
751
|
+
.replace(/\\"/g, '"')
|
|
752
|
+
.replace(/\\\\/g, '\\');
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return normalized;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function isManagedUpdatePath(filePath) {
|
|
759
|
+
const normalized = normalizePorcelainPath(filePath);
|
|
760
|
+
|
|
761
|
+
if (normalized === '.aether' || normalized.startsWith('.aether/')) {
|
|
762
|
+
const rel = normalized === '.aether' ? '' : normalized.slice('.aether/'.length);
|
|
763
|
+
if (!rel) return true;
|
|
764
|
+
|
|
765
|
+
const first = rel.split('/')[0];
|
|
766
|
+
if (!first || first.startsWith('.')) return false;
|
|
767
|
+
if (UPDATE_PROTECTED_AETHER_DIRS.has(first)) return false;
|
|
768
|
+
if (rel === 'QUEEN.md') return false;
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return UPDATE_MANAGED_PREFIXES.some(prefix => normalized === prefix || normalized.startsWith(`${prefix}/`));
|
|
773
|
+
}
|
|
774
|
+
|
|
721
775
|
function getGitDirtyFiles(repoPath, targetDirs) {
|
|
722
776
|
try {
|
|
723
777
|
const args = targetDirs.filter(d => fs.existsSync(path.join(repoPath, d)));
|
|
@@ -727,7 +781,14 @@ function getGitDirtyFiles(repoPath, targetDirs) {
|
|
|
727
781
|
stdio: 'pipe',
|
|
728
782
|
encoding: 'utf8',
|
|
729
783
|
});
|
|
730
|
-
|
|
784
|
+
const files = [];
|
|
785
|
+
for (const line of result.split('\n')) {
|
|
786
|
+
if (!line || line.length < 4) continue;
|
|
787
|
+
const filePath = line.slice(3);
|
|
788
|
+
if (!isManagedUpdatePath(filePath)) continue;
|
|
789
|
+
files.push(normalizePorcelainPath(filePath));
|
|
790
|
+
}
|
|
791
|
+
return [...new Set(files)];
|
|
731
792
|
} catch {
|
|
732
793
|
return [];
|
|
733
794
|
}
|
|
@@ -750,6 +811,11 @@ function gitStashFiles(repoPath, files) {
|
|
|
750
811
|
// Directories to exclude from hub sync (user data, local state)
|
|
751
812
|
// 'rules' is excluded here because it is synced via a dedicated step (rulesSrc below)
|
|
752
813
|
const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp', 'rules'];
|
|
814
|
+
// Directories excluded only when they are the FIRST path segment under .aether/
|
|
815
|
+
// (e.g., .aether/oracle/ is excluded but .aether/utils/oracle/ is NOT)
|
|
816
|
+
const HUB_EXCLUDE_FIRST_SEGMENT = ['oracle'];
|
|
817
|
+
// Files to exclude from hub sync (repo-local state, not distributable)
|
|
818
|
+
const HUB_EXCLUDE_FILES = ['CONTEXT.md', 'HANDOFF.md'];
|
|
753
819
|
|
|
754
820
|
/**
|
|
755
821
|
* Check if a path should be excluded from hub sync
|
|
@@ -758,8 +824,13 @@ const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp', 'rul
|
|
|
758
824
|
*/
|
|
759
825
|
function shouldExcludeFromHub(relPath) {
|
|
760
826
|
const parts = relPath.split(path.sep);
|
|
827
|
+
const basename = path.basename(relPath);
|
|
761
828
|
// Exclude if any part of the path is in the exclude list
|
|
762
|
-
|
|
829
|
+
if (parts.some(part => HUB_EXCLUDE_DIRS.includes(part))) return true;
|
|
830
|
+
// Exclude if first segment matches first-segment-only list (position-aware)
|
|
831
|
+
if (parts.length > 0 && HUB_EXCLUDE_FIRST_SEGMENT.includes(parts[0])) return true;
|
|
832
|
+
if (HUB_EXCLUDE_FILES.includes(basename)) return true;
|
|
833
|
+
return false;
|
|
763
834
|
}
|
|
764
835
|
|
|
765
836
|
/**
|
|
@@ -784,6 +855,7 @@ function syncAetherToHub(srcDir, destDir) {
|
|
|
784
855
|
const fullPath = path.join(dir, entry.name);
|
|
785
856
|
const relPath = path.relative(base, fullPath);
|
|
786
857
|
|
|
858
|
+
// Exclude non-distributable paths from source collection.
|
|
787
859
|
if (shouldExcludeFromHub(relPath)) continue;
|
|
788
860
|
|
|
789
861
|
if (entry.isDirectory()) {
|
|
@@ -834,7 +906,10 @@ function syncAetherToHub(srcDir, destDir) {
|
|
|
834
906
|
const fullPath = path.join(dir, entry.name);
|
|
835
907
|
const relPath = path.relative(base, fullPath);
|
|
836
908
|
|
|
837
|
-
|
|
909
|
+
// Skip protected directories during cleanup, but allow excluded files
|
|
910
|
+
// (e.g. CONTEXT.md/HANDOFF.md) to be removed from the hub if stale.
|
|
911
|
+
const parts = relPath.split(path.sep);
|
|
912
|
+
if (parts.some(part => HUB_EXCLUDE_DIRS.includes(part))) continue;
|
|
838
913
|
|
|
839
914
|
if (entry.isDirectory()) {
|
|
840
915
|
collectDestFiles(fullPath, base);
|
|
@@ -864,6 +939,70 @@ function syncAetherToHub(srcDir, destDir) {
|
|
|
864
939
|
return { copied, removed, skipped };
|
|
865
940
|
}
|
|
866
941
|
|
|
942
|
+
/**
|
|
943
|
+
* Sync skills from source to hub using manifest-aware strategy.
|
|
944
|
+
* Only syncs skills listed in .manifest.json (Aether-managed).
|
|
945
|
+
* User-created skills (not in manifest) are never modified or deleted.
|
|
946
|
+
*
|
|
947
|
+
* @param {string} skillsSrc - Source skills directory (e.g., .aether/skills)
|
|
948
|
+
* @param {string} hubSkillsDir - Hub skills directory (e.g., ~/.aether/skills)
|
|
949
|
+
* @returns {{ synced: string[], skipped: string[], notices: string[] }}
|
|
950
|
+
*/
|
|
951
|
+
function syncSkillsToHub(skillsSrc, hubSkillsDir) {
|
|
952
|
+
const result = { synced: [], skipped: [], notices: [] };
|
|
953
|
+
|
|
954
|
+
if (!fs.existsSync(skillsSrc)) {
|
|
955
|
+
return result;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
for (const category of ['colony', 'domain']) {
|
|
959
|
+
const srcCat = path.join(skillsSrc, category);
|
|
960
|
+
const hubCat = path.join(hubSkillsDir, category);
|
|
961
|
+
|
|
962
|
+
if (!fs.existsSync(srcCat)) continue;
|
|
963
|
+
fs.mkdirSync(hubCat, { recursive: true });
|
|
964
|
+
|
|
965
|
+
// Copy manifest
|
|
966
|
+
const manifestSrc = path.join(srcCat, '.manifest.json');
|
|
967
|
+
if (fs.existsSync(manifestSrc)) {
|
|
968
|
+
fs.copyFileSync(manifestSrc, path.join(hubCat, '.manifest.json'));
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Read manifest for owned skills list
|
|
972
|
+
let manifest = { skills: [] };
|
|
973
|
+
try {
|
|
974
|
+
manifest = JSON.parse(fs.readFileSync(manifestSrc, 'utf8'));
|
|
975
|
+
} catch (e) { /* no manifest = no managed skills */ }
|
|
976
|
+
|
|
977
|
+
// Sync only managed skills (skip user-created)
|
|
978
|
+
const srcDirs = fs.readdirSync(srcCat, { withFileTypes: true })
|
|
979
|
+
.filter(d => d.isDirectory());
|
|
980
|
+
|
|
981
|
+
for (const dir of srcDirs) {
|
|
982
|
+
if (manifest.skills.includes(dir.name)) {
|
|
983
|
+
// Managed skill — overwrite via syncDirWithCleanup
|
|
984
|
+
const srcSkill = path.join(srcCat, dir.name);
|
|
985
|
+
const hubSkill = path.join(hubCat, dir.name);
|
|
986
|
+
syncDirWithCleanup(srcSkill, hubSkill);
|
|
987
|
+
result.synced.push(`${category}/${dir.name}`);
|
|
988
|
+
} else if (fs.existsSync(path.join(hubCat, dir.name))) {
|
|
989
|
+
// User-created skill exists with same name — skip and log notice
|
|
990
|
+
const notice = `Skipped skill '${dir.name}' — user version exists. Run 'aether skill-diff ${dir.name}' to compare.`;
|
|
991
|
+
result.notices.push(notice);
|
|
992
|
+
result.skipped.push(`${category}/${dir.name}`);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// Copy README if present
|
|
997
|
+
const readmeSrc = path.join(srcCat, 'README.md');
|
|
998
|
+
if (fs.existsSync(readmeSrc)) {
|
|
999
|
+
fs.copyFileSync(readmeSrc, path.join(hubCat, 'README.md'));
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return result;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
867
1006
|
function setupHub() {
|
|
868
1007
|
// Create ~/.aether/ directory structure and populate from package
|
|
869
1008
|
try {
|
|
@@ -882,7 +1021,7 @@ function setupHub() {
|
|
|
882
1021
|
fs.mkdirSync(HUB_SYSTEM_DIR, { recursive: true });
|
|
883
1022
|
|
|
884
1023
|
// Move system files to system/
|
|
885
|
-
const systemFiles = ['aether-utils.sh', 'workers.md'
|
|
1024
|
+
const systemFiles = ['aether-utils.sh', 'workers.md'];
|
|
886
1025
|
const systemDirs = ['docs', 'utils', 'commands', 'agents', 'schemas', 'exchange', 'templates', 'lib'];
|
|
887
1026
|
|
|
888
1027
|
for (const file of systemFiles) {
|
|
@@ -1015,6 +1154,20 @@ function setupHub() {
|
|
|
1015
1154
|
}
|
|
1016
1155
|
}
|
|
1017
1156
|
|
|
1157
|
+
// Sync skills to hub (~/.aether/skills/)
|
|
1158
|
+
// Skills install at hub root (NOT in system/) so users can find and create their own
|
|
1159
|
+
const skillsSrc = path.join(aetherSrc, 'skills');
|
|
1160
|
+
const HUB_SKILLS_DIR = path.join(HUB_DIR, 'skills');
|
|
1161
|
+
if (fs.existsSync(skillsSrc)) {
|
|
1162
|
+
const skillsResult = syncSkillsToHub(skillsSrc, HUB_SKILLS_DIR);
|
|
1163
|
+
if (skillsResult.synced.length > 0) {
|
|
1164
|
+
log(` Hub skills: ${skillsResult.synced.length} managed skills synced -> ${HUB_SKILLS_DIR}`);
|
|
1165
|
+
}
|
|
1166
|
+
for (const notice of skillsResult.notices) {
|
|
1167
|
+
log(` ${notice}`);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1018
1171
|
// Create/preserve registry.json (at root, not in system/)
|
|
1019
1172
|
if (!fs.existsSync(HUB_REGISTRY)) {
|
|
1020
1173
|
writeJsonSync(HUB_REGISTRY, { schema_version: 1, repos: [] });
|
|
@@ -1070,19 +1223,21 @@ async function updateRepo(repoPath, sourceVersion, opts) {
|
|
|
1070
1223
|
// Target directories for git safety checks
|
|
1071
1224
|
const targetDirs = ['.aether', '.claude/commands/ant', '.claude/agents/ant', '.claude/rules', '.opencode/commands/ant', '.opencode/agents'];
|
|
1072
1225
|
|
|
1073
|
-
//
|
|
1226
|
+
// Use UpdateTransaction for two-phase commit with automatic rollback
|
|
1227
|
+
const transaction = new UpdateTransaction(repoPath, { sourceVersion, quiet, force });
|
|
1228
|
+
|
|
1229
|
+
// Git safety: only warn about dirty files the update would actually overwrite
|
|
1074
1230
|
let dirtyFiles = [];
|
|
1075
|
-
if (isGitRepo(repoPath)) {
|
|
1076
|
-
|
|
1077
|
-
|
|
1231
|
+
if (isGitRepo(repoPath) && !force) {
|
|
1232
|
+
const wouldOverwrite = new Set(transaction.getConflictingFiles());
|
|
1233
|
+
const allDirty = getGitDirtyFiles(repoPath, targetDirs);
|
|
1234
|
+
dirtyFiles = allDirty.filter(f => wouldOverwrite.has(f));
|
|
1235
|
+
if (dirtyFiles.length > 0) {
|
|
1078
1236
|
return { status: 'dirty', files: dirtyFiles };
|
|
1079
1237
|
}
|
|
1080
1238
|
// Note: --force handling is now done via checkpoint stash in UpdateTransaction
|
|
1081
1239
|
}
|
|
1082
1240
|
|
|
1083
|
-
// Use UpdateTransaction for two-phase commit with automatic rollback
|
|
1084
|
-
const transaction = new UpdateTransaction(repoPath, { sourceVersion, quiet, force });
|
|
1085
|
-
|
|
1086
1241
|
try {
|
|
1087
1242
|
const result = await transaction.execute(sourceVersion, { dryRun });
|
|
1088
1243
|
|
|
@@ -1199,6 +1354,28 @@ program
|
|
|
1199
1354
|
}
|
|
1200
1355
|
}
|
|
1201
1356
|
|
|
1357
|
+
// Sync OpenCode commands to ~/.opencode/command/ (with orphan cleanup)
|
|
1358
|
+
const opencodeCmdsSrc = path.join(PACKAGE_DIR, '.opencode', 'commands', 'ant');
|
|
1359
|
+
if (fs.existsSync(opencodeCmdsSrc)) {
|
|
1360
|
+
const result = syncDirWithCleanup(opencodeCmdsSrc, OPENCODE_COMMANDS_DEST);
|
|
1361
|
+
log(` Commands (opencode): ${result.copied} files -> ${OPENCODE_COMMANDS_DEST}`);
|
|
1362
|
+
if (result.removed.length > 0) {
|
|
1363
|
+
log(` Commands (opencode): removed ${result.removed.length} stale files`);
|
|
1364
|
+
for (const f of result.removed) log(` - ${f}`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// Sync OpenCode agents to ~/.opencode/agent/ (with orphan cleanup)
|
|
1369
|
+
const opencodeAgentsSrc = path.join(PACKAGE_DIR, '.opencode', 'agents');
|
|
1370
|
+
if (fs.existsSync(opencodeAgentsSrc)) {
|
|
1371
|
+
const result = syncDirWithCleanup(opencodeAgentsSrc, OPENCODE_AGENTS_DEST);
|
|
1372
|
+
log(` Agents (opencode): ${result.copied} files -> ${OPENCODE_AGENTS_DEST}`);
|
|
1373
|
+
if (result.removed.length > 0) {
|
|
1374
|
+
log(` Agents (opencode): removed ${result.removed.length} stale files`);
|
|
1375
|
+
for (const f of result.removed) log(` - ${f}`);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1202
1379
|
// Set up distribution hub at ~/.aether/
|
|
1203
1380
|
log('');
|
|
1204
1381
|
log(c.colony('Setting up distribution hub...'));
|
|
@@ -1207,6 +1384,7 @@ program
|
|
|
1207
1384
|
log('');
|
|
1208
1385
|
log(c.success('Install complete.'));
|
|
1209
1386
|
log(` ${c.queen('Claude Code:')} run /ant to get started`);
|
|
1387
|
+
log(` ${c.colony('OpenCode:')} run /ant to get started`);
|
|
1210
1388
|
log(` ${c.colony('Hub:')} ${c.dim('~/.aether/')} (for coordinated updates across repos)`);
|
|
1211
1389
|
}));
|
|
1212
1390
|
|
|
@@ -1475,7 +1653,7 @@ program
|
|
|
1475
1653
|
// Uninstall command
|
|
1476
1654
|
program
|
|
1477
1655
|
.command('uninstall')
|
|
1478
|
-
.description('Remove slash-commands from ~/.claude/commands/ant/ (preserves project state and hub)')
|
|
1656
|
+
.description('Remove slash-commands from ~/.claude/commands/ant/ and ~/.opencode/ (preserves project state and hub)')
|
|
1479
1657
|
.action(wrapCommand(async () => {
|
|
1480
1658
|
log(c.header(`aether-colony v${VERSION} — uninstalling...`));
|
|
1481
1659
|
|
|
@@ -1487,6 +1665,26 @@ program
|
|
|
1487
1665
|
log(' Claude Code commands already removed.');
|
|
1488
1666
|
}
|
|
1489
1667
|
|
|
1668
|
+
// Remove Claude Code agents
|
|
1669
|
+
if (fs.existsSync(AGENTS_DEST)) {
|
|
1670
|
+
const n = removeDirSync(AGENTS_DEST);
|
|
1671
|
+
log(` Removed: ${n} agent files from ${AGENTS_DEST}`);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// Remove OpenCode commands (only our files, preserve others)
|
|
1675
|
+
const opencodeCmdsSrc = path.join(PACKAGE_DIR, '.opencode', 'commands', 'ant');
|
|
1676
|
+
if (fs.existsSync(OPENCODE_COMMANDS_DEST) && fs.existsSync(opencodeCmdsSrc)) {
|
|
1677
|
+
const n = removeFilesFromSource(opencodeCmdsSrc, OPENCODE_COMMANDS_DEST);
|
|
1678
|
+
log(` Removed: ${n} command files from ${OPENCODE_COMMANDS_DEST}`);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// Remove OpenCode agents (only our files, preserve others)
|
|
1682
|
+
const opencodeAgentsSrc = path.join(PACKAGE_DIR, '.opencode', 'agents');
|
|
1683
|
+
if (fs.existsSync(OPENCODE_AGENTS_DEST) && fs.existsSync(opencodeAgentsSrc)) {
|
|
1684
|
+
const n = removeFilesFromSource(opencodeAgentsSrc, OPENCODE_AGENTS_DEST);
|
|
1685
|
+
log(` Removed: ${n} agent files from ${OPENCODE_AGENTS_DEST}`);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1490
1688
|
log('');
|
|
1491
1689
|
log(c.success('Uninstall complete. Per-project .aether/data/ directories are untouched.'));
|
|
1492
1690
|
log(` ${c.colony('Hub:')} ${c.dim('~/.aether/')} preserved (remove manually if desired).`);
|
|
@@ -1724,261 +1922,6 @@ program
|
|
|
1724
1922
|
}
|
|
1725
1923
|
}));
|
|
1726
1924
|
|
|
1727
|
-
// Caste emoji mapping for display
|
|
1728
|
-
const CASTE_EMOJIS = {
|
|
1729
|
-
builder: '🔨',
|
|
1730
|
-
watcher: '👁️',
|
|
1731
|
-
scout: '🔍',
|
|
1732
|
-
chaos: '🎲',
|
|
1733
|
-
oracle: '🔮',
|
|
1734
|
-
architect: '🏗️',
|
|
1735
|
-
prime: '🏛️',
|
|
1736
|
-
colonizer: '🌱',
|
|
1737
|
-
route_setter: '🧭',
|
|
1738
|
-
archaeologist: '📜',
|
|
1739
|
-
};
|
|
1740
|
-
|
|
1741
|
-
/**
|
|
1742
|
-
* Format context window for display
|
|
1743
|
-
* @param {number} contextWindow - Context window size
|
|
1744
|
-
* @returns {string} Formatted string (e.g., "256K")
|
|
1745
|
-
*/
|
|
1746
|
-
function formatContextWindow(contextWindow) {
|
|
1747
|
-
if (!contextWindow) return '-';
|
|
1748
|
-
if (contextWindow >= 1000) {
|
|
1749
|
-
return `${Math.round(contextWindow / 1000)}K`;
|
|
1750
|
-
}
|
|
1751
|
-
return String(contextWindow);
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
// Caste-models command - Manage caste-to-model assignments
|
|
1755
|
-
const casteModelsCmd = program
|
|
1756
|
-
.command('caste-models')
|
|
1757
|
-
.description('Manage caste-to-model assignments');
|
|
1758
|
-
|
|
1759
|
-
// list subcommand
|
|
1760
|
-
casteModelsCmd
|
|
1761
|
-
.command('list')
|
|
1762
|
-
.description('List current model assignments per caste')
|
|
1763
|
-
.option('--verify', 'Verify model availability on proxy')
|
|
1764
|
-
.action(wrapCommand(async (options) => {
|
|
1765
|
-
const repoPath = process.cwd();
|
|
1766
|
-
const profiles = loadModelProfiles(repoPath);
|
|
1767
|
-
const overrides = getUserOverrides(profiles);
|
|
1768
|
-
const proxyConfig = getProxyConfig(profiles);
|
|
1769
|
-
|
|
1770
|
-
// Check proxy health
|
|
1771
|
-
let proxyHealth = null;
|
|
1772
|
-
let proxyModels = null;
|
|
1773
|
-
if (proxyConfig?.endpoint) {
|
|
1774
|
-
proxyHealth = await checkProxyHealth(proxyConfig.endpoint);
|
|
1775
|
-
if (proxyHealth.healthy && proxyHealth.models) {
|
|
1776
|
-
proxyModels = proxyHealth.models;
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
|
-
console.log(c.header('Caste Model Assignments\n'));
|
|
1781
|
-
|
|
1782
|
-
// Display proxy status
|
|
1783
|
-
if (proxyConfig?.endpoint) {
|
|
1784
|
-
const proxyStatus = formatProxyStatusColored(proxyHealth, c) + c.dim(` @ ${proxyConfig.endpoint}`);
|
|
1785
|
-
console.log(`Proxy: ${proxyStatus}`);
|
|
1786
|
-
if (!proxyHealth?.healthy) {
|
|
1787
|
-
console.log(c.warning('Warning: Using default model (kimi-k2.5) for all castes'));
|
|
1788
|
-
}
|
|
1789
|
-
console.log('');
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
// Table header - add Verify column if --verify flag
|
|
1793
|
-
const verifyFlag = options.verify;
|
|
1794
|
-
const header = verifyFlag
|
|
1795
|
-
? `${'Caste'.padEnd(14)} ${'Model'.padEnd(14)} ${'Provider'.padEnd(10)} ${'Context'.padEnd(8)} Verify Status`
|
|
1796
|
-
: `${'Caste'.padEnd(14)} ${'Model'.padEnd(14)} ${'Provider'.padEnd(10)} ${'Context'.padEnd(8)} Status`;
|
|
1797
|
-
console.log(header);
|
|
1798
|
-
console.log(verifyFlag ? '─'.repeat(70) : '─'.repeat(60));
|
|
1799
|
-
|
|
1800
|
-
// Get all assignments
|
|
1801
|
-
const assignments = getAllAssignments(profiles);
|
|
1802
|
-
|
|
1803
|
-
for (const assignment of assignments) {
|
|
1804
|
-
const emoji = CASTE_EMOJIS[assignment.caste] || '•';
|
|
1805
|
-
const casteName = assignment.caste.charAt(0).toUpperCase() + assignment.caste.slice(1);
|
|
1806
|
-
const casteDisplay = `${emoji} ${casteName}`;
|
|
1807
|
-
|
|
1808
|
-
// Check for override
|
|
1809
|
-
const hasOverride = overrides[assignment.caste] !== undefined;
|
|
1810
|
-
const effectiveModel = getEffectiveModel(profiles, assignment.caste);
|
|
1811
|
-
const modelDisplay = effectiveModel.model + (hasOverride ? ' (override)' : '');
|
|
1812
|
-
|
|
1813
|
-
// Get model metadata
|
|
1814
|
-
const metadata = getModelMetadata(profiles, effectiveModel.model);
|
|
1815
|
-
const provider = metadata?.provider || assignment.provider || '-';
|
|
1816
|
-
const contextWindow = formatContextWindow(metadata?.context_window);
|
|
1817
|
-
|
|
1818
|
-
// Status indicator based on proxy health
|
|
1819
|
-
const status = proxyHealth?.healthy ? '✓' : '⚠';
|
|
1820
|
-
|
|
1821
|
-
// Verify flag - check if model is available on proxy
|
|
1822
|
-
let verifyStatus = '';
|
|
1823
|
-
if (verifyFlag) {
|
|
1824
|
-
if (proxyModels) {
|
|
1825
|
-
const isAvailable = proxyModels.includes(effectiveModel.model);
|
|
1826
|
-
verifyStatus = isAvailable ? '✓' : '✗';
|
|
1827
|
-
} else {
|
|
1828
|
-
verifyStatus = '?';
|
|
1829
|
-
}
|
|
1830
|
-
console.log(
|
|
1831
|
-
`${casteDisplay.padEnd(14)} ${modelDisplay.padEnd(14)} ${provider.padEnd(10)} ${contextWindow.padEnd(8)} ${verifyStatus.padEnd(7)} ${status}`
|
|
1832
|
-
);
|
|
1833
|
-
} else {
|
|
1834
|
-
console.log(
|
|
1835
|
-
`${casteDisplay.padEnd(14)} ${modelDisplay.padEnd(14)} ${provider.padEnd(10)} ${contextWindow.padEnd(8)} ${status}`
|
|
1836
|
-
);
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
// Show overrides summary if any exist
|
|
1841
|
-
const overrideCount = Object.keys(overrides).length;
|
|
1842
|
-
if (overrideCount > 0) {
|
|
1843
|
-
console.log('');
|
|
1844
|
-
console.log(c.info(`Active overrides: ${overrideCount}`));
|
|
1845
|
-
for (const [caste, model] of Object.entries(overrides)) {
|
|
1846
|
-
console.log(` ${caste}: ${model}`);
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
}));
|
|
1850
|
-
|
|
1851
|
-
// set subcommand
|
|
1852
|
-
casteModelsCmd
|
|
1853
|
-
.command('set')
|
|
1854
|
-
.description('Set model override for a caste')
|
|
1855
|
-
.argument('<assignment>', 'caste=model (e.g., builder=glm-5)')
|
|
1856
|
-
.action(wrapCommand(async (assignment) => {
|
|
1857
|
-
// Parse caste=model format
|
|
1858
|
-
const match = assignment.match(/^([^=]+)=(.+)$/);
|
|
1859
|
-
if (!match) {
|
|
1860
|
-
const error = new ValidationError(
|
|
1861
|
-
`Invalid assignment format: '${assignment}'`,
|
|
1862
|
-
{ received: assignment },
|
|
1863
|
-
'Use format: caste=model (e.g., builder=glm-5)'
|
|
1864
|
-
);
|
|
1865
|
-
throw error;
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
const [, caste, model] = match;
|
|
1869
|
-
|
|
1870
|
-
const repoPath = process.cwd();
|
|
1871
|
-
|
|
1872
|
-
// Validate and set
|
|
1873
|
-
try {
|
|
1874
|
-
const result = setModelOverride(repoPath, caste, model);
|
|
1875
|
-
|
|
1876
|
-
if (result.previous) {
|
|
1877
|
-
console.log(c.success(`Updated ${caste}: ${result.previous} → ${model}`));
|
|
1878
|
-
} else {
|
|
1879
|
-
console.log(c.success(`Set ${caste} to ${model}`));
|
|
1880
|
-
}
|
|
1881
|
-
} catch (error) {
|
|
1882
|
-
if (error.name === 'ValidationError') {
|
|
1883
|
-
// Add helpful suggestions
|
|
1884
|
-
if (error.details?.validCastes) {
|
|
1885
|
-
console.error(c.error(`Error: ${error.message}`));
|
|
1886
|
-
console.error('\nValid castes:');
|
|
1887
|
-
for (const casteName of error.details.validCastes) {
|
|
1888
|
-
const emoji = CASTE_EMOJIS[casteName] || '•';
|
|
1889
|
-
console.error(` ${emoji} ${casteName}`);
|
|
1890
|
-
}
|
|
1891
|
-
} else if (error.details?.validModels) {
|
|
1892
|
-
console.error(c.error(`Error: ${error.message}`));
|
|
1893
|
-
console.error('\nValid models:');
|
|
1894
|
-
for (const modelName of error.details.validModels) {
|
|
1895
|
-
console.error(` • ${modelName}`);
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
process.exit(1);
|
|
1899
|
-
}
|
|
1900
|
-
throw error;
|
|
1901
|
-
}
|
|
1902
|
-
}));
|
|
1903
|
-
|
|
1904
|
-
// reset subcommand
|
|
1905
|
-
casteModelsCmd
|
|
1906
|
-
.command('reset')
|
|
1907
|
-
.description('Reset caste to default model (remove override)')
|
|
1908
|
-
.argument('<caste>', 'caste name (e.g., builder)')
|
|
1909
|
-
.action(wrapCommand(async (caste) => {
|
|
1910
|
-
const repoPath = process.cwd();
|
|
1911
|
-
|
|
1912
|
-
try {
|
|
1913
|
-
const result = resetModelOverride(repoPath, caste);
|
|
1914
|
-
|
|
1915
|
-
if (result.hadOverride) {
|
|
1916
|
-
console.log(c.success(`Reset ${caste} to default model`));
|
|
1917
|
-
} else {
|
|
1918
|
-
console.log(c.info(`${caste} was already using default model`));
|
|
1919
|
-
}
|
|
1920
|
-
} catch (error) {
|
|
1921
|
-
if (error.name === 'ValidationError' && error.details?.validCastes) {
|
|
1922
|
-
console.error(c.error(`Error: ${error.message}`));
|
|
1923
|
-
console.error('\nValid castes:');
|
|
1924
|
-
for (const casteName of error.details.validCastes) {
|
|
1925
|
-
const emoji = CASTE_EMOJIS[casteName] || '•';
|
|
1926
|
-
console.error(` ${emoji} ${casteName}`);
|
|
1927
|
-
}
|
|
1928
|
-
process.exit(1);
|
|
1929
|
-
}
|
|
1930
|
-
throw error;
|
|
1931
|
-
}
|
|
1932
|
-
}));
|
|
1933
|
-
|
|
1934
|
-
// Verify-models command - Verify model routing configuration
|
|
1935
|
-
program
|
|
1936
|
-
.command('verify-models')
|
|
1937
|
-
.description('Verify model routing configuration is active')
|
|
1938
|
-
.action(wrapCommand(async () => {
|
|
1939
|
-
const repoPath = process.cwd();
|
|
1940
|
-
const report = await createVerificationReport(repoPath);
|
|
1941
|
-
|
|
1942
|
-
console.log('=== Model Routing Verification ===\n');
|
|
1943
|
-
|
|
1944
|
-
// Proxy status
|
|
1945
|
-
console.log(`LiteLLM Proxy: ${report.proxy.running ? '✓ Running' : '✗ Not running'}`);
|
|
1946
|
-
if (report.proxy.running) {
|
|
1947
|
-
console.log(` Latency: ${report.proxy.latency}ms`);
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
// Environment
|
|
1951
|
-
console.log(`\nEnvironment:`);
|
|
1952
|
-
console.log(` ANTHROPIC_MODEL: ${report.env.model || '(not set)'}`);
|
|
1953
|
-
console.log(` ANTHROPIC_BASE_URL: ${report.env.baseUrl || '(not set)'}`);
|
|
1954
|
-
|
|
1955
|
-
// Caste assignments
|
|
1956
|
-
console.log(`\nCaste Model Assignments:`);
|
|
1957
|
-
for (const [caste, info] of Object.entries(report.castes)) {
|
|
1958
|
-
const status = info.assigned ? '✓' : '✗';
|
|
1959
|
-
console.log(` ${status} ${caste}: ${info.model || 'default'}`);
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
// Model profiles file
|
|
1963
|
-
console.log(`\nModel Profiles File:`);
|
|
1964
|
-
if (report.profilesFile.exists) {
|
|
1965
|
-
console.log(` ✓ Found: ${report.profilesFile.path}`);
|
|
1966
|
-
const profileCount = Object.keys(report.profilesFile.profiles).length;
|
|
1967
|
-
console.log(` Profiles: ${profileCount} castes configured`);
|
|
1968
|
-
} else {
|
|
1969
|
-
console.log(` ✗ Not found: ${report.profilesFile.path}`);
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
// Issues
|
|
1973
|
-
if (report.issues.length > 0) {
|
|
1974
|
-
console.log(`\nIssues Found:`);
|
|
1975
|
-
report.issues.forEach(issue => console.log(` ⚠ ${issue}`));
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
// Recommendation
|
|
1979
|
-
console.log(`\n${report.recommendation}`);
|
|
1980
|
-
}));
|
|
1981
|
-
|
|
1982
1925
|
// Spawn-log command - Log a worker spawn event
|
|
1983
1926
|
program
|
|
1984
1927
|
.command('spawn-log')
|
|
@@ -2108,141 +2051,6 @@ program
|
|
|
2108
2051
|
console.log(formatNestmates(nestmates));
|
|
2109
2052
|
}));
|
|
2110
2053
|
|
|
2111
|
-
// Telemetry command - View model performance telemetry
|
|
2112
|
-
const telemetryCmd = program
|
|
2113
|
-
.command('telemetry')
|
|
2114
|
-
.description('View model performance telemetry')
|
|
2115
|
-
.action(wrapCommand(async () => {
|
|
2116
|
-
// Default action: show summary
|
|
2117
|
-
const repoPath = process.cwd();
|
|
2118
|
-
const summary = getTelemetrySummary(repoPath);
|
|
2119
|
-
|
|
2120
|
-
console.log(c.header('Model Performance Telemetry\n'));
|
|
2121
|
-
console.log(`Total Spawns: ${summary.total_spawns}`);
|
|
2122
|
-
console.log(`Models Used: ${summary.total_models}\n`);
|
|
2123
|
-
|
|
2124
|
-
if (summary.total_spawns === 0) {
|
|
2125
|
-
console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
|
|
2126
|
-
return;
|
|
2127
|
-
}
|
|
2128
|
-
|
|
2129
|
-
console.log('Model Performance:');
|
|
2130
|
-
console.log('─'.repeat(60));
|
|
2131
|
-
for (const [model, stats] of Object.entries(summary.models)) {
|
|
2132
|
-
const rate = (stats.success_rate * 100).toFixed(1);
|
|
2133
|
-
const rateColor = stats.success_rate >= 0.9 ? c.success :
|
|
2134
|
-
stats.success_rate >= 0.7 ? c.warning : c.error;
|
|
2135
|
-
console.log(` ${model.padEnd(15)} ${String(stats.total_spawns).padStart(4)} spawns ${rateColor(rate + '%')} success`);
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
if (summary.recent_decisions.length > 0) {
|
|
2139
|
-
console.log('\nRecent Routing Decisions:');
|
|
2140
|
-
console.log('─'.repeat(60));
|
|
2141
|
-
for (const decision of summary.recent_decisions.slice(-5)) {
|
|
2142
|
-
console.log(` ${decision.caste.padEnd(10)} → ${decision.selected_model.padEnd(12)} (${decision.source})`);
|
|
2143
|
-
}
|
|
2144
|
-
}
|
|
2145
|
-
}));
|
|
2146
|
-
|
|
2147
|
-
// summary subcommand (explicit)
|
|
2148
|
-
telemetryCmd
|
|
2149
|
-
.command('summary')
|
|
2150
|
-
.description('Show overall telemetry summary')
|
|
2151
|
-
.action(wrapCommand(async () => {
|
|
2152
|
-
const repoPath = process.cwd();
|
|
2153
|
-
const summary = getTelemetrySummary(repoPath);
|
|
2154
|
-
|
|
2155
|
-
console.log(c.header('Model Performance Telemetry\n'));
|
|
2156
|
-
console.log(`Total Spawns: ${summary.total_spawns}`);
|
|
2157
|
-
console.log(`Models Used: ${summary.total_models}\n`);
|
|
2158
|
-
|
|
2159
|
-
if (summary.total_spawns === 0) {
|
|
2160
|
-
console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
|
|
2161
|
-
return;
|
|
2162
|
-
}
|
|
2163
|
-
|
|
2164
|
-
console.log('Model Performance:');
|
|
2165
|
-
console.log('─'.repeat(60));
|
|
2166
|
-
for (const [model, stats] of Object.entries(summary.models)) {
|
|
2167
|
-
const rate = (stats.success_rate * 100).toFixed(1);
|
|
2168
|
-
const rateColor = stats.success_rate >= 0.9 ? c.success :
|
|
2169
|
-
stats.success_rate >= 0.7 ? c.warning : c.error;
|
|
2170
|
-
console.log(` ${model.padEnd(15)} ${String(stats.total_spawns).padStart(4)} spawns ${rateColor(rate + '%')} success`);
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
if (summary.recent_decisions.length > 0) {
|
|
2174
|
-
console.log('\nRecent Routing Decisions:');
|
|
2175
|
-
console.log('─'.repeat(60));
|
|
2176
|
-
for (const decision of summary.recent_decisions.slice(-5)) {
|
|
2177
|
-
console.log(` ${decision.caste.padEnd(10)} → ${decision.selected_model.padEnd(12)} (${decision.source})`);
|
|
2178
|
-
}
|
|
2179
|
-
}
|
|
2180
|
-
}));
|
|
2181
|
-
|
|
2182
|
-
// model subcommand
|
|
2183
|
-
telemetryCmd
|
|
2184
|
-
.command('model <model-name>')
|
|
2185
|
-
.description('Show detailed performance for a specific model')
|
|
2186
|
-
.action(wrapCommand(async (modelName) => {
|
|
2187
|
-
const repoPath = process.cwd();
|
|
2188
|
-
const performance = getModelPerformance(repoPath, modelName);
|
|
2189
|
-
|
|
2190
|
-
if (!performance) {
|
|
2191
|
-
console.log(c.warning(`No data for model: ${modelName}`));
|
|
2192
|
-
return;
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
console.log(c.header(`Model Performance: ${modelName}\n`));
|
|
2196
|
-
console.log(`Total Spawns: ${performance.total_spawns}`);
|
|
2197
|
-
console.log(`Success Rate: ${(performance.success_rate * 100).toFixed(1)}%`);
|
|
2198
|
-
console.log(` ✓ Completed: ${performance.successful_completions}`);
|
|
2199
|
-
console.log(` ✗ Failed: ${performance.failed_completions}`);
|
|
2200
|
-
console.log(` 🚫 Blocked: ${performance.blocked}`);
|
|
2201
|
-
|
|
2202
|
-
if (Object.keys(performance.by_caste).length > 0) {
|
|
2203
|
-
console.log('\nPerformance by Caste:');
|
|
2204
|
-
console.log('─'.repeat(50));
|
|
2205
|
-
for (const [caste, stats] of Object.entries(performance.by_caste)) {
|
|
2206
|
-
const casteRate = stats.spawns > 0 ? (stats.success / stats.spawns * 100).toFixed(1) : '0.0';
|
|
2207
|
-
console.log(` ${caste.padEnd(12)} ${String(stats.spawns).padStart(4)} spawns ${casteRate}% success`);
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
}));
|
|
2211
|
-
|
|
2212
|
-
// performance subcommand
|
|
2213
|
-
telemetryCmd
|
|
2214
|
-
.command('performance')
|
|
2215
|
-
.description('Show models ranked by performance')
|
|
2216
|
-
.action(wrapCommand(async () => {
|
|
2217
|
-
const repoPath = process.cwd();
|
|
2218
|
-
const summary = getTelemetrySummary(repoPath);
|
|
2219
|
-
|
|
2220
|
-
console.log(c.header('Model Performance Ranking\n'));
|
|
2221
|
-
|
|
2222
|
-
if (summary.total_spawns === 0) {
|
|
2223
|
-
console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
|
|
2224
|
-
return;
|
|
2225
|
-
}
|
|
2226
|
-
|
|
2227
|
-
// Sort models by success rate
|
|
2228
|
-
const ranked = Object.entries(summary.models)
|
|
2229
|
-
.map(([model, stats]) => ({ model, ...stats }))
|
|
2230
|
-
.sort((a, b) => b.success_rate - a.success_rate);
|
|
2231
|
-
|
|
2232
|
-
console.log(`${'Rank'.padEnd(6)} ${'Model'.padEnd(15)} ${'Spawns'.padStart(6)} ${'Success'.padStart(8)} ${'Rate'.padStart(6)}`);
|
|
2233
|
-
console.log('─'.repeat(60));
|
|
2234
|
-
|
|
2235
|
-
ranked.forEach((m, i) => {
|
|
2236
|
-
const rank = `${i + 1}.`.padEnd(6);
|
|
2237
|
-
const rate = (m.success_rate * 100).toFixed(1);
|
|
2238
|
-
const rateColor = m.success_rate >= 0.9 ? c.success :
|
|
2239
|
-
m.success_rate >= 0.7 ? c.warning : c.error;
|
|
2240
|
-
console.log(`${rank} ${m.model.padEnd(15)} ${String(m.total_spawns).padStart(6)} ${String(m.successful_completions || 0).padStart(8)} ${rateColor(rate.padStart(5) + '%')}`);
|
|
2241
|
-
});
|
|
2242
|
-
|
|
2243
|
-
console.log('\n' + c.dim('Tip: Use "aether telemetry model <name>" for detailed stats'));
|
|
2244
|
-
}));
|
|
2245
|
-
|
|
2246
2054
|
// Context command - Show auto-loaded context
|
|
2247
2055
|
program
|
|
2248
2056
|
.command('context')
|
|
@@ -2313,8 +2121,7 @@ program
|
|
|
2313
2121
|
console.log('Next steps:');
|
|
2314
2122
|
console.log(' 1. Define your colony goal in .aether/data/COLONY_STATE.json');
|
|
2315
2123
|
console.log(' 2. Run: aether sync-state');
|
|
2316
|
-
console.log(' 3.
|
|
2317
|
-
console.log(' 4. Start building: /ant:init');
|
|
2124
|
+
console.log(' 3. Start building: /ant:init');
|
|
2318
2125
|
}
|
|
2319
2126
|
}));
|
|
2320
2127
|
|
|
@@ -2326,7 +2133,6 @@ program.on('--help', () => {
|
|
|
2326
2133
|
console.log(' install Install slash-commands and set up distribution hub');
|
|
2327
2134
|
console.log(' update Update current repo from hub');
|
|
2328
2135
|
console.log(' sync-state Synchronize COLONY_STATE.json with .planning/STATE.md');
|
|
2329
|
-
console.log(' verify-models Verify model routing configuration');
|
|
2330
2136
|
console.log(' version Show installed version');
|
|
2331
2137
|
console.log(' uninstall Remove slash-commands (preserves project state and hub)');
|
|
2332
2138
|
console.log('');
|
|
@@ -2364,6 +2170,7 @@ module.exports = {
|
|
|
2364
2170
|
saveCheckpointMetadata,
|
|
2365
2171
|
isUserData,
|
|
2366
2172
|
syncDirWithCleanup,
|
|
2173
|
+
syncSkillsToHub,
|
|
2367
2174
|
listFilesRecursive,
|
|
2368
2175
|
cleanEmptyDirs
|
|
2369
2176
|
};
|