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
|
@@ -172,10 +172,35 @@ class UpdateTransaction {
|
|
|
172
172
|
|
|
173
173
|
// Directories to exclude from sync (user data, local state, and separately-synced dirs)
|
|
174
174
|
// v4.0: archive and chambers added — these are private and must not sync to target repos
|
|
175
|
-
|
|
175
|
+
// v6.0: oracle, midden, exchange added for complete user data protection
|
|
176
|
+
this.EXCLUDE_DIRS = ['data', 'dreams', 'oracle', 'midden', 'checkpoints', 'locks', 'temp', 'agents', 'commands', 'rules', 'archive', 'chambers', 'exchange'];
|
|
177
|
+
|
|
178
|
+
// Files to exclude from sync (user wisdom, protected files)
|
|
179
|
+
this.EXCLUDE_FILES = ['QUEEN.md'];
|
|
176
180
|
|
|
177
181
|
// Target directories for git safety checks
|
|
178
182
|
this.targetDirs = ['.aether', '.claude/commands/ant', '.claude/agents/ant', '.claude/rules', '.opencode/commands/ant', '.opencode/agents'];
|
|
183
|
+
|
|
184
|
+
// Managed paths for update safety checks (must align with files update can actually modify)
|
|
185
|
+
this.protectedAetherDirs = new Set([
|
|
186
|
+
'data',
|
|
187
|
+
'dreams',
|
|
188
|
+
'oracle',
|
|
189
|
+
'midden',
|
|
190
|
+
'checkpoints',
|
|
191
|
+
'locks',
|
|
192
|
+
'temp',
|
|
193
|
+
'archive',
|
|
194
|
+
'chambers',
|
|
195
|
+
'exchange',
|
|
196
|
+
]);
|
|
197
|
+
this.managedPrefixes = [
|
|
198
|
+
'.claude/commands/ant',
|
|
199
|
+
'.claude/agents/ant',
|
|
200
|
+
'.claude/rules',
|
|
201
|
+
'.opencode/commands/ant',
|
|
202
|
+
'.opencode/agents',
|
|
203
|
+
];
|
|
179
204
|
}
|
|
180
205
|
|
|
181
206
|
/**
|
|
@@ -210,7 +235,22 @@ class UpdateTransaction {
|
|
|
210
235
|
*/
|
|
211
236
|
writeJsonSync(filePath, data) {
|
|
212
237
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
213
|
-
|
|
238
|
+
const content = JSON.stringify(data, null, 2) + '\n';
|
|
239
|
+
const tmpPath = filePath + '.tmp';
|
|
240
|
+
try {
|
|
241
|
+
fs.writeFileSync(tmpPath, content);
|
|
242
|
+
fs.renameSync(tmpPath, filePath);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
// Clean up temp file on failure
|
|
245
|
+
try {
|
|
246
|
+
if (fs.existsSync(tmpPath)) {
|
|
247
|
+
fs.unlinkSync(tmpPath);
|
|
248
|
+
}
|
|
249
|
+
} catch {
|
|
250
|
+
// Ignore cleanup errors
|
|
251
|
+
}
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
214
254
|
}
|
|
215
255
|
|
|
216
256
|
/**
|
|
@@ -228,6 +268,40 @@ class UpdateTransaction {
|
|
|
228
268
|
}
|
|
229
269
|
}
|
|
230
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Copy file atomically using temp file + rename pattern
|
|
273
|
+
* Prevents partial file corruption on process interruption
|
|
274
|
+
* @param {string} srcPath - Source file path
|
|
275
|
+
* @param {string} destPath - Destination file path
|
|
276
|
+
* @private
|
|
277
|
+
*/
|
|
278
|
+
copyFileAtomic(srcPath, destPath) {
|
|
279
|
+
const tempPath = `${destPath}.tmp.${process.pid}.${Date.now()}`;
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
// Copy to temp file first
|
|
283
|
+
fs.copyFileSync(srcPath, tempPath);
|
|
284
|
+
|
|
285
|
+
// Atomic rename (POSIX guarantees atomicity)
|
|
286
|
+
fs.renameSync(tempPath, destPath);
|
|
287
|
+
|
|
288
|
+
// Set executable permission for shell scripts
|
|
289
|
+
if (destPath.endsWith('.sh')) {
|
|
290
|
+
fs.chmodSync(destPath, 0o755);
|
|
291
|
+
}
|
|
292
|
+
} catch (err) {
|
|
293
|
+
// Clean up temp file on failure
|
|
294
|
+
try {
|
|
295
|
+
if (fs.existsSync(tempPath)) {
|
|
296
|
+
fs.unlinkSync(tempPath);
|
|
297
|
+
}
|
|
298
|
+
} catch {
|
|
299
|
+
// Ignore cleanup errors
|
|
300
|
+
}
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
231
305
|
/**
|
|
232
306
|
* Check if path is a git repository
|
|
233
307
|
* @returns {boolean} True if git repo
|
|
@@ -242,6 +316,54 @@ class UpdateTransaction {
|
|
|
242
316
|
}
|
|
243
317
|
}
|
|
244
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Normalize git porcelain path output.
|
|
321
|
+
* Handles rename syntax and quoted/escaped paths.
|
|
322
|
+
* @param {string} filePath - Raw path fragment from git porcelain
|
|
323
|
+
* @returns {string} Normalized path
|
|
324
|
+
* @private
|
|
325
|
+
*/
|
|
326
|
+
normalizePorcelainPath(filePath) {
|
|
327
|
+
let normalized = filePath;
|
|
328
|
+
|
|
329
|
+
if (normalized.includes(' -> ')) {
|
|
330
|
+
normalized = normalized.split(' -> ').pop();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (normalized.startsWith('"') && normalized.endsWith('"')) {
|
|
334
|
+
normalized = normalized
|
|
335
|
+
.slice(1, -1)
|
|
336
|
+
.replace(/\\"/g, '"')
|
|
337
|
+
.replace(/\\\\/g, '\\');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return normalized;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* True when a path is managed by update and may be overwritten by sync.
|
|
345
|
+
* Paths under .aether/data and other protected runtime dirs are intentionally excluded.
|
|
346
|
+
* @param {string} filePath - Normalized or raw path
|
|
347
|
+
* @returns {boolean} Whether update should treat this path as managed
|
|
348
|
+
* @private
|
|
349
|
+
*/
|
|
350
|
+
isManagedUpdatePath(filePath) {
|
|
351
|
+
const p = this.normalizePorcelainPath(filePath);
|
|
352
|
+
|
|
353
|
+
if (p === '.aether' || p.startsWith('.aether/')) {
|
|
354
|
+
const rel = p === '.aether' ? '' : p.slice('.aether/'.length);
|
|
355
|
+
if (!rel) return true;
|
|
356
|
+
|
|
357
|
+
const first = rel.split('/')[0];
|
|
358
|
+
if (!first || first.startsWith('.')) return false;
|
|
359
|
+
if (this.protectedAetherDirs.has(first)) return false;
|
|
360
|
+
if (rel === 'QUEEN.md') return false;
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return this.managedPrefixes.some(prefix => p === prefix || p.startsWith(`${prefix}/`));
|
|
365
|
+
}
|
|
366
|
+
|
|
245
367
|
/**
|
|
246
368
|
* Detect dirty repository state with detailed categorization
|
|
247
369
|
* @returns {object} Dirty state info: { isDirty, tracked, untracked, staged }
|
|
@@ -257,14 +379,25 @@ class UpdateTransaction {
|
|
|
257
379
|
encoding: 'utf8',
|
|
258
380
|
});
|
|
259
381
|
|
|
260
|
-
|
|
382
|
+
// Preserve the two-column porcelain status prefix on each line.
|
|
383
|
+
// Using `trim()` on the whole output would strip leading spaces from
|
|
384
|
+
// the first line and can misclassify unstaged vs staged changes.
|
|
385
|
+
const lines = result.split('\n').filter(line => line.trim().length > 0);
|
|
261
386
|
const tracked = [];
|
|
262
387
|
const untracked = [];
|
|
263
388
|
const staged = [];
|
|
389
|
+
let managedCount = 0;
|
|
264
390
|
|
|
265
391
|
for (const line of lines) {
|
|
266
392
|
const status = line.slice(0, 2);
|
|
267
|
-
const
|
|
393
|
+
const rawPath = line.slice(3);
|
|
394
|
+
const filePath = this.normalizePorcelainPath(rawPath);
|
|
395
|
+
|
|
396
|
+
if (!this.isManagedUpdatePath(filePath)) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
managedCount++;
|
|
268
401
|
|
|
269
402
|
// Staged changes (in index)
|
|
270
403
|
if (status[0] !== ' ' && status[0] !== '?') {
|
|
@@ -281,7 +414,7 @@ class UpdateTransaction {
|
|
|
281
414
|
}
|
|
282
415
|
|
|
283
416
|
return {
|
|
284
|
-
isDirty:
|
|
417
|
+
isDirty: managedCount > 0,
|
|
285
418
|
tracked,
|
|
286
419
|
untracked,
|
|
287
420
|
staged,
|
|
@@ -580,10 +713,7 @@ class UpdateTransaction {
|
|
|
580
713
|
}
|
|
581
714
|
|
|
582
715
|
if (shouldCopy) {
|
|
583
|
-
|
|
584
|
-
if (relPath.endsWith('.sh')) {
|
|
585
|
-
fs.chmodSync(destPath, 0o755);
|
|
586
|
-
}
|
|
716
|
+
this.copyFileAtomic(srcPath, destPath);
|
|
587
717
|
copied++;
|
|
588
718
|
}
|
|
589
719
|
} catch (err) {
|
|
@@ -653,7 +783,52 @@ class UpdateTransaction {
|
|
|
653
783
|
*/
|
|
654
784
|
shouldExclude(relPath) {
|
|
655
785
|
const parts = relPath.split(path.sep);
|
|
656
|
-
|
|
786
|
+
// Check for excluded directories
|
|
787
|
+
if (parts.some(part => this.EXCLUDE_DIRS.includes(part))) {
|
|
788
|
+
return true;
|
|
789
|
+
}
|
|
790
|
+
// Check for excluded files (QUEEN.md protection)
|
|
791
|
+
const basename = path.basename(relPath);
|
|
792
|
+
if (this.EXCLUDE_FILES.includes(basename)) {
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Get files that the update would actually overwrite (hub version differs from repo version).
|
|
800
|
+
* Used by the dirty-file check to only warn about files that matter.
|
|
801
|
+
* @returns {string[]} Repo-relative paths of files that would be overwritten
|
|
802
|
+
*/
|
|
803
|
+
getConflictingFiles() {
|
|
804
|
+
const conflicts = [];
|
|
805
|
+
|
|
806
|
+
// Helper: check a hub->repo dir pair for differing files
|
|
807
|
+
const checkDir = (hubDir, repoDir, prefix) => {
|
|
808
|
+
if (!fs.existsSync(hubDir)) return;
|
|
809
|
+
const srcFiles = this.listFilesRecursive(hubDir);
|
|
810
|
+
for (const relPath of srcFiles) {
|
|
811
|
+
if (this.shouldExclude(relPath)) continue;
|
|
812
|
+
const destPath = path.join(repoDir, relPath);
|
|
813
|
+
if (fs.existsSync(destPath)) {
|
|
814
|
+
const srcHash = this.hashFileSync(path.join(hubDir, relPath));
|
|
815
|
+
const destHash = this.hashFileSync(destPath);
|
|
816
|
+
if (srcHash !== destHash) {
|
|
817
|
+
conflicts.push(`${prefix}${relPath}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
const repoAether = path.join(this.repoPath, '.aether');
|
|
824
|
+
checkDir(this.HUB_SYSTEM_DIR, repoAether, '.aether/');
|
|
825
|
+
checkDir(this.HUB_COMMANDS_CLAUDE, path.join(this.repoPath, '.claude', 'commands', 'ant'), '.claude/commands/ant/');
|
|
826
|
+
checkDir(this.HUB_COMMANDS_OPENCODE, path.join(this.repoPath, '.opencode', 'commands', 'ant'), '.opencode/commands/ant/');
|
|
827
|
+
checkDir(this.HUB_AGENTS, path.join(this.repoPath, '.opencode', 'agents'), '.opencode/agents/');
|
|
828
|
+
checkDir(this.HUB_AGENTS_CLAUDE, path.join(this.repoPath, '.claude', 'agents', 'ant'), '.claude/agents/ant/');
|
|
829
|
+
checkDir(this.HUB_RULES, path.join(this.repoPath, '.claude', 'rules'), '.claude/rules/');
|
|
830
|
+
|
|
831
|
+
return conflicts;
|
|
657
832
|
}
|
|
658
833
|
|
|
659
834
|
/**
|
|
@@ -699,28 +874,30 @@ class UpdateTransaction {
|
|
|
699
874
|
const srcPath = path.join(srcDir, relPath);
|
|
700
875
|
const destPath = path.join(destDir, relPath);
|
|
701
876
|
|
|
877
|
+
// Hash comparison: determine if copy is needed (runs for both dry-run and actual)
|
|
878
|
+
let shouldCopy = true;
|
|
879
|
+
if (fs.existsSync(destPath)) {
|
|
880
|
+
const srcHash = this.hashFileSync(srcPath);
|
|
881
|
+
const destHash = this.hashFileSync(destPath);
|
|
882
|
+
if (srcHash === destHash) {
|
|
883
|
+
shouldCopy = false;
|
|
884
|
+
skipped++;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
702
888
|
if (!dryRun) {
|
|
703
889
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
704
890
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const srcHash = this.hashFileSync(srcPath);
|
|
709
|
-
const destHash = this.hashFileSync(destPath);
|
|
710
|
-
if (srcHash === destHash) {
|
|
711
|
-
shouldCopy = false;
|
|
712
|
-
skipped++;
|
|
713
|
-
}
|
|
891
|
+
if (shouldCopy) {
|
|
892
|
+
this.copyFileAtomic(srcPath, destPath);
|
|
893
|
+
copied++; // FIX: Only increment when actually copied
|
|
714
894
|
}
|
|
715
|
-
|
|
895
|
+
} else {
|
|
896
|
+
// dry-run: count files that WOULD be copied
|
|
716
897
|
if (shouldCopy) {
|
|
717
|
-
|
|
718
|
-
if (relPath.endsWith('.sh')) {
|
|
719
|
-
fs.chmodSync(destPath, 0o755);
|
|
720
|
-
}
|
|
898
|
+
copied++; // Count only files that would actually be copied
|
|
721
899
|
}
|
|
722
900
|
}
|
|
723
|
-
copied++;
|
|
724
901
|
}
|
|
725
902
|
|
|
726
903
|
// Cleanup: remove files in dest that aren't in source
|
|
@@ -766,18 +943,75 @@ class UpdateTransaction {
|
|
|
766
943
|
return { copied, removed, skipped };
|
|
767
944
|
}
|
|
768
945
|
|
|
946
|
+
/**
|
|
947
|
+
* Move a file or directory to trash instead of deleting
|
|
948
|
+
* @param {string} itemPath - Path to file or directory
|
|
949
|
+
* @param {string} repoPath - Repository root for trash location
|
|
950
|
+
* @returns {boolean} True if moved to trash successfully
|
|
951
|
+
* @private
|
|
952
|
+
*/
|
|
953
|
+
moveToTrash(itemPath, repoPath) {
|
|
954
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
955
|
+
const trashDir = path.join(repoPath, '.aether', '.trash', timestamp);
|
|
956
|
+
const basename = path.basename(itemPath);
|
|
957
|
+
const trashPath = path.join(trashDir, basename);
|
|
958
|
+
|
|
959
|
+
try {
|
|
960
|
+
fs.mkdirSync(trashDir, { recursive: true });
|
|
961
|
+
fs.renameSync(itemPath, trashPath);
|
|
962
|
+
return true;
|
|
963
|
+
} catch (err) {
|
|
964
|
+
// If rename fails (cross-device), fall back to copy + delete
|
|
965
|
+
try {
|
|
966
|
+
fs.mkdirSync(trashDir, { recursive: true });
|
|
967
|
+
if (fs.statSync(itemPath).isDirectory()) {
|
|
968
|
+
fs.cpSync(itemPath, path.join(trashDir, basename), { recursive: true });
|
|
969
|
+
fs.rmSync(itemPath, { recursive: true, force: true });
|
|
970
|
+
} else {
|
|
971
|
+
fs.copyFileSync(itemPath, path.join(trashDir, basename));
|
|
972
|
+
fs.unlinkSync(itemPath);
|
|
973
|
+
}
|
|
974
|
+
return true;
|
|
975
|
+
} catch (fallbackErr) {
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
769
981
|
/**
|
|
770
982
|
* Remove known stale directories and files left behind by pre-3.0.0 versions.
|
|
771
|
-
*
|
|
772
|
-
*
|
|
773
|
-
*
|
|
983
|
+
*
|
|
984
|
+
* IMPORTANT: Removed items are moved to .aether/.trash/ NOT deleted.
|
|
985
|
+
* Users can inspect and manually clean trash when ready.
|
|
986
|
+
* Trash folders are timestamped for easy identification.
|
|
987
|
+
*
|
|
988
|
+
* Protected paths (never cleaned):
|
|
989
|
+
* - .aether/data/ - Colony state
|
|
990
|
+
* - .aether/dreams/ - Session notes
|
|
991
|
+
* - .aether/oracle/ - Research progress
|
|
992
|
+
* - .aether/midden/ - Failure tracking
|
|
993
|
+
* - .aether/QUEEN.md - User's wisdom file
|
|
774
994
|
*
|
|
775
995
|
* Idempotent: safe to call when items do not exist.
|
|
776
996
|
*
|
|
777
997
|
* @param {string} repoPath - Absolute path to the target repository root
|
|
778
|
-
* @returns {{ cleaned: string[], failed: Array<{label: string, error: string}
|
|
998
|
+
* @returns {{ cleaned: string[], failed: Array<{label: string, error: string}>, trashDir: string }}
|
|
779
999
|
*/
|
|
780
1000
|
cleanupStaleAetherDirs(repoPath) {
|
|
1001
|
+
// Safety check: never clean protected directories
|
|
1002
|
+
const protectedDirs = ['data', 'dreams', 'oracle', 'midden'];
|
|
1003
|
+
const protectedFiles = ['QUEEN.md'];
|
|
1004
|
+
|
|
1005
|
+
// Verify none of our stale items are in protected paths
|
|
1006
|
+
const aetherDir = path.join(repoPath, '.aether');
|
|
1007
|
+
for (const dir of protectedDirs) {
|
|
1008
|
+
const protectedPath = path.join(aetherDir, dir);
|
|
1009
|
+
if (fs.existsSync(protectedPath)) {
|
|
1010
|
+
// Log that protected dir exists and will be preserved
|
|
1011
|
+
this.log?.(` Preserving protected directory: .aether/${dir}/`);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
781
1015
|
const staleItems = [
|
|
782
1016
|
{
|
|
783
1017
|
path: path.join(repoPath, '.aether', 'agents'),
|
|
@@ -806,18 +1040,17 @@ class UpdateTransaction {
|
|
|
806
1040
|
}
|
|
807
1041
|
|
|
808
1042
|
try {
|
|
809
|
-
if (item.
|
|
810
|
-
|
|
1043
|
+
if (this.moveToTrash(item.path, repoPath)) {
|
|
1044
|
+
cleaned.push(item.label);
|
|
811
1045
|
} else {
|
|
812
|
-
|
|
1046
|
+
failed.push({ label: item.label, error: 'Failed to move to trash' });
|
|
813
1047
|
}
|
|
814
|
-
cleaned.push(item.label);
|
|
815
1048
|
} catch (err) {
|
|
816
1049
|
failed.push({ label: item.label, error: err.message });
|
|
817
1050
|
}
|
|
818
1051
|
}
|
|
819
1052
|
|
|
820
|
-
return { cleaned, failed };
|
|
1053
|
+
return { cleaned, failed, trashDir: path.join(repoPath, '.aether', '.trash') };
|
|
821
1054
|
}
|
|
822
1055
|
|
|
823
1056
|
/**
|
|
@@ -1250,6 +1483,23 @@ class UpdateTransaction {
|
|
|
1250
1483
|
return false;
|
|
1251
1484
|
}
|
|
1252
1485
|
|
|
1486
|
+
// Restore backed-up managed files before stash pop
|
|
1487
|
+
// Stash only contains the user's local modifications; managed files
|
|
1488
|
+
// overwritten by syncFiles must be restored from the checkpoint backup.
|
|
1489
|
+
if (this.checkpoint.backedUpFiles && this.checkpoint.backedUpFiles.length > 0) {
|
|
1490
|
+
for (const entry of this.checkpoint.backedUpFiles) {
|
|
1491
|
+
try {
|
|
1492
|
+
if (fs.existsSync(entry.backupPath)) {
|
|
1493
|
+
fs.mkdirSync(path.dirname(path.join(this.repoPath, entry.relPath)), { recursive: true });
|
|
1494
|
+
fs.copyFileSync(entry.backupPath, path.join(this.repoPath, entry.relPath));
|
|
1495
|
+
this.log(` Restored managed file: ${entry.relPath}`);
|
|
1496
|
+
}
|
|
1497
|
+
} catch (err) {
|
|
1498
|
+
this.log(` Warning: could not restore ${entry.relPath}: ${err.message}`);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1253
1503
|
// Restore from stash if available
|
|
1254
1504
|
if (this.checkpoint.stashRef) {
|
|
1255
1505
|
try {
|
|
@@ -1451,4 +1701,4 @@ module.exports = {
|
|
|
1451
1701
|
UpdateError,
|
|
1452
1702
|
UpdateErrorCodes,
|
|
1453
1703
|
TransactionStates,
|
|
1454
|
-
};
|
|
1704
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* npx-install.js — Professional installer for Aether Colony
|
|
5
|
+
*
|
|
6
|
+
* Usage: npx aether-colony install
|
|
7
|
+
*
|
|
8
|
+
* Creates the global hub at ~/.aether/ with all system files,
|
|
9
|
+
* slash commands, and agent definitions.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
|
|
16
|
+
const BANNER = `
|
|
17
|
+
█████╗ ███████╗████████╗██╗ ██╗███████╗██████╗
|
|
18
|
+
██╔══██╗██╔════╝╚══██╔══╝██║ ██║██╔════╝██╔══██╗
|
|
19
|
+
███████║█████╗ ██║ ███████║█████╗ ██████╔╝
|
|
20
|
+
██╔══██║██╔══╝ ██║ ██╔══██║██╔══╝ ██╔══██╗
|
|
21
|
+
██║ ██║███████╗ ██║ ██║ ██║███████╗██║ ██║
|
|
22
|
+
╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const AETHER_VERSION = require('../package.json').version;
|
|
26
|
+
const HOME_DIR = os.homedir();
|
|
27
|
+
const HUB_DIR = path.join(HOME_DIR, '.aether');
|
|
28
|
+
const CLAUDE_COMMANDS_DIR = path.join(HOME_DIR, '.claude', 'commands', 'ant');
|
|
29
|
+
const CLAUDE_AGENTS_DIR = path.join(HOME_DIR, '.claude', 'agents', 'ant');
|
|
30
|
+
|
|
31
|
+
// Get the package root (where this script is located)
|
|
32
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
33
|
+
const AETHER_SRC = path.join(PACKAGE_ROOT, '.aether');
|
|
34
|
+
const CLAUDE_COMMANDS_SRC = path.join(PACKAGE_ROOT, '.claude', 'commands', 'ant');
|
|
35
|
+
const OPENCODE_AGENTS_SRC = path.join(PACKAGE_ROOT, '.opencode', 'agents');
|
|
36
|
+
const OPENCODE_COMMANDS_SRC = path.join(PACKAGE_ROOT, '.opencode', 'commands', 'ant');
|
|
37
|
+
|
|
38
|
+
function log(message, type = 'info') {
|
|
39
|
+
const icons = {
|
|
40
|
+
info: 'ℹ',
|
|
41
|
+
success: '✓',
|
|
42
|
+
warning: '⚠',
|
|
43
|
+
error: '✗',
|
|
44
|
+
ant: '🐜'
|
|
45
|
+
};
|
|
46
|
+
console.log(`${icons[type] || '•'} ${message}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ensureDir(dir) {
|
|
50
|
+
if (!fs.existsSync(dir)) {
|
|
51
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function copyDir(src, dest, options = {}) {
|
|
58
|
+
const { exclude = [] } = options;
|
|
59
|
+
ensureDir(dest);
|
|
60
|
+
|
|
61
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
62
|
+
let copied = 0;
|
|
63
|
+
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
const srcPath = path.join(src, entry.name);
|
|
66
|
+
const destPath = path.join(dest, entry.name);
|
|
67
|
+
|
|
68
|
+
// Skip excluded directories
|
|
69
|
+
if (exclude.includes(entry.name)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (entry.isDirectory()) {
|
|
74
|
+
copied += copyDir(srcPath, destPath, options);
|
|
75
|
+
} else {
|
|
76
|
+
fs.copyFileSync(srcPath, destPath);
|
|
77
|
+
copied++;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return copied;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function copyFile(src, dest) {
|
|
85
|
+
const destDir = path.dirname(dest);
|
|
86
|
+
ensureDir(destDir);
|
|
87
|
+
fs.copyFileSync(src, dest);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function install() {
|
|
91
|
+
console.log(BANNER);
|
|
92
|
+
console.log('\n');
|
|
93
|
+
|
|
94
|
+
let filesCopied = 0;
|
|
95
|
+
|
|
96
|
+
// Step 1: Create hub directory structure
|
|
97
|
+
log('Creating hub directory structure...', 'ant');
|
|
98
|
+
const hubDirs = [
|
|
99
|
+
path.join(HUB_DIR, 'system'),
|
|
100
|
+
path.join(HUB_DIR, 'system', 'docs'),
|
|
101
|
+
path.join(HUB_DIR, 'system', 'utils'),
|
|
102
|
+
path.join(HUB_DIR, 'system', 'templates'),
|
|
103
|
+
path.join(HUB_DIR, 'system', 'schemas'),
|
|
104
|
+
path.join(HUB_DIR, 'system', 'exchange'),
|
|
105
|
+
path.join(HUB_DIR, 'system', 'rules'),
|
|
106
|
+
path.join(HUB_DIR, 'data'),
|
|
107
|
+
path.join(HUB_DIR, 'chambers')
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
for (const dir of hubDirs) {
|
|
111
|
+
if (ensureDir(dir)) {
|
|
112
|
+
log(` Created ${path.relative(HOME_DIR, dir)}`, 'info');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Step 2: Copy system files from .aether/
|
|
117
|
+
log('Copying system files to hub...', 'ant');
|
|
118
|
+
if (fs.existsSync(AETHER_SRC)) {
|
|
119
|
+
// Private directories to exclude
|
|
120
|
+
const excludeDirs = ['data', 'dreams', 'oracle', 'checkpoints', 'locks', 'temp', 'archive', 'chambers'];
|
|
121
|
+
filesCopied += copyDir(AETHER_SRC, path.join(HUB_DIR, 'system'), { exclude: excludeDirs });
|
|
122
|
+
log(` Copied ${filesCopied} files from .aether/`, 'success');
|
|
123
|
+
} else {
|
|
124
|
+
log(' Warning: .aether/ source not found', 'warning');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Step 3: Copy Claude Code commands
|
|
128
|
+
log('Installing Claude Code commands...', 'ant');
|
|
129
|
+
if (fs.existsSync(CLAUDE_COMMANDS_SRC)) {
|
|
130
|
+
const cmdCount = copyDir(CLAUDE_COMMANDS_SRC, CLAUDE_COMMANDS_DIR);
|
|
131
|
+
log(` Installed ${cmdCount} slash commands to ~/.claude/commands/ant/`, 'success');
|
|
132
|
+
filesCopied += cmdCount;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Step 4: Copy Claude Code agents (from OpenCode agents)
|
|
136
|
+
log('Installing Claude Code agents...', 'ant');
|
|
137
|
+
if (fs.existsSync(OPENCODE_AGENTS_SRC)) {
|
|
138
|
+
const agentCount = copyDir(OPENCODE_AGENTS_SRC, CLAUDE_AGENTS_DIR);
|
|
139
|
+
log(` Installed ${agentCount} agents to ~/.claude/agents/ant/`, 'success');
|
|
140
|
+
filesCopied += agentCount;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Step 5: Write version file
|
|
144
|
+
const versionData = {
|
|
145
|
+
version: AETHER_VERSION,
|
|
146
|
+
installed_at: new Date().toISOString(),
|
|
147
|
+
package_root: PACKAGE_ROOT
|
|
148
|
+
};
|
|
149
|
+
fs.writeFileSync(
|
|
150
|
+
path.join(HUB_DIR, 'version.json'),
|
|
151
|
+
JSON.stringify(versionData, null, 2)
|
|
152
|
+
);
|
|
153
|
+
log(' Version info written', 'success');
|
|
154
|
+
|
|
155
|
+
// Step 6: Create global QUEEN.md if missing
|
|
156
|
+
const globalQueen = path.join(HUB_DIR, 'QUEEN.md');
|
|
157
|
+
if (!fs.existsSync(globalQueen)) {
|
|
158
|
+
const queenTemplate = path.join(HUB_DIR, 'system', 'templates', 'QUEEN.md.template');
|
|
159
|
+
if (fs.existsSync(queenTemplate)) {
|
|
160
|
+
let content = fs.readFileSync(queenTemplate, 'utf8');
|
|
161
|
+
content = content.replace(/{TIMESTAMP}/g, new Date().toISOString());
|
|
162
|
+
fs.writeFileSync(globalQueen, content);
|
|
163
|
+
log(' Created global QUEEN.md', 'success');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Summary
|
|
168
|
+
console.log('\n ─────────────────────────────────────────────\n');
|
|
169
|
+
log(`Installation complete! ${filesCopied} files installed.`, 'success');
|
|
170
|
+
console.log('\n Next steps:\n');
|
|
171
|
+
console.log(' 1. Run /ant:init "your goal" in any project');
|
|
172
|
+
console.log(' 2. Use /ant:build to execute phases');
|
|
173
|
+
console.log(' 3. Run /ant:help for command reference\n');
|
|
174
|
+
console.log(' ─────────────────────────────────────────────\n');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Run installer
|
|
178
|
+
install();
|