aiox-core 5.0.7 → 5.0.8
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/.aiox-core/cli/commands/pro/buyer.js +379 -0
- package/.aiox-core/cli/commands/pro/index.js +191 -52
- package/.aiox-core/cli/commands/validate/index.js +2 -0
- package/.aiox-core/core/code-intel/helpers/dev-helper.js +1 -1
- package/.aiox-core/core/code-intel/helpers/devops-helper.js +0 -1
- package/.aiox-core/core/code-intel/helpers/planning-helper.js +1 -1
- package/.aiox-core/core/code-intel/helpers/qa-helper.js +2 -2
- package/.aiox-core/core/config/schemas/framework-config.schema.json +1 -0
- package/.aiox-core/core/config/template-overrides.js +1 -1
- package/.aiox-core/core/doctor/checks/ide-sync.js +81 -25
- package/.aiox-core/core/doctor/checks/rules-files.js +0 -1
- package/.aiox-core/core/doctor/checks/skills-count.js +83 -15
- package/.aiox-core/core/graph-dashboard/cli.js +1 -2
- package/.aiox-core/core/graph-dashboard/data-sources/code-intel-source.js +1 -1
- package/.aiox-core/core/ids/layer-classifier.js +1 -1
- package/.aiox-core/core/pro/pro-updater.js +578 -0
- package/.aiox-core/core/synapse/context/context-tracker.js +107 -9
- package/.aiox-core/core/synapse/layers/layer-processor.js +1 -1
- package/.aiox-core/core-config.yaml +15 -1
- package/.aiox-core/data/capability-detection.js +15 -15
- package/.aiox-core/data/entity-registry.yaml +18 -2
- package/.aiox-core/data/registry-update-log.jsonl +5 -0
- package/.aiox-core/data/tok3-token-comparison.js +0 -4
- package/.aiox-core/data/tool-search-validation.js +1 -1
- package/.aiox-core/development/agents/aiox-master.md +44 -6
- package/.aiox-core/development/agents/data-engineer.md +4 -4
- package/.aiox-core/development/agents/devops.md +52 -2
- package/.aiox-core/development/agents/po.md +1 -1
- package/.aiox-core/development/agents/qa.md +5 -11
- package/.aiox-core/development/agents/sm.md +3 -3
- package/.aiox-core/development/agents/ux-design-expert.md +1 -1
- package/.aiox-core/development/scripts/unified-activation-pipeline.js +29 -3
- package/.aiox-core/development/tasks/dev-develop-story.md +46 -7
- package/.aiox-core/development/tasks/devops-pro-access-grant.md +93 -0
- package/.aiox-core/development/tasks/devops-pro-activate.md +42 -0
- package/.aiox-core/development/tasks/devops-pro-check-access.md +34 -0
- package/.aiox-core/development/tasks/devops-pro-request-reset.md +34 -0
- package/.aiox-core/development/tasks/devops-pro-resend-verification.md +32 -0
- package/.aiox-core/development/tasks/devops-pro-reset-password.md +36 -0
- package/.aiox-core/development/tasks/devops-pro-validate-login.md +36 -0
- package/.aiox-core/development/tasks/devops-pro-verify-status.md +33 -0
- package/.aiox-core/development/tasks/qa-gate.md +54 -4
- package/.aiox-core/development/tasks/validate-next-story.md +39 -2
- package/.aiox-core/framework-config.yaml +1 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/README.md +69 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/bootstrap.js +727 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/index.js +10 -0
- package/.aiox-core/infrastructure/scripts/codex-skills-sync/validate.js +65 -4
- package/.aiox-core/infrastructure/scripts/generate-settings-json.js +29 -4
- package/.aiox-core/infrastructure/scripts/ide-sync/agent-parser.js +4 -0
- package/.aiox-core/infrastructure/scripts/ide-sync/index.js +67 -7
- package/.aiox-core/infrastructure/scripts/ide-sync/transformers/claude-code.js +145 -3
- package/.aiox-core/infrastructure/scripts/repair-agent-references.js +263 -0
- package/.aiox-core/infrastructure/scripts/validate-claude-integration.js +60 -8
- package/.aiox-core/infrastructure/scripts/validate-paths.js +13 -0
- package/.aiox-core/install-manifest.yaml +134 -82
- package/.aiox-core/utils/filters/index.js +2 -1
- package/.claude/commands/AIOX/agents/aiox-master.md +21 -0
- package/.claude/commands/AIOX/agents/analyst.md +21 -0
- package/.claude/commands/AIOX/agents/architect.md +21 -0
- package/.claude/commands/AIOX/agents/data-engineer.md +21 -0
- package/.claude/commands/AIOX/agents/dev.md +21 -0
- package/.claude/commands/AIOX/agents/devops.md +21 -0
- package/.claude/commands/AIOX/agents/pm.md +21 -0
- package/.claude/commands/AIOX/agents/po.md +21 -0
- package/.claude/commands/AIOX/agents/qa.md +21 -0
- package/.claude/commands/AIOX/agents/sm.md +21 -0
- package/.claude/commands/AIOX/agents/squad-creator.md +21 -0
- package/.claude/commands/AIOX/agents/ux-design-expert.md +21 -0
- package/.claude/commands/AIOX/scripts/agent-config-loader.js +624 -0
- package/.claude/commands/AIOX/scripts/generate-greeting.js +160 -0
- package/.claude/commands/AIOX/scripts/greeting-builder.js +866 -0
- package/.claude/commands/AIOX/scripts/session-context-loader.js +286 -0
- package/.claude/commands/AIOX/stories/story-6.1.4.md +1404 -0
- package/.claude/commands/cohort-squad/agents/cohort-manager.md +156 -0
- package/.claude/commands/design-system/agents/brad-frost.md +1097 -0
- package/.claude/commands/design-system/agents/dan-mall.md +857 -0
- package/.claude/commands/design-system/agents/dave-malouf.md +2272 -0
- package/.claude/commands/design-system/agents/design-chief.md +102 -0
- package/.claude/commands/design-system/agents/nano-banana-generator.md +162 -0
- package/.claude/commands/greet.md +101 -0
- package/.claude/commands/synapse/manager.md +75 -0
- package/.claude/commands/synapse/tasks/add-rule.md +94 -0
- package/.claude/commands/synapse/tasks/create-command.md +109 -0
- package/.claude/commands/synapse/tasks/create-domain.md +127 -0
- package/.claude/commands/synapse/tasks/diagnose-synapse.md +245 -0
- package/.claude/commands/synapse/tasks/edit-rule.md +109 -0
- package/.claude/commands/synapse/tasks/suggest-domain.md +116 -0
- package/.claude/commands/synapse/tasks/toggle-domain.md +83 -0
- package/.claude/commands/synapse/templates/domain-template +8 -0
- package/.claude/commands/synapse/templates/manifest-entry-template +4 -0
- package/.claude/commands/synapse/utils/manifest-parser-reference.md +134 -0
- package/.claude/hooks/precompact-session-digest.cjs +2 -2
- package/.claude/skills/AIOX/agents/aiox-master/SKILL.md +511 -0
- package/.claude/skills/AIOX/agents/analyst/SKILL.md +281 -0
- package/.claude/skills/AIOX/agents/architect/SKILL.md +482 -0
- package/.claude/skills/AIOX/agents/data-engineer/SKILL.md +503 -0
- package/.claude/skills/AIOX/agents/dev/SKILL.md +568 -0
- package/.claude/skills/AIOX/agents/devops/SKILL.md +597 -0
- package/.claude/skills/AIOX/agents/pm/SKILL.md +385 -0
- package/.claude/skills/AIOX/agents/po/SKILL.md +343 -0
- package/.claude/skills/AIOX/agents/qa/SKILL.md +451 -0
- package/.claude/skills/AIOX/agents/sm/SKILL.md +295 -0
- package/.claude/skills/AIOX/agents/squad-creator/SKILL.md +352 -0
- package/.claude/skills/AIOX/agents/ux-design-expert/SKILL.md +503 -0
- package/.claude/skills/architect-first/SKILL.md +275 -0
- package/.claude/skills/architect-first/assets/architecture-template.md +505 -0
- package/.claude/skills/architect-first/assets/config-template.yaml +351 -0
- package/.claude/skills/architect-first/references/architecture-checklist.md +216 -0
- package/.claude/skills/architect-first/references/pre-implementation-checklist.md +119 -0
- package/.claude/skills/architect-first/references/stop-rules-guide.md +291 -0
- package/.claude/skills/architect-first/references/testing-strategy-guide.md +477 -0
- package/.claude/skills/architect-first/scripts/architecture_validator.py +490 -0
- package/.claude/skills/architect-first/scripts/check_coupling.py +306 -0
- package/.claude/skills/architect-first/scripts/validate_risk_mitigation.py +382 -0
- package/.claude/skills/checklist-runner/SKILL.md +113 -0
- package/.claude/skills/clone-mind.md +329 -0
- package/.claude/skills/coderabbit-review/SKILL.md +106 -0
- package/.claude/skills/course-generation-workflow.md +76 -0
- package/.claude/skills/enhance-workflow.md +466 -0
- package/.claude/skills/mcp-builder/LICENSE.txt +202 -0
- package/.claude/skills/mcp-builder/SKILL.md +328 -0
- package/.claude/skills/mcp-builder/reference/evaluation.md +602 -0
- package/.claude/skills/mcp-builder/reference/mcp_best_practices.md +915 -0
- package/.claude/skills/mcp-builder/reference/node_mcp_server.md +916 -0
- package/.claude/skills/mcp-builder/reference/python_mcp_server.md +752 -0
- package/.claude/skills/mcp-builder/scripts/connections.py +151 -0
- package/.claude/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/.claude/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/.claude/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/.claude/skills/ralph.md +181 -0
- package/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/skill-creator/SKILL.md +209 -0
- package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/.claude/skills/squad.md +301 -0
- package/.claude/skills/synapse/SKILL.md +132 -0
- package/.claude/skills/synapse/assets/README.md +50 -0
- package/.claude/skills/synapse/references/brackets.md +100 -0
- package/.claude/skills/synapse/references/commands.md +118 -0
- package/.claude/skills/synapse/references/domains.md +126 -0
- package/.claude/skills/synapse/references/layers.md +186 -0
- package/.claude/skills/synapse/references/manifest.md +142 -0
- package/.claude/skills/tech-search/SKILL.md +431 -0
- package/.claude/skills/tech-search/prompts/page-extract.md +133 -0
- package/README.en.md +2 -2
- package/README.md +8 -2
- package/bin/aiox.js +55 -4
- package/bin/utils/framework-guard.js +4 -2
- package/bin/utils/pro-detector.js +119 -28
- package/bin/utils/validate-publish.js +6 -6
- package/docs/aiox-agent-flows/devops-system.md +18 -0
- package/docs/aiox-workflows/README.md +1 -0
- package/docs/aiox-workflows/pro-access-grant-workflow.md +218 -0
- package/docs/guides/pro/access-grant-ops-playbook.md +370 -0
- package/docs/guides/pro/install-gate-setup.md +12 -6
- package/docs/guides/pro/squad-creator-handoff-pro-access-ops.md +134 -0
- package/docs/guides/supabase-ops-handoff.md +768 -0
- package/package.json +12 -1
- package/packages/aiox-pro-cli/bin/aiox-pro.js +33 -12
- package/packages/installer/src/config/configure-environment.js +118 -50
- package/packages/installer/src/installer/aiox-core-installer.js +124 -27
- package/packages/installer/src/installer/brownfield-upgrader.js +66 -9
- package/packages/installer/src/installer/dependency-installer.js +4 -0
- package/packages/installer/src/pro/pro-scaffolder.js +5 -5
- package/packages/installer/src/updater/index.js +151 -10
- package/packages/installer/src/wizard/ide-config-generator.js +73 -7
- package/packages/installer/src/wizard/index.js +119 -31
- package/packages/installer/src/wizard/pro-setup.js +118 -47
- package/packages/installer/src/wizard/validation/validators/dependency-validator.js +32 -25
- package/packages/installer/src/wizard/validation/validators/file-structure-validator.js +26 -0
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +84 -1
- package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +1 -1
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +85 -19
- package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +4 -4
- package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +5 -5
- package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +4 -4
- package/packages/installer/tests/unit/merger/yaml-merger.test.js +11 -11
- package/pro/README.md +12 -1
- package/pro/license/index.js +3 -11
- package/pro/license/license-api.js +25 -0
- package/pro/license/license-cache.js +135 -31
- package/pro/license/license-crypto.js +59 -3
- package/pro/package.json +5 -4
- package/pro/squads/README.md +16 -16
- package/pro/squads/index.js +1 -1
- package/scripts/e2e/installed-skills-smoke.js +264 -0
- package/scripts/package-synapse.js +3 -3
- package/scripts/validate-package-completeness.js +8 -11
- package/.aiox-core/lib/build.json +0 -1
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Codex local skills bootstrap.
|
|
6
|
+
*
|
|
7
|
+
* Purpose:
|
|
8
|
+
* - Generate `.codex/skills/aiox-*` for each core AIOX agent in
|
|
9
|
+
* `.aiox-core/development/agents/` (e.g. aiox-dev, aiox-pm, aiox-po).
|
|
10
|
+
* - Generate `.codex/skills/aiox-*` for each squad entry chief discovered
|
|
11
|
+
* in `squads/*\/config.yaml` (e.g. aiox-mega-brain-chief, aiox-slides-chief).
|
|
12
|
+
* - Heavy source files stay in `.aiox-core/development/agents/` and `squads/`.
|
|
13
|
+
* Generated SKILL.md files are activator stubs that load the source on demand.
|
|
14
|
+
*
|
|
15
|
+
* Why a separate script (vs `index.js`):
|
|
16
|
+
* - `index.js` (npm run sync:skills:codex) is the incremental sync used in CI.
|
|
17
|
+
* It only covers core agents and is tightly coupled with the shared
|
|
18
|
+
* `agent-parser.js` from `ide-sync/`.
|
|
19
|
+
* - `bootstrap.js` is a one-shot operator command that ALSO covers squad
|
|
20
|
+
* chiefs (the missing-skills bug fix). It is standalone with a vendored
|
|
21
|
+
* js-yaml fallback so it works on broken/partial installs.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* npm run setup:codex-skills # generate / update
|
|
25
|
+
* npm run setup:codex-skills:dry # preview without writes
|
|
26
|
+
* node .aiox-core/infrastructure/scripts/codex-skills-sync/bootstrap.js --help
|
|
27
|
+
*
|
|
28
|
+
* After running, restart Codex CLI from the project root if the `$` menu
|
|
29
|
+
* does not refresh automatically.
|
|
30
|
+
*
|
|
31
|
+
* @see Story 123.9 — Codex Local Skills Bootstrap (squad chief coverage).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const fs = require('fs');
|
|
35
|
+
const path = require('path');
|
|
36
|
+
|
|
37
|
+
const MARKER = '<!-- AIOX-CODEX-LOCAL-SKILLS: generated -->';
|
|
38
|
+
|
|
39
|
+
function parseArgs(argv) {
|
|
40
|
+
const args = {
|
|
41
|
+
projectRoot: '',
|
|
42
|
+
skillsDir: '.codex/skills',
|
|
43
|
+
dryRun: false,
|
|
44
|
+
force: false,
|
|
45
|
+
quiet: false,
|
|
46
|
+
help: false,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
for (let index = 2; index < argv.length; index += 1) {
|
|
50
|
+
const arg = argv[index];
|
|
51
|
+
if (arg === '--help' || arg === '-h') {
|
|
52
|
+
args.help = true;
|
|
53
|
+
} else if (arg === '--dry-run' || arg === '-n') {
|
|
54
|
+
args.dryRun = true;
|
|
55
|
+
} else if (arg === '--force' || arg === '-f') {
|
|
56
|
+
args.force = true;
|
|
57
|
+
} else if (arg === '--quiet' || arg === '-q') {
|
|
58
|
+
args.quiet = true;
|
|
59
|
+
} else if (arg === '--project-root' && argv[index + 1]) {
|
|
60
|
+
args.projectRoot = argv[index + 1];
|
|
61
|
+
index += 1;
|
|
62
|
+
} else if (arg === '--skills-dir' && argv[index + 1]) {
|
|
63
|
+
args.skillsDir = argv[index + 1];
|
|
64
|
+
index += 1;
|
|
65
|
+
} else {
|
|
66
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return args;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function printHelp() {
|
|
74
|
+
console.log(`Codex local skills bootstrap
|
|
75
|
+
|
|
76
|
+
Usage:
|
|
77
|
+
node .aiox-core/infrastructure/scripts/codex-skills-sync/bootstrap.js [options]
|
|
78
|
+
npm run setup:codex-skills [-- options]
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
--project-root <path> Project root. Defaults to current directory or nearest parent with .aiox-core/squads.
|
|
82
|
+
--skills-dir <path> Skills output directory. Defaults to .codex/skills.
|
|
83
|
+
--dry-run, -n Preview without writing files.
|
|
84
|
+
--force, -f Overwrite non-generated skill files after creating a .bak copy.
|
|
85
|
+
--quiet, -q Reduce output.
|
|
86
|
+
--help, -h Show this help.
|
|
87
|
+
`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function findProjectRoot(start) {
|
|
91
|
+
let current = path.resolve(start || process.cwd());
|
|
92
|
+
|
|
93
|
+
for (let depth = 0; depth < 10; depth += 1) {
|
|
94
|
+
const hasAioxCore = fs.existsSync(path.join(current, '.aiox-core'));
|
|
95
|
+
const hasSquads = fs.existsSync(path.join(current, 'squads'));
|
|
96
|
+
if (hasAioxCore || hasSquads) {
|
|
97
|
+
return current;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const parent = path.dirname(current);
|
|
101
|
+
if (parent === current) break;
|
|
102
|
+
current = parent;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return path.resolve(start || process.cwd());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function tryRequire(modulePath) {
|
|
109
|
+
try {
|
|
110
|
+
return require(modulePath);
|
|
111
|
+
} catch (_error) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function loadYaml(projectRoot) {
|
|
117
|
+
const candidates = [
|
|
118
|
+
'js-yaml',
|
|
119
|
+
path.join(projectRoot, 'node_modules', 'js-yaml'),
|
|
120
|
+
path.join(projectRoot, '.aiox-core', 'node_modules', 'js-yaml'),
|
|
121
|
+
path.join(__dirname, 'node_modules', 'js-yaml'),
|
|
122
|
+
path.join(__dirname, '.aiox-core', 'node_modules', 'js-yaml'),
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
for (const candidate of candidates) {
|
|
126
|
+
const mod = tryRequire(candidate);
|
|
127
|
+
if (mod && typeof mod.load === 'function') {
|
|
128
|
+
return mod;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function readText(filePath) {
|
|
136
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function listFiles(dirPath, predicate) {
|
|
140
|
+
if (!fs.existsSync(dirPath)) return [];
|
|
141
|
+
return fs
|
|
142
|
+
.readdirSync(dirPath)
|
|
143
|
+
.map((name) => path.join(dirPath, name))
|
|
144
|
+
.filter((filePath) => {
|
|
145
|
+
try {
|
|
146
|
+
return fs.statSync(filePath).isFile() && (!predicate || predicate(filePath));
|
|
147
|
+
} catch (_error) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
.sort();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function listDirs(dirPath) {
|
|
155
|
+
if (!fs.existsSync(dirPath)) return [];
|
|
156
|
+
return fs
|
|
157
|
+
.readdirSync(dirPath)
|
|
158
|
+
.map((name) => path.join(dirPath, name))
|
|
159
|
+
.filter((filePath) => {
|
|
160
|
+
try {
|
|
161
|
+
return fs.statSync(filePath).isDirectory();
|
|
162
|
+
} catch (_error) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
.sort();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function extractYamlBlock(markdown) {
|
|
170
|
+
const match = markdown.match(/```yaml\s*([\s\S]*?)```/i);
|
|
171
|
+
return match ? match[1] : '';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseYamlText(text, yaml) {
|
|
175
|
+
if (!text || !yaml) return null;
|
|
176
|
+
try {
|
|
177
|
+
const parsed = yaml.load(text);
|
|
178
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
179
|
+
} catch (_error) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getNested(object, pathParts) {
|
|
185
|
+
let current = object;
|
|
186
|
+
for (const part of pathParts) {
|
|
187
|
+
if (!current || typeof current !== 'object') return '';
|
|
188
|
+
current = current[part];
|
|
189
|
+
}
|
|
190
|
+
return typeof current === 'string' ? current.trim() : '';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function findScalar(text, keys) {
|
|
194
|
+
for (const key of keys) {
|
|
195
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
196
|
+
const match = text.match(new RegExp(`^[ \\t]*${escaped}[ \\t]*:[ \\t]*['"]?([^'"\\n#]+)`, 'm'));
|
|
197
|
+
if (match && match[1]) return match[1].trim();
|
|
198
|
+
}
|
|
199
|
+
return '';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function findBlockScalar(text, key) {
|
|
203
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
204
|
+
const match = text.match(new RegExp(`^\\s*${escaped}\\s*:\\s*[|>]\\s*\\n([\\s\\S]*?)(?=\\n\\S|\\n\\s{0,2}[A-Za-z0-9_-]+\\s*:|$)`, 'm'));
|
|
205
|
+
if (!match || !match[1]) return '';
|
|
206
|
+
|
|
207
|
+
return match[1]
|
|
208
|
+
.split('\n')
|
|
209
|
+
.map((line) => line.replace(/^\s{2,}/, ''))
|
|
210
|
+
.join(' ')
|
|
211
|
+
.replace(/\s+/g, ' ')
|
|
212
|
+
.trim();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function slug(value) {
|
|
216
|
+
return String(value || '')
|
|
217
|
+
.trim()
|
|
218
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
219
|
+
.replace(/[_\s]+/g, '-')
|
|
220
|
+
.replace(/[^A-Za-z0-9-]/g, '-')
|
|
221
|
+
.replace(/-+/g, '-')
|
|
222
|
+
.replace(/^-|-$/g, '')
|
|
223
|
+
.toLowerCase();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function yamlString(value) {
|
|
227
|
+
return JSON.stringify(String(value || '').replace(/\s+/g, ' ').trim());
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function truncate(value, maxLength) {
|
|
231
|
+
const clean = String(value || '').replace(/\s+/g, ' ').trim();
|
|
232
|
+
if (clean.length <= maxLength) return clean;
|
|
233
|
+
return `${clean.slice(0, maxLength - 3).trim()}...`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function basenameNoExt(filePath) {
|
|
237
|
+
return path.basename(filePath, path.extname(filePath));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function relative(projectRoot, filePath) {
|
|
241
|
+
return path.relative(projectRoot, filePath).replace(/\\/g, '/');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function parseAgentFile(filePath, yaml) {
|
|
245
|
+
const raw = readText(filePath);
|
|
246
|
+
const yamlBlock = extractYamlBlock(raw);
|
|
247
|
+
const parsed = parseYamlText(yamlBlock, yaml) || {};
|
|
248
|
+
const agent = parsed.agent && typeof parsed.agent === 'object' ? parsed.agent : {};
|
|
249
|
+
const id = slug(agent.id || basenameNoExt(filePath));
|
|
250
|
+
const title =
|
|
251
|
+
String(agent.title || '').trim() ||
|
|
252
|
+
findScalar(yamlBlock, ['title']) ||
|
|
253
|
+
basenameNoExt(filePath);
|
|
254
|
+
const name = String(agent.name || '').trim() || findScalar(yamlBlock, ['name']) || title;
|
|
255
|
+
const whenToUse =
|
|
256
|
+
String(agent.whenToUse || '').trim() ||
|
|
257
|
+
String(agent.when_to_use || '').trim() ||
|
|
258
|
+
findBlockScalar(yamlBlock, 'whenToUse') ||
|
|
259
|
+
findScalar(yamlBlock, ['whenToUse', 'when_to_use']) ||
|
|
260
|
+
`Use this skill to activate ${title}.`;
|
|
261
|
+
const commands = collectCommands(parsed.commands).concat(collectCommandsFromText(yamlBlock));
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
id,
|
|
265
|
+
name,
|
|
266
|
+
title,
|
|
267
|
+
whenToUse,
|
|
268
|
+
commands: uniqueCommands(commands),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function collectCommands(node, acc = []) {
|
|
273
|
+
if (!node) return acc;
|
|
274
|
+
|
|
275
|
+
if (Array.isArray(node)) {
|
|
276
|
+
node.forEach((item) => collectCommands(item, acc));
|
|
277
|
+
return acc;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (typeof node === 'string') {
|
|
281
|
+
const parsed = parseCommandLine(node);
|
|
282
|
+
if (parsed) acc.push(parsed);
|
|
283
|
+
return acc;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (typeof node !== 'object') return acc;
|
|
287
|
+
|
|
288
|
+
if (typeof node.command === 'string') {
|
|
289
|
+
const parsed = parseCommandLine(node.command);
|
|
290
|
+
if (parsed) {
|
|
291
|
+
parsed.description =
|
|
292
|
+
typeof node.description === 'string' && node.description.trim()
|
|
293
|
+
? node.description.trim()
|
|
294
|
+
: parsed.description;
|
|
295
|
+
acc.push(parsed);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
Object.keys(node).forEach((key) => collectCommands(node[key], acc));
|
|
300
|
+
return acc;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function collectCommandsFromText(text) {
|
|
304
|
+
const commands = [];
|
|
305
|
+
const patterns = [
|
|
306
|
+
/command\s*:\s*['"]?(\*[A-Za-z0-9:_-]+)['"]?(?:\s*\n\s*description\s*:\s*['"]?([^'"\n]+))?/g,
|
|
307
|
+
/-\s+`?(\*[A-Za-z0-9:_-]+)`?\s+-\s+([^\n]+)/g,
|
|
308
|
+
/-\s+['"]?(\*[A-Za-z0-9:_-]+)['"]?\s+-\s+([^\n]+)/g,
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
for (const pattern of patterns) {
|
|
312
|
+
let match = pattern.exec(text);
|
|
313
|
+
while (match) {
|
|
314
|
+
commands.push({
|
|
315
|
+
name: match[1].trim(),
|
|
316
|
+
description: (match[2] || 'Execute command').trim(),
|
|
317
|
+
});
|
|
318
|
+
match = pattern.exec(text);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return commands;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function parseCommandLine(value) {
|
|
326
|
+
if (typeof value !== 'string') return null;
|
|
327
|
+
const clean = value.trim();
|
|
328
|
+
if (!clean.startsWith('*')) return null;
|
|
329
|
+
|
|
330
|
+
const separator = clean.indexOf(' - ');
|
|
331
|
+
if (separator === -1) {
|
|
332
|
+
return { name: clean.split(/\s+/)[0], description: 'Execute command' };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
name: clean.slice(0, separator).trim(),
|
|
337
|
+
description: clean.slice(separator + 3).trim() || 'Execute command',
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function uniqueCommands(commands) {
|
|
342
|
+
const seen = new Set();
|
|
343
|
+
const result = [];
|
|
344
|
+
|
|
345
|
+
for (const item of commands) {
|
|
346
|
+
if (!item || !item.name || seen.has(item.name)) continue;
|
|
347
|
+
seen.add(item.name);
|
|
348
|
+
result.push(item);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function starterCommands(commands) {
|
|
355
|
+
const unique = uniqueCommands(commands);
|
|
356
|
+
const help = unique.find((item) => item.name === '*help');
|
|
357
|
+
const rest = unique.filter((item) => item.name !== '*help');
|
|
358
|
+
const selected = help ? [help].concat(rest.slice(0, 7)) : rest.slice(0, 8);
|
|
359
|
+
return selected.length ? selected : [{ name: '*help', description: 'List available commands' }];
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function parseConfig(filePath, yaml) {
|
|
363
|
+
const raw = readText(filePath);
|
|
364
|
+
const parsed = parseYamlText(raw, yaml) || {};
|
|
365
|
+
return { raw, parsed };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function resolveEntryAgent(configData, squadDir) {
|
|
369
|
+
const parsed = configData.parsed || {};
|
|
370
|
+
const raw = configData.raw || '';
|
|
371
|
+
|
|
372
|
+
const direct =
|
|
373
|
+
getNested(parsed, ['entry_agent']) ||
|
|
374
|
+
getNested(parsed, ['squad', 'entry_agent']) ||
|
|
375
|
+
getNested(parsed, ['pack', 'entry_agent']) ||
|
|
376
|
+
getNested(parsed, ['orchestrator', 'agent']) ||
|
|
377
|
+
getNested(parsed, ['tier_system', 'orchestrator']) ||
|
|
378
|
+
findScalar(raw, ['entry_agent', 'orchestrator']);
|
|
379
|
+
if (direct) return slug(direct);
|
|
380
|
+
|
|
381
|
+
if (Array.isArray(parsed.agents)) {
|
|
382
|
+
const chief = parsed.agents.find((agent) => {
|
|
383
|
+
if (!agent || typeof agent !== 'object') return false;
|
|
384
|
+
const id = String(agent.id || '').trim();
|
|
385
|
+
const tier = String(agent.tier || '').trim().toLowerCase();
|
|
386
|
+
return tier === 'orchestrator' || /chief$/i.test(id);
|
|
387
|
+
});
|
|
388
|
+
if (chief && chief.id) return slug(chief.id);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const agentsDir = path.join(squadDir, 'agents');
|
|
392
|
+
const agentFiles = listFiles(agentsDir, (filePath) => path.extname(filePath) === '.md');
|
|
393
|
+
const preferred = agentFiles.find((filePath) => /(^|-)chief$/i.test(basenameNoExt(filePath)));
|
|
394
|
+
if (preferred) return slug(basenameNoExt(preferred));
|
|
395
|
+
|
|
396
|
+
const anyChief = agentFiles.find((filePath) => /chief/i.test(basenameNoExt(filePath)));
|
|
397
|
+
if (anyChief) return slug(basenameNoExt(anyChief));
|
|
398
|
+
|
|
399
|
+
return agentFiles[0] ? slug(basenameNoExt(agentFiles[0])) : '';
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function resolveAgentFile(squadDir, configData, entryAgent) {
|
|
403
|
+
const parsed = configData.parsed || {};
|
|
404
|
+
|
|
405
|
+
if (Array.isArray(parsed.agents)) {
|
|
406
|
+
const match = parsed.agents.find((agent) => agent && slug(agent.id) === entryAgent);
|
|
407
|
+
if (match && typeof match.file === 'string' && match.file.trim()) {
|
|
408
|
+
const candidate = path.join(squadDir, match.file.trim());
|
|
409
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const direct = path.join(squadDir, 'agents', `${entryAgent}.md`);
|
|
414
|
+
if (fs.existsSync(direct)) return direct;
|
|
415
|
+
|
|
416
|
+
const agentFiles = listFiles(path.join(squadDir, 'agents'), (filePath) => path.extname(filePath) === '.md');
|
|
417
|
+
return agentFiles.find((filePath) => slug(basenameNoExt(filePath)) === entryAgent) || '';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function resolveSquadAlias(configData, squadName) {
|
|
421
|
+
const parsed = configData.parsed || {};
|
|
422
|
+
const raw = configData.raw || '';
|
|
423
|
+
const alias =
|
|
424
|
+
getNested(parsed, ['slashPrefix']) ||
|
|
425
|
+
getNested(parsed, ['slash_prefix']) ||
|
|
426
|
+
getNested(parsed, ['squad', 'slashPrefix']) ||
|
|
427
|
+
getNested(parsed, ['squad', 'slash_prefix']) ||
|
|
428
|
+
getNested(parsed, ['pack', 'slashPrefix']) ||
|
|
429
|
+
getNested(parsed, ['pack', 'slash_prefix']) ||
|
|
430
|
+
findScalar(raw, ['slashPrefix', 'slash_prefix']);
|
|
431
|
+
|
|
432
|
+
return slug(alias || squadName);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function coreSkillId(agentId) {
|
|
436
|
+
const clean = slug(agentId);
|
|
437
|
+
return clean.startsWith('aiox-') ? clean : `aiox-${clean}`;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function squadSkillId(squadName, entryAgent, squadAlias) {
|
|
441
|
+
const alias = slug(squadAlias || '').replace(/^aiox-/, '');
|
|
442
|
+
const squadBase = slug(squadName).replace(/^aiox-/, '');
|
|
443
|
+
const entry = slug(entryAgent).replace(/^aiox-/, '');
|
|
444
|
+
const entryRoot = entry.replace(/-(chief|master|orchestrator)$/i, '');
|
|
445
|
+
const genericEntryRoots = new Set(['agent', 'chief', 'master', 'orchestrator', 'squad']);
|
|
446
|
+
|
|
447
|
+
if (!entry) return coreSkillId(squadBase || alias);
|
|
448
|
+
if (entry === squadBase || entry.startsWith(`${squadBase}-`)) return `aiox-${entry}`;
|
|
449
|
+
if (alias && (entry === alias || entry.startsWith(`${alias}-`))) return `aiox-${entry}`;
|
|
450
|
+
if (entryRoot && !genericEntryRoots.has(entryRoot) && squadBase.startsWith(`${entryRoot}-`)) {
|
|
451
|
+
return `aiox-${entry}`;
|
|
452
|
+
}
|
|
453
|
+
if (entryRoot && !genericEntryRoots.has(entryRoot) && entry.includes('-chief')) {
|
|
454
|
+
return `aiox-${entry}`;
|
|
455
|
+
}
|
|
456
|
+
return `aiox-${squadBase || alias}-${entry}`;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function buildSkillContent({ skillId, title, whenToUse, sourcePath, scopePath, commands }) {
|
|
460
|
+
const starters = starterCommands(commands)
|
|
461
|
+
.map((command) => `- \`${command.name}\` - ${command.description}`)
|
|
462
|
+
.join('\n');
|
|
463
|
+
|
|
464
|
+
const description = truncate(`${title}. ${whenToUse}`, 180);
|
|
465
|
+
|
|
466
|
+
return `---
|
|
467
|
+
name: ${skillId}
|
|
468
|
+
description: ${yamlString(description)}
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
# ${title} Activator
|
|
472
|
+
|
|
473
|
+
${MARKER}
|
|
474
|
+
|
|
475
|
+
## Source Of Truth
|
|
476
|
+
Load \`${sourcePath}\` before adopting this skill.
|
|
477
|
+
|
|
478
|
+
## When To Use
|
|
479
|
+
${whenToUse}
|
|
480
|
+
|
|
481
|
+
## Activation Protocol
|
|
482
|
+
1. Read \`${sourcePath}\` as the source of truth.
|
|
483
|
+
2. Adopt the persona, command system, dependencies, and activation instructions from that file.
|
|
484
|
+
3. Resolve dependencies relative to \`${scopePath}\` unless the source file declares a more specific path.
|
|
485
|
+
4. Stay in this persona until the user asks to switch or exit.
|
|
486
|
+
|
|
487
|
+
## Starter Commands
|
|
488
|
+
${starters}
|
|
489
|
+
|
|
490
|
+
## Non-Negotiables
|
|
491
|
+
- Follow \`.aiox-core/constitution.md\` when it exists.
|
|
492
|
+
- Do not copy squad internals into this skill; load them on demand from the source paths.
|
|
493
|
+
- Keep writes scoped to the active project unless the user explicitly asks otherwise.
|
|
494
|
+
`;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function buildCorePlans(projectRoot, yaml) {
|
|
498
|
+
const agentsDir = path.join(projectRoot, '.aiox-core', 'development', 'agents');
|
|
499
|
+
const files = listFiles(agentsDir, (filePath) => path.extname(filePath) === '.md');
|
|
500
|
+
|
|
501
|
+
return files.map((filePath) => {
|
|
502
|
+
const agent = parseAgentFile(filePath, yaml);
|
|
503
|
+
const sourcePath = relative(projectRoot, filePath);
|
|
504
|
+
const skillId = coreSkillId(agent.id);
|
|
505
|
+
return {
|
|
506
|
+
kind: 'core',
|
|
507
|
+
skillId,
|
|
508
|
+
sourcePath,
|
|
509
|
+
content: buildSkillContent({
|
|
510
|
+
skillId,
|
|
511
|
+
title: `AIOX ${agent.title}`,
|
|
512
|
+
whenToUse: agent.whenToUse,
|
|
513
|
+
sourcePath,
|
|
514
|
+
scopePath: '.aiox-core/development',
|
|
515
|
+
commands: agent.commands,
|
|
516
|
+
}),
|
|
517
|
+
};
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function buildSquadPlans(projectRoot, yaml) {
|
|
522
|
+
const squadsDir = path.join(projectRoot, 'squads');
|
|
523
|
+
const squadDirs = listDirs(squadsDir);
|
|
524
|
+
const plans = [];
|
|
525
|
+
const warnings = [];
|
|
526
|
+
|
|
527
|
+
for (const squadDir of squadDirs) {
|
|
528
|
+
const squadName = path.basename(squadDir);
|
|
529
|
+
const configPath = path.join(squadDir, 'config.yaml');
|
|
530
|
+
if (!fs.existsSync(configPath)) {
|
|
531
|
+
warnings.push(`Skipped ${squadName}: missing config.yaml`);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const configData = parseConfig(configPath, yaml);
|
|
536
|
+
const entryAgent = resolveEntryAgent(configData, squadDir);
|
|
537
|
+
if (!entryAgent) {
|
|
538
|
+
warnings.push(`Skipped ${squadName}: no entry/chief agent found`);
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const sourceFile = resolveAgentFile(squadDir, configData, entryAgent);
|
|
543
|
+
if (!sourceFile) {
|
|
544
|
+
warnings.push(`Skipped ${squadName}: entry/chief file not found for ${entryAgent}`);
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const agent = parseAgentFile(sourceFile, yaml);
|
|
549
|
+
const alias = resolveSquadAlias(configData, squadName);
|
|
550
|
+
const skillId = squadSkillId(squadName, entryAgent, alias);
|
|
551
|
+
const sourcePath = relative(projectRoot, sourceFile);
|
|
552
|
+
|
|
553
|
+
plans.push({
|
|
554
|
+
kind: 'squad',
|
|
555
|
+
skillId,
|
|
556
|
+
sourcePath,
|
|
557
|
+
content: buildSkillContent({
|
|
558
|
+
skillId,
|
|
559
|
+
title: `${agent.title} (${squadName})`,
|
|
560
|
+
whenToUse: agent.whenToUse,
|
|
561
|
+
sourcePath,
|
|
562
|
+
scopePath: relative(projectRoot, squadDir),
|
|
563
|
+
commands: agent.commands,
|
|
564
|
+
}),
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return { plans, warnings };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function targetFileFor(skillsDir, skillId) {
|
|
572
|
+
return path.join(skillsDir, skillId, 'SKILL.md');
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function backupFile(filePath) {
|
|
576
|
+
const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+$/, '').replace('T', '-');
|
|
577
|
+
const backup = `${filePath}.bak-${stamp}`;
|
|
578
|
+
fs.copyFileSync(filePath, backup);
|
|
579
|
+
return backup;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function writePlanItem(item, skillsDir, options) {
|
|
583
|
+
const filePath = targetFileFor(skillsDir, item.skillId);
|
|
584
|
+
const dirPath = path.dirname(filePath);
|
|
585
|
+
const rel = relative(options.projectRoot, filePath);
|
|
586
|
+
|
|
587
|
+
if (options.dryRun) {
|
|
588
|
+
return { status: fs.existsSync(filePath) ? 'would_update' : 'would_create', path: rel };
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (!fs.existsSync(dirPath)) {
|
|
592
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (fs.existsSync(filePath)) {
|
|
596
|
+
const current = readText(filePath);
|
|
597
|
+
if (current === item.content) {
|
|
598
|
+
return { status: 'unchanged', path: rel };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (!current.includes(MARKER) && !options.force) {
|
|
602
|
+
return { status: 'skipped_existing', path: rel };
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
let backup = '';
|
|
606
|
+
if (!current.includes(MARKER) && options.force) {
|
|
607
|
+
backup = relative(options.projectRoot, backupFile(filePath));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
fs.writeFileSync(filePath, item.content, 'utf8');
|
|
611
|
+
return { status: backup ? 'updated_with_backup' : 'updated', path: rel, backup };
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
fs.writeFileSync(filePath, item.content, 'utf8');
|
|
615
|
+
return { status: 'created', path: rel };
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function dedupePlans(plans) {
|
|
619
|
+
const seen = new Map();
|
|
620
|
+
const result = [];
|
|
621
|
+
const warnings = [];
|
|
622
|
+
|
|
623
|
+
for (const item of plans) {
|
|
624
|
+
if (!seen.has(item.skillId)) {
|
|
625
|
+
seen.set(item.skillId, item);
|
|
626
|
+
result.push(item);
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const replacement = {
|
|
631
|
+
...item,
|
|
632
|
+
skillId: `${item.skillId}-${slug(path.dirname(item.sourcePath).split('/')[1] || 'duplicate')}`,
|
|
633
|
+
};
|
|
634
|
+
warnings.push(`Renamed duplicate skill ${item.skillId} to ${replacement.skillId}`);
|
|
635
|
+
result.push(replacement);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return { plans: result, warnings };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function summarize(results) {
|
|
642
|
+
return results.reduce((summary, item) => {
|
|
643
|
+
summary[item.status] = (summary[item.status] || 0) + 1;
|
|
644
|
+
return summary;
|
|
645
|
+
}, {});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function main() {
|
|
649
|
+
const args = parseArgs(process.argv);
|
|
650
|
+
if (args.help) {
|
|
651
|
+
printHelp();
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const projectRoot = findProjectRoot(args.projectRoot || process.cwd());
|
|
656
|
+
const skillsDir = path.isAbsolute(args.skillsDir)
|
|
657
|
+
? args.skillsDir
|
|
658
|
+
: path.join(projectRoot, args.skillsDir);
|
|
659
|
+
const yaml = loadYaml(projectRoot);
|
|
660
|
+
|
|
661
|
+
const corePlans = buildCorePlans(projectRoot, yaml);
|
|
662
|
+
const squadResult = buildSquadPlans(projectRoot, yaml);
|
|
663
|
+
const deduped = dedupePlans(corePlans.concat(squadResult.plans));
|
|
664
|
+
const plans = deduped.plans;
|
|
665
|
+
const warnings = squadResult.warnings.concat(deduped.warnings);
|
|
666
|
+
|
|
667
|
+
if (!plans.length) {
|
|
668
|
+
throw new Error('No skills were discovered. Run this from the project root that contains .aiox-core/ and squads/.');
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const options = {
|
|
672
|
+
...args,
|
|
673
|
+
projectRoot,
|
|
674
|
+
};
|
|
675
|
+
const results = plans.map((item) => writePlanItem(item, skillsDir, options));
|
|
676
|
+
const summary = summarize(results);
|
|
677
|
+
|
|
678
|
+
if (!args.quiet) {
|
|
679
|
+
console.log(`Project root: ${projectRoot}`);
|
|
680
|
+
console.log(`Skills dir: ${relative(projectRoot, skillsDir)}`);
|
|
681
|
+
console.log(`Mode: ${args.dryRun ? 'dry-run' : 'write'}`);
|
|
682
|
+
console.log('');
|
|
683
|
+
console.log(`Discovered: ${corePlans.length} core skills, ${squadResult.plans.length} squad entry skills`);
|
|
684
|
+
console.log(`Results: ${Object.keys(summary).map((key) => `${key}=${summary[key]}`).join(', ')}`);
|
|
685
|
+
|
|
686
|
+
if (warnings.length) {
|
|
687
|
+
console.log('');
|
|
688
|
+
console.log('Warnings:');
|
|
689
|
+
warnings.forEach((warning) => console.log(`- ${warning}`));
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (results.some((item) => item.status === 'skipped_existing')) {
|
|
693
|
+
console.log('');
|
|
694
|
+
console.log('Some existing non-generated skills were skipped. Re-run with --force only if you want backups and overwrite.');
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
console.log('');
|
|
698
|
+
console.log('Next steps:');
|
|
699
|
+
console.log('- Open or restart Codex CLI from this project root if the $ menu does not refresh immediately.');
|
|
700
|
+
console.log('- Use the aiox-* skill names to avoid matching unrelated global aios-* skills.');
|
|
701
|
+
console.log('- This script does not delete or modify global skills in ~/.codex/skills.');
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (require.main === module) {
|
|
706
|
+
try {
|
|
707
|
+
main();
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.error(`ERROR: ${error.message}`);
|
|
710
|
+
process.exit(1);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
module.exports = {
|
|
715
|
+
MARKER,
|
|
716
|
+
parseArgs,
|
|
717
|
+
findProjectRoot,
|
|
718
|
+
loadYaml,
|
|
719
|
+
buildCorePlans,
|
|
720
|
+
buildSquadPlans,
|
|
721
|
+
buildSkillContent,
|
|
722
|
+
coreSkillId,
|
|
723
|
+
squadSkillId,
|
|
724
|
+
resolveEntryAgent,
|
|
725
|
+
resolveSquadAlias,
|
|
726
|
+
parseAgentFile,
|
|
727
|
+
};
|