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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiox-core",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.8",
|
|
4
4
|
"description": "Synkra AIOX: AI-Orchestrated System for Full Stack Development - Core Framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aiox": "bin/aiox.js",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"packages/",
|
|
19
19
|
".aiox-core/",
|
|
20
20
|
".claude/CLAUDE.md",
|
|
21
|
+
".claude/commands/",
|
|
22
|
+
".claude/skills/",
|
|
21
23
|
".claude/rules/",
|
|
22
24
|
".claude/hooks/",
|
|
23
25
|
"pro/license/",
|
|
@@ -43,11 +45,16 @@
|
|
|
43
45
|
"README.md",
|
|
44
46
|
"LICENSE"
|
|
45
47
|
],
|
|
48
|
+
"exports": {
|
|
49
|
+
"./installer/pro-scaffolder": "./packages/installer/src/pro/pro-scaffolder.js",
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
46
52
|
"scripts": {
|
|
47
53
|
"format": "prettier --write \"**/*.md\"",
|
|
48
54
|
"test": "jest",
|
|
49
55
|
"test:watch": "jest --watch",
|
|
50
56
|
"test:coverage": "jest --coverage",
|
|
57
|
+
"test:e2e:installed-skills": "node scripts/e2e/installed-skills-smoke.js",
|
|
51
58
|
"test:health-check": "mocha tests/health-check/**/*.test.js --timeout 30000",
|
|
52
59
|
"lint": "eslint . --cache --cache-location .eslintcache",
|
|
53
60
|
"typecheck": "tsc --noEmit",
|
|
@@ -75,7 +82,10 @@
|
|
|
75
82
|
"validate:gemini-integration": "node .aiox-core/infrastructure/scripts/validate-gemini-integration.js",
|
|
76
83
|
"sync:skills:codex": "node .aiox-core/infrastructure/scripts/codex-skills-sync/index.js",
|
|
77
84
|
"sync:skills:codex:global": "node .aiox-core/infrastructure/scripts/codex-skills-sync/index.js --global --global-only",
|
|
85
|
+
"setup:codex-skills": "node .aiox-core/infrastructure/scripts/codex-skills-sync/bootstrap.js",
|
|
86
|
+
"setup:codex-skills:dry": "node .aiox-core/infrastructure/scripts/codex-skills-sync/bootstrap.js --dry-run",
|
|
78
87
|
"validate:codex-skills": "node .aiox-core/infrastructure/scripts/codex-skills-sync/validate.js --strict",
|
|
88
|
+
"repair:agent-references": "node .aiox-core/infrastructure/scripts/repair-agent-references.js",
|
|
79
89
|
"validate:paths": "node .aiox-core/infrastructure/scripts/validate-paths.js",
|
|
80
90
|
"validate:parity": "node .aiox-core/infrastructure/scripts/validate-parity.js",
|
|
81
91
|
"validate:semantic-lint": "node scripts/semantic-lint.js",
|
|
@@ -103,6 +113,7 @@
|
|
|
103
113
|
"handlebars": "^4.7.8",
|
|
104
114
|
"inquirer": "^8.2.6",
|
|
105
115
|
"js-yaml": "^4.1.0",
|
|
116
|
+
"node-machine-id": "^1.1.12",
|
|
106
117
|
"ora": "^5.4.1",
|
|
107
118
|
"picocolors": "^1.1.1",
|
|
108
119
|
"proper-lockfile": "^4.1.2",
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* aiox-pro CLI
|
|
5
5
|
*
|
|
6
|
-
* Thin CLI wrapper for
|
|
6
|
+
* Thin CLI wrapper for AIOX Pro packages.
|
|
7
7
|
* Provides a clean npx interface: npx aiox-pro install
|
|
8
8
|
*
|
|
9
9
|
* Commands:
|
|
10
|
-
* install Install
|
|
10
|
+
* install Install AIOX Pro in the current project
|
|
11
|
+
* update Update AIOX Pro and re-sync assets
|
|
11
12
|
* activate --key X Activate a license key
|
|
12
13
|
* deactivate Deactivate the current license
|
|
13
14
|
* status Show license status
|
|
@@ -22,7 +23,10 @@ const path = require('path');
|
|
|
22
23
|
const fs = require('fs');
|
|
23
24
|
const { recoverLicense } = require('../src/recover');
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
+
const PRO_PACKAGE_CANONICAL = '@aiox-squads/pro';
|
|
27
|
+
const PRO_PACKAGE_FALLBACK = '@aiox-fullstack/pro';
|
|
28
|
+
const PRO_PACKAGE_LEGACY = '@aios-fullstack/pro';
|
|
29
|
+
const PRO_PACKAGES = [PRO_PACKAGE_CANONICAL, PRO_PACKAGE_FALLBACK, PRO_PACKAGE_LEGACY];
|
|
26
30
|
const VERSION = require('../package.json').version;
|
|
27
31
|
|
|
28
32
|
const args = process.argv.slice(2);
|
|
@@ -42,8 +46,11 @@ function run(cmd, options = {}) {
|
|
|
42
46
|
|
|
43
47
|
function isProInstalled() {
|
|
44
48
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
return PRO_PACKAGES.some((packageName) => {
|
|
50
|
+
const scopeDir = packageName.split('/')[0];
|
|
51
|
+
const packageJson = path.join(process.cwd(), 'node_modules', scopeDir, 'pro', 'package.json');
|
|
52
|
+
return fs.existsSync(packageJson);
|
|
53
|
+
});
|
|
47
54
|
} catch {
|
|
48
55
|
return false;
|
|
49
56
|
}
|
|
@@ -133,7 +140,8 @@ Usage:
|
|
|
133
140
|
npx aiox-pro <command> [options]
|
|
134
141
|
|
|
135
142
|
Commands:
|
|
136
|
-
install Install
|
|
143
|
+
install Install AIOX Pro in the current project
|
|
144
|
+
update Update AIOX Pro and re-sync assets
|
|
137
145
|
install --wizard Install and run the setup wizard
|
|
138
146
|
setup, wizard Run Pro setup wizard (license gate + scaffold + verify)
|
|
139
147
|
activate --key KEY Activate a license key
|
|
@@ -147,6 +155,7 @@ Commands:
|
|
|
147
155
|
|
|
148
156
|
Examples:
|
|
149
157
|
npx aiox-pro install
|
|
158
|
+
npx aiox-pro update
|
|
150
159
|
npx aiox-pro setup
|
|
151
160
|
npx aiox-pro wizard --key PRO-XXXX-XXXX-XXXX-XXXX
|
|
152
161
|
npx aiox-pro activate --key PRO-XXXX-XXXX-XXXX-XXXX
|
|
@@ -158,16 +167,27 @@ Documentation: https://synkra.ai/pro/docs
|
|
|
158
167
|
}
|
|
159
168
|
|
|
160
169
|
function installPro() {
|
|
161
|
-
console.log(
|
|
170
|
+
console.log('\nInstalling AIOX Pro...\n');
|
|
171
|
+
|
|
172
|
+
let installedPackage = null;
|
|
162
173
|
|
|
163
|
-
const
|
|
174
|
+
for (const packageName of PRO_PACKAGES) {
|
|
175
|
+
console.log(`Trying ${packageName}...`);
|
|
176
|
+
const exitCode = run(`npm install ${packageName}`);
|
|
177
|
+
if (exitCode === 0) {
|
|
178
|
+
installedPackage = packageName;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
console.log('');
|
|
182
|
+
}
|
|
164
183
|
|
|
165
|
-
if (
|
|
166
|
-
console.error(
|
|
184
|
+
if (!installedPackage) {
|
|
185
|
+
console.error('\nFailed to install AIOX Pro.');
|
|
186
|
+
console.error(`Tried: ${PRO_PACKAGES.join(', ')}`);
|
|
167
187
|
process.exit(1);
|
|
168
188
|
}
|
|
169
189
|
|
|
170
|
-
console.log(`\n✅ ${
|
|
190
|
+
console.log(`\n✅ ${installedPackage} installed successfully!\n`);
|
|
171
191
|
console.log('Next steps:');
|
|
172
192
|
console.log(' npx aiox-pro activate --key PRO-XXXX-XXXX-XXXX-XXXX');
|
|
173
193
|
console.log(' npx aiox-pro status');
|
|
@@ -218,8 +238,9 @@ switch (command) {
|
|
|
218
238
|
case 'status':
|
|
219
239
|
case 'features':
|
|
220
240
|
case 'validate':
|
|
241
|
+
case 'update':
|
|
221
242
|
if (!isProInstalled()) {
|
|
222
|
-
console.error(
|
|
243
|
+
console.error('AIOX Pro is not installed.');
|
|
223
244
|
console.error('Run first: npx aiox-pro install\n');
|
|
224
245
|
process.exit(1);
|
|
225
246
|
}
|
|
@@ -24,6 +24,68 @@ const {
|
|
|
24
24
|
} = require('./validation/config-validator');
|
|
25
25
|
const { getMergeStrategy, hasMergeStrategy } = require('../merger/index.js');
|
|
26
26
|
|
|
27
|
+
function isBrownfieldProjectType(projectType) {
|
|
28
|
+
const normalized = String(projectType || '').toLowerCase();
|
|
29
|
+
return normalized === 'brownfield' || normalized === 'existing_aiox' || normalized === 'existing-aiox';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function resolveFileAction(filePath, options = {}) {
|
|
33
|
+
const {
|
|
34
|
+
skipPrompts = false,
|
|
35
|
+
forceMerge = false,
|
|
36
|
+
noMerge = false,
|
|
37
|
+
projectType = 'greenfield',
|
|
38
|
+
message,
|
|
39
|
+
} = options;
|
|
40
|
+
|
|
41
|
+
const exists = await fs.pathExists(filePath);
|
|
42
|
+
if (!exists) {
|
|
43
|
+
return 'create';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const isBrownfield = isBrownfieldProjectType(projectType);
|
|
47
|
+
const canMerge = !noMerge && hasMergeStrategy(filePath);
|
|
48
|
+
|
|
49
|
+
if (forceMerge && canMerge) {
|
|
50
|
+
return 'merge';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (skipPrompts) {
|
|
54
|
+
return isBrownfield && canMerge ? 'merge' : 'overwrite';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const choices = [];
|
|
58
|
+
|
|
59
|
+
if (canMerge) {
|
|
60
|
+
choices.push({
|
|
61
|
+
value: 'merge',
|
|
62
|
+
label: 'Merge (preserve existing customizations)',
|
|
63
|
+
hint: isBrownfield ? 'recommended' : '',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
choices.push(
|
|
68
|
+
{ value: 'backup', label: 'Backup and overwrite' },
|
|
69
|
+
{ value: 'overwrite', label: 'Overwrite completely' },
|
|
70
|
+
{ value: 'skip', label: 'Skip (keep existing)' },
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
let action = await select({
|
|
74
|
+
message,
|
|
75
|
+
options: choices,
|
|
76
|
+
initialValue: isBrownfield && canMerge ? 'merge' : 'backup',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (action === 'backup') {
|
|
80
|
+
const backupPath = `${filePath}.backup.${Date.now()}`;
|
|
81
|
+
await fs.copy(filePath, backupPath);
|
|
82
|
+
console.log(`✅ Backup created: ${backupPath}`);
|
|
83
|
+
action = 'overwrite';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return action;
|
|
87
|
+
}
|
|
88
|
+
|
|
27
89
|
/**
|
|
28
90
|
* Configure environment files (.env and core-config.yaml)
|
|
29
91
|
*
|
|
@@ -62,50 +124,16 @@ async function configureEnvironment(options = {}) {
|
|
|
62
124
|
// Step 1: Check for existing .env and handle with merge/backup/overwrite
|
|
63
125
|
const envPath = path.join(targetDir, '.env');
|
|
64
126
|
const envExists = await fs.pathExists(envPath);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
} else if (skipPrompts) {
|
|
76
|
-
// Quiet mode: default to merge for brownfield, overwrite for greenfield
|
|
77
|
-
envAction = isBrownfield && canMerge ? 'merge' : 'overwrite';
|
|
78
|
-
} else {
|
|
79
|
-
// Interactive mode: Offer merge option for brownfield projects
|
|
80
|
-
const choices = [];
|
|
81
|
-
|
|
82
|
-
if (canMerge) {
|
|
83
|
-
choices.push({
|
|
84
|
-
value: 'merge',
|
|
85
|
-
label: 'Merge (add new variables, keep existing)',
|
|
86
|
-
hint: isBrownfield ? 'recommended' : '',
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
choices.push(
|
|
91
|
-
{ value: 'backup', label: 'Backup and overwrite' },
|
|
92
|
-
{ value: 'overwrite', label: 'Overwrite completely' },
|
|
93
|
-
{ value: 'skip', label: 'Skip (keep existing)' },
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
envAction = await select({
|
|
97
|
-
message: 'Found existing .env file. What would you like to do?',
|
|
98
|
-
options: choices,
|
|
99
|
-
initialValue: isBrownfield && canMerge ? 'merge' : 'backup',
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
if (envAction === 'backup') {
|
|
103
|
-
const backupPath = path.join(targetDir, `.env.backup.${Date.now()}`);
|
|
104
|
-
await fs.copy(envPath, backupPath);
|
|
105
|
-
console.log(`✅ Backup created: ${backupPath}`);
|
|
106
|
-
envAction = 'overwrite';
|
|
107
|
-
}
|
|
108
|
-
}
|
|
127
|
+
const envAction = await resolveFileAction(envPath, {
|
|
128
|
+
skipPrompts,
|
|
129
|
+
forceMerge,
|
|
130
|
+
noMerge,
|
|
131
|
+
projectType,
|
|
132
|
+
message: 'Found existing .env file. What would you like to do?',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (envAction === 'merge' && envExists && forceMerge) {
|
|
136
|
+
console.log('🔀 Using merge mode (--merge flag)');
|
|
109
137
|
}
|
|
110
138
|
|
|
111
139
|
// Step 2: API keys are configured later via .env or aiox-master
|
|
@@ -158,9 +186,29 @@ async function configureEnvironment(options = {}) {
|
|
|
158
186
|
// Step 4: Generate and write .env.example
|
|
159
187
|
const envExamplePath = path.join(targetDir, '.env.example');
|
|
160
188
|
const envExampleContent = generateEnvExample();
|
|
161
|
-
await
|
|
162
|
-
|
|
163
|
-
|
|
189
|
+
const envExampleAction = await resolveFileAction(envExamplePath, {
|
|
190
|
+
skipPrompts,
|
|
191
|
+
forceMerge,
|
|
192
|
+
noMerge,
|
|
193
|
+
projectType,
|
|
194
|
+
message: 'Found existing .env.example file. What would you like to do?',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (envExampleAction === 'skip') {
|
|
198
|
+
console.log('⏭️ Skipped .env.example file (keeping existing)');
|
|
199
|
+
} else if (envExampleAction === 'merge' && await fs.pathExists(envExamplePath)) {
|
|
200
|
+
const existingContent = await fs.readFile(envExamplePath, 'utf8');
|
|
201
|
+
const merger = getMergeStrategy(envExamplePath);
|
|
202
|
+
const mergeResult = await merger.merge(existingContent, envExampleContent);
|
|
203
|
+
|
|
204
|
+
await fs.writeFile(envExamplePath, mergeResult.content, { encoding: 'utf8' });
|
|
205
|
+
results.envExampleCreated = true;
|
|
206
|
+
console.log('✅ Merged .env.example file');
|
|
207
|
+
} else {
|
|
208
|
+
await fs.writeFile(envExamplePath, envExampleContent, { encoding: 'utf8' });
|
|
209
|
+
results.envExampleCreated = true;
|
|
210
|
+
console.log('✅ Created .env.example file');
|
|
211
|
+
}
|
|
164
212
|
|
|
165
213
|
// Step 5: Update .gitignore
|
|
166
214
|
await updateGitignore(targetDir);
|
|
@@ -194,9 +242,29 @@ async function configureEnvironment(options = {}) {
|
|
|
194
242
|
}
|
|
195
243
|
|
|
196
244
|
const coreConfigPath = path.join(coreConfigDir, 'core-config.yaml');
|
|
197
|
-
await
|
|
198
|
-
|
|
199
|
-
|
|
245
|
+
const coreConfigAction = await resolveFileAction(coreConfigPath, {
|
|
246
|
+
skipPrompts,
|
|
247
|
+
forceMerge,
|
|
248
|
+
noMerge,
|
|
249
|
+
projectType,
|
|
250
|
+
message: 'Found existing .aiox-core/core-config.yaml. What would you like to do?',
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
if (coreConfigAction === 'skip') {
|
|
254
|
+
console.log('⏭️ Skipped .aiox-core/core-config.yaml (keeping existing)');
|
|
255
|
+
} else if (coreConfigAction === 'merge' && await fs.pathExists(coreConfigPath)) {
|
|
256
|
+
const existingContent = await fs.readFile(coreConfigPath, 'utf8');
|
|
257
|
+
const merger = getMergeStrategy(coreConfigPath);
|
|
258
|
+
const mergeResult = await merger.merge(coreConfigContent, existingContent);
|
|
259
|
+
|
|
260
|
+
await fs.writeFile(coreConfigPath, mergeResult.content, { encoding: 'utf8' });
|
|
261
|
+
results.coreConfigCreated = true;
|
|
262
|
+
console.log('✅ Merged .aiox-core/core-config.yaml');
|
|
263
|
+
} else {
|
|
264
|
+
await fs.writeFile(coreConfigPath, coreConfigContent, { encoding: 'utf8' });
|
|
265
|
+
results.coreConfigCreated = true;
|
|
266
|
+
console.log('✅ Created .aiox-core/core-config.yaml');
|
|
267
|
+
}
|
|
200
268
|
|
|
201
269
|
return results;
|
|
202
270
|
} catch (error) {
|
|
@@ -12,6 +12,7 @@ const fs = require('fs-extra');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const ora = require('ora');
|
|
14
14
|
const { hashFile } = require('./file-hasher');
|
|
15
|
+
const { loadSourceManifest, updateInstalledManifest } = require('./brownfield-upgrader');
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Get the path to the source .aiox-core directory in the package
|
|
@@ -70,6 +71,59 @@ const ROOT_FILES_TO_COPY = [
|
|
|
70
71
|
'working-in-the-brownfield.md',
|
|
71
72
|
];
|
|
72
73
|
|
|
74
|
+
const BROWNFIELD_PRESERVE_PATTERNS = [
|
|
75
|
+
/^core-config\.yaml$/,
|
|
76
|
+
/^development\/agents\/[^/]+\/MEMORY\.md$/,
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
function isBrownfieldProjectType(projectType = '') {
|
|
80
|
+
const normalized = String(projectType).toLowerCase();
|
|
81
|
+
return normalized === 'brownfield' || normalized === 'existing_aiox' || normalized === 'existing-aiox';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function shouldPreserveExistingFile(relativePath, options = {}) {
|
|
85
|
+
if (!options.preserveExisting) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return BROWNFIELD_PRESERVE_PATTERNS.some((pattern) => pattern.test(relativePath));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function extractManifestFileHashes(manifest) {
|
|
93
|
+
if (!manifest || !Array.isArray(manifest.files)) {
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const fileHashes = {};
|
|
98
|
+
for (const entry of manifest.files) {
|
|
99
|
+
if (entry && entry.path && entry.hash) {
|
|
100
|
+
fileHashes[entry.path] = entry.hash;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return fileHashes;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function copyManifestArtifacts(sourceDir, targetAioxCore) {
|
|
108
|
+
const manifestPath = path.join(sourceDir, 'install-manifest.yaml');
|
|
109
|
+
const signaturePath = manifestPath + '.minisig';
|
|
110
|
+
const targetSignaturePath = path.join(targetAioxCore, 'install-manifest.yaml.minisig');
|
|
111
|
+
|
|
112
|
+
if (await fs.pathExists(manifestPath)) {
|
|
113
|
+
await fs.copy(manifestPath, path.join(targetAioxCore, 'install-manifest.yaml'), {
|
|
114
|
+
overwrite: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (await fs.pathExists(signaturePath)) {
|
|
119
|
+
await fs.copy(signaturePath, targetSignaturePath, {
|
|
120
|
+
overwrite: true,
|
|
121
|
+
});
|
|
122
|
+
} else if (await fs.pathExists(targetSignaturePath)) {
|
|
123
|
+
await fs.remove(targetSignaturePath);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
73
127
|
/**
|
|
74
128
|
* Replace {root} placeholder in file content
|
|
75
129
|
* @param {string} content - File content
|
|
@@ -128,9 +182,10 @@ async function generateVersionJson(options) {
|
|
|
128
182
|
version,
|
|
129
183
|
installedFiles,
|
|
130
184
|
mode = 'project-development',
|
|
185
|
+
fileHashes: providedFileHashes = null,
|
|
131
186
|
} = options;
|
|
132
187
|
|
|
133
|
-
const fileHashes = await generateFileHashes(targetAioxCore, installedFiles);
|
|
188
|
+
const fileHashes = providedFileHashes || await generateFileHashes(targetAioxCore, installedFiles);
|
|
134
189
|
|
|
135
190
|
const versionJson = {
|
|
136
191
|
version,
|
|
@@ -153,8 +208,14 @@ async function generateVersionJson(options) {
|
|
|
153
208
|
* @param {boolean} replaceRoot - Whether to replace {root} placeholders
|
|
154
209
|
* @returns {Promise<boolean>} Success status
|
|
155
210
|
*/
|
|
156
|
-
async function copyFileWithRootReplacement(sourcePath, destPath, replaceRoot = true) {
|
|
211
|
+
async function copyFileWithRootReplacement(sourcePath, destPath, replaceRoot = true, options = {}) {
|
|
157
212
|
try {
|
|
213
|
+
if (options.relativePath && shouldPreserveExistingFile(options.relativePath, options)) {
|
|
214
|
+
if (await fs.pathExists(destPath)) {
|
|
215
|
+
return { copied: false, preserved: true, relativePath: options.relativePath };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
158
219
|
await fs.ensureDir(path.dirname(destPath));
|
|
159
220
|
|
|
160
221
|
// Check if file needs {root} replacement (.md, .yaml, .yml)
|
|
@@ -169,10 +230,10 @@ async function copyFileWithRootReplacement(sourcePath, destPath, replaceRoot = t
|
|
|
169
230
|
await fs.copy(sourcePath, destPath);
|
|
170
231
|
}
|
|
171
232
|
|
|
172
|
-
return true;
|
|
233
|
+
return { copied: true, preserved: false, relativePath: options.relativePath || null };
|
|
173
234
|
} catch (error) {
|
|
174
235
|
console.error(`Failed to copy ${sourcePath}: ${error.message}`);
|
|
175
|
-
return false;
|
|
236
|
+
return { copied: false, preserved: false, relativePath: options.relativePath || null };
|
|
176
237
|
}
|
|
177
238
|
}
|
|
178
239
|
|
|
@@ -183,7 +244,7 @@ async function copyFileWithRootReplacement(sourcePath, destPath, replaceRoot = t
|
|
|
183
244
|
* @param {Function} onProgress - Progress callback
|
|
184
245
|
* @returns {Promise<string[]>} List of copied files (relative paths)
|
|
185
246
|
*/
|
|
186
|
-
async function copyDirectoryWithRootReplacement(sourceDir, destDir, onProgress = null) {
|
|
247
|
+
async function copyDirectoryWithRootReplacement(sourceDir, destDir, onProgress = null, options = {}) {
|
|
187
248
|
const copiedFiles = [];
|
|
188
249
|
|
|
189
250
|
if (!await fs.pathExists(sourceDir)) {
|
|
@@ -205,15 +266,28 @@ async function copyDirectoryWithRootReplacement(sourceDir, destDir, onProgress =
|
|
|
205
266
|
}
|
|
206
267
|
|
|
207
268
|
if (item.isDirectory()) {
|
|
208
|
-
const subFiles = await copyDirectoryWithRootReplacement(sourcePath, destPath, onProgress
|
|
269
|
+
const subFiles = await copyDirectoryWithRootReplacement(sourcePath, destPath, onProgress, {
|
|
270
|
+
...options,
|
|
271
|
+
baseDir: options.baseDir || destDir,
|
|
272
|
+
});
|
|
209
273
|
copiedFiles.push(...subFiles);
|
|
210
274
|
} else {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
275
|
+
const baseDir = options.baseDir || destDir;
|
|
276
|
+
const relativePath = path.relative(baseDir, destPath).replace(/\\/g, '/');
|
|
277
|
+
const fullRelativePath = options.pathPrefix
|
|
278
|
+
? path.posix.join(options.pathPrefix, relativePath)
|
|
279
|
+
: relativePath;
|
|
280
|
+
const result = await copyFileWithRootReplacement(sourcePath, destPath, true, {
|
|
281
|
+
...options,
|
|
282
|
+
relativePath: fullRelativePath,
|
|
283
|
+
});
|
|
284
|
+
if (result.copied) {
|
|
285
|
+
copiedFiles.push(relativePath);
|
|
214
286
|
if (onProgress) {
|
|
215
287
|
onProgress({ file: item.name, copied: true });
|
|
216
288
|
}
|
|
289
|
+
} else if (result.preserved && onProgress) {
|
|
290
|
+
onProgress({ file: item.name, copied: false, preserved: true });
|
|
217
291
|
}
|
|
218
292
|
}
|
|
219
293
|
}
|
|
@@ -237,6 +311,9 @@ async function installAioxCore(options = {}) {
|
|
|
237
311
|
const {
|
|
238
312
|
targetDir = process.cwd(),
|
|
239
313
|
onProgress = null,
|
|
314
|
+
projectType = 'greenfield',
|
|
315
|
+
sourceDir: providedSourceDir = null,
|
|
316
|
+
packageVersion: providedPackageVersion = null,
|
|
240
317
|
} = options;
|
|
241
318
|
|
|
242
319
|
const result = {
|
|
@@ -249,8 +326,9 @@ async function installAioxCore(options = {}) {
|
|
|
249
326
|
const spinner = ora('Installing AIOX core framework...').start();
|
|
250
327
|
|
|
251
328
|
try {
|
|
252
|
-
const sourceDir = getAioxCoreSourcePath();
|
|
329
|
+
const sourceDir = providedSourceDir || getAioxCoreSourcePath();
|
|
253
330
|
const targetAioxCore = path.join(targetDir, '.aiox-core');
|
|
331
|
+
const preserveExisting = isBrownfieldProjectType(projectType);
|
|
254
332
|
|
|
255
333
|
// Check if source exists
|
|
256
334
|
if (!await fs.pathExists(sourceDir)) {
|
|
@@ -272,6 +350,11 @@ async function installAioxCore(options = {}) {
|
|
|
272
350
|
folderSource,
|
|
273
351
|
folderDest,
|
|
274
352
|
onProgress,
|
|
353
|
+
{
|
|
354
|
+
baseDir: folderDest,
|
|
355
|
+
pathPrefix: folder,
|
|
356
|
+
preserveExisting,
|
|
357
|
+
},
|
|
275
358
|
);
|
|
276
359
|
|
|
277
360
|
if (copiedFiles.length > 0) {
|
|
@@ -288,28 +371,37 @@ async function installAioxCore(options = {}) {
|
|
|
288
371
|
|
|
289
372
|
if (await fs.pathExists(fileSource)) {
|
|
290
373
|
spinner.text = `Copying ${file}...`;
|
|
291
|
-
const
|
|
292
|
-
|
|
374
|
+
const relativePath = file;
|
|
375
|
+
const copyResult = await copyFileWithRootReplacement(fileSource, fileDest, true, {
|
|
376
|
+
relativePath,
|
|
377
|
+
preserveExisting,
|
|
378
|
+
});
|
|
379
|
+
if (copyResult.copied) {
|
|
293
380
|
result.installedFiles.push(file);
|
|
294
381
|
}
|
|
295
382
|
}
|
|
296
383
|
}
|
|
297
384
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const packageVersion = require('../../../../package.json').version;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
385
|
+
const sourceManifest = loadSourceManifest(sourceDir);
|
|
386
|
+
const manifestFileHashes = extractManifestFileHashes(sourceManifest);
|
|
387
|
+
const packageVersion = providedPackageVersion || require('../../../../package.json').version;
|
|
388
|
+
|
|
389
|
+
spinner.text = 'Copying installation manifest...';
|
|
390
|
+
await copyManifestArtifacts(sourceDir, targetAioxCore);
|
|
391
|
+
if (!sourceManifest) {
|
|
392
|
+
const manifest = {
|
|
393
|
+
version: packageVersion,
|
|
394
|
+
installed_at: new Date().toISOString(),
|
|
395
|
+
install_type: 'full',
|
|
396
|
+
files: result.installedFiles,
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
await fs.writeFile(
|
|
400
|
+
path.join(targetAioxCore, 'install-manifest.yaml'),
|
|
401
|
+
require('js-yaml').dump(manifest),
|
|
402
|
+
'utf8',
|
|
403
|
+
);
|
|
404
|
+
}
|
|
313
405
|
|
|
314
406
|
// Story 7.2: Create version.json with file hashes for update tracking
|
|
315
407
|
spinner.text = 'Generating version tracking info...';
|
|
@@ -318,9 +410,14 @@ async function installAioxCore(options = {}) {
|
|
|
318
410
|
version: packageVersion,
|
|
319
411
|
installedFiles: result.installedFiles,
|
|
320
412
|
mode: 'project-development',
|
|
413
|
+
fileHashes: Object.keys(manifestFileHashes).length > 0 ? manifestFileHashes : null,
|
|
321
414
|
});
|
|
322
415
|
result.versionInfo = versionInfo;
|
|
323
416
|
|
|
417
|
+
if (sourceManifest) {
|
|
418
|
+
updateInstalledManifest(targetDir, sourceManifest, `aiox-core@${packageVersion}`);
|
|
419
|
+
}
|
|
420
|
+
|
|
324
421
|
// BUG-2 fix (INS-1): Install .aiox-core dependencies after copy
|
|
325
422
|
// The copied .aiox-core/package.json has dependencies (js-yaml, execa, etc.)
|
|
326
423
|
// that must be installed for the activation pipeline to work
|