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
|
@@ -21,6 +21,7 @@ const fs = require('fs');
|
|
|
21
21
|
const path = require('path');
|
|
22
22
|
const {
|
|
23
23
|
generateMachineId,
|
|
24
|
+
generateMachineIdLegacy,
|
|
24
25
|
generateSalt,
|
|
25
26
|
deriveCacheKey,
|
|
26
27
|
encrypt,
|
|
@@ -44,6 +45,9 @@ const CONFIG = {
|
|
|
44
45
|
|
|
45
46
|
// Cache version for migration support
|
|
46
47
|
CACHE_VERSION: 1,
|
|
48
|
+
|
|
49
|
+
// Legacy cache fallback window for hostname/CPU/MAC machine IDs.
|
|
50
|
+
LEGACY_MACHINE_ID_FALLBACK_DAYS: 90,
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
/**
|
|
@@ -168,6 +172,112 @@ function writeLicenseCache(data, baseDir) {
|
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Build the canonical HMAC payload for encrypted cache content.
|
|
177
|
+
*
|
|
178
|
+
* @param {object} cacheFile - Parsed encrypted cache file
|
|
179
|
+
* @returns {string} JSON payload used for HMAC verification
|
|
180
|
+
*/
|
|
181
|
+
function buildHmacData(cacheFile) {
|
|
182
|
+
return JSON.stringify({
|
|
183
|
+
ciphertext: cacheFile.encrypted,
|
|
184
|
+
iv: cacheFile.iv,
|
|
185
|
+
tag: cacheFile.tag,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Validate encrypted cache file structure.
|
|
191
|
+
*
|
|
192
|
+
* @param {object} cacheFile - Parsed encrypted cache file
|
|
193
|
+
* @returns {boolean} true when required fields exist
|
|
194
|
+
*/
|
|
195
|
+
function hasValidCacheStructure(cacheFile) {
|
|
196
|
+
return Boolean(
|
|
197
|
+
cacheFile
|
|
198
|
+
&& cacheFile.encrypted
|
|
199
|
+
&& cacheFile.iv
|
|
200
|
+
&& cacheFile.tag
|
|
201
|
+
&& cacheFile.hmac
|
|
202
|
+
&& cacheFile.salt,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Try to decrypt a cache file with a specific machine ID.
|
|
208
|
+
*
|
|
209
|
+
* @param {object} cacheFile - Parsed encrypted cache file
|
|
210
|
+
* @param {string} machineId - Machine ID candidate for key derivation
|
|
211
|
+
* @returns {object|null} Decrypted cache data or null
|
|
212
|
+
*/
|
|
213
|
+
function decryptCacheWithMachineId(cacheFile, machineId) {
|
|
214
|
+
try {
|
|
215
|
+
const salt = Buffer.from(cacheFile.salt, 'hex');
|
|
216
|
+
const key = deriveCacheKey(machineId, salt);
|
|
217
|
+
const hmacData = buildHmacData(cacheFile);
|
|
218
|
+
|
|
219
|
+
if (!verifyHMAC(hmacData, key, cacheFile.hmac)) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const decrypted = decrypt(
|
|
224
|
+
{
|
|
225
|
+
ciphertext: cacheFile.encrypted,
|
|
226
|
+
iv: cacheFile.iv,
|
|
227
|
+
tag: cacheFile.tag,
|
|
228
|
+
},
|
|
229
|
+
key,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
if (decrypted.machineId !== machineId) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return decrypted;
|
|
237
|
+
} catch {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Check whether a legacy machine-ID cache is still inside its migration window.
|
|
244
|
+
*
|
|
245
|
+
* @param {object} cache - Decrypted legacy cache data
|
|
246
|
+
* @returns {boolean} true if fallback may still be used
|
|
247
|
+
*/
|
|
248
|
+
function isLegacyFallbackAllowed(cache) {
|
|
249
|
+
if (!cache || !cache.activatedAt) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const activatedAt = new Date(cache.activatedAt);
|
|
254
|
+
const activatedAtMs = activatedAt.getTime();
|
|
255
|
+
|
|
256
|
+
if (!Number.isFinite(activatedAtMs)) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const fallbackMs = CONFIG.LEGACY_MACHINE_ID_FALLBACK_DAYS * 24 * 60 * 60 * 1000;
|
|
261
|
+
return Date.now() - activatedAtMs <= fallbackMs;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Re-encrypt a legacy cache with the new native machine-ID key.
|
|
266
|
+
*
|
|
267
|
+
* @param {object} cache - Decrypted legacy cache data
|
|
268
|
+
* @param {string} [baseDir] - Optional base directory override
|
|
269
|
+
* @returns {{ success: boolean, error?: string }} Migration result
|
|
270
|
+
*/
|
|
271
|
+
function migrateCacheKey(cache, baseDir) {
|
|
272
|
+
const result = writeLicenseCache(cache, baseDir);
|
|
273
|
+
|
|
274
|
+
if (result.success) {
|
|
275
|
+
console.info('[aiox-pro] Cache migrated to new machine_id format');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
|
|
171
281
|
/**
|
|
172
282
|
* Read and verify license cache from disk.
|
|
173
283
|
*
|
|
@@ -195,42 +305,34 @@ function readLicenseCache(baseDir) {
|
|
|
195
305
|
const cacheFile = JSON.parse(fileContent);
|
|
196
306
|
|
|
197
307
|
// Validate structure
|
|
198
|
-
if (!cacheFile
|
|
308
|
+
if (!hasValidCacheStructure(cacheFile)) {
|
|
199
309
|
return null;
|
|
200
310
|
}
|
|
201
311
|
|
|
202
|
-
//
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
312
|
+
// First try the legacy key so existing caches can migrate in place.
|
|
313
|
+
const legacyMachineId = generateMachineIdLegacy();
|
|
314
|
+
const legacyCache = decryptCacheWithMachineId(cacheFile, legacyMachineId);
|
|
315
|
+
|
|
316
|
+
if (legacyCache) {
|
|
317
|
+
if (!isLegacyFallbackAllowed(legacyCache)) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const migrationResult = migrateCacheKey(legacyCache, baseDir);
|
|
322
|
+
if (migrationResult.success) {
|
|
323
|
+
return {
|
|
324
|
+
...legacyCache,
|
|
325
|
+
machineId: generateMachineId(),
|
|
326
|
+
version: CONFIG.CACHE_VERSION,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return legacyCache;
|
|
217
331
|
}
|
|
218
332
|
|
|
219
|
-
//
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
iv: cacheFile.iv,
|
|
223
|
-
tag: cacheFile.tag,
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const decrypted = decrypt(encryptedData, key);
|
|
227
|
-
|
|
228
|
-
// Verify machine ID matches (defense in depth)
|
|
229
|
-
if (decrypted.machineId !== machineId) {
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return decrypted;
|
|
333
|
+
// Then try the new native machine-ID key for migrated and fresh caches.
|
|
334
|
+
const machineId = generateMachineId();
|
|
335
|
+
return decryptCacheWithMachineId(cacheFile, machineId);
|
|
234
336
|
} catch {
|
|
235
337
|
// Any error (parse, decrypt, etc.) means cache is invalid
|
|
236
338
|
return null;
|
|
@@ -519,5 +621,7 @@ module.exports = {
|
|
|
519
621
|
getAioxDir,
|
|
520
622
|
|
|
521
623
|
// Exported for testing
|
|
624
|
+
migrateCacheKey,
|
|
625
|
+
isLegacyFallbackAllowed,
|
|
522
626
|
_CONFIG: CONFIG,
|
|
523
627
|
};
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const crypto = require('crypto');
|
|
18
18
|
const os = require('os');
|
|
19
|
+
const { machineIdSync } = require('node-machine-id');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Configuration constants for cryptographic operations.
|
|
@@ -40,14 +41,32 @@ const CONFIG = {
|
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
+
* Namespace the raw OS UUID before hashing so this format cannot collide with
|
|
45
|
+
* legacy hostname/CPU/MAC fingerprints that happen to produce the same text.
|
|
46
|
+
*/
|
|
47
|
+
const MACHINE_ID_HASH_PREFIX = 'aiox-pro-native-machine-id:v1:';
|
|
48
|
+
|
|
49
|
+
let cachedMachineId = null;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Hash machine identity material without exposing the raw OS UUID.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} value - Raw machine identity material
|
|
55
|
+
* @returns {string} SHA-256 hex digest
|
|
56
|
+
*/
|
|
57
|
+
function hashMachineId(value) {
|
|
58
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Generate the legacy deterministic machine identifier.
|
|
44
63
|
*
|
|
45
64
|
* Combines hostname + CPU model + first MAC address to create
|
|
46
65
|
* a unique but reproducible identifier for this machine.
|
|
47
66
|
*
|
|
48
67
|
* @returns {string} SHA-256 hash of machine fingerprint components
|
|
49
68
|
*/
|
|
50
|
-
function
|
|
69
|
+
function generateMachineIdLegacy() {
|
|
51
70
|
const components = [];
|
|
52
71
|
|
|
53
72
|
// Hostname
|
|
@@ -77,7 +96,42 @@ function generateMachineId() {
|
|
|
77
96
|
|
|
78
97
|
// Create deterministic hash
|
|
79
98
|
const fingerprint = components.join('|');
|
|
80
|
-
return
|
|
99
|
+
return hashMachineId(fingerprint);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Generate a stable deterministic machine identifier.
|
|
104
|
+
*
|
|
105
|
+
* Uses the OS native machine UUID instead of network-interface metadata so the
|
|
106
|
+
* result survives Wi-Fi MAC rotation, VPN changes, and interface ordering.
|
|
107
|
+
*
|
|
108
|
+
* @returns {string} SHA-256 hash of native machine UUID
|
|
109
|
+
*/
|
|
110
|
+
function generateMachineId() {
|
|
111
|
+
if (cachedMachineId) {
|
|
112
|
+
return cachedMachineId;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const nativeMachineId = machineIdSync(true);
|
|
117
|
+
|
|
118
|
+
if (!nativeMachineId || typeof nativeMachineId !== 'string') {
|
|
119
|
+
throw new Error('Native machine id unavailable');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
cachedMachineId = hashMachineId(`${MACHINE_ID_HASH_PREFIX}${nativeMachineId}`);
|
|
123
|
+
return cachedMachineId;
|
|
124
|
+
} catch {
|
|
125
|
+
cachedMachineId = generateMachineIdLegacy();
|
|
126
|
+
return cachedMachineId;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Reset cached machine ID. Intended for tests that mock the machine-id provider.
|
|
132
|
+
*/
|
|
133
|
+
function _resetMachineIdCacheForTests() {
|
|
134
|
+
cachedMachineId = null;
|
|
81
135
|
}
|
|
82
136
|
|
|
83
137
|
/**
|
|
@@ -299,5 +353,7 @@ module.exports = {
|
|
|
299
353
|
validateKeyFormat,
|
|
300
354
|
|
|
301
355
|
// Exported for testing
|
|
356
|
+
generateMachineIdLegacy,
|
|
357
|
+
_resetMachineIdCacheForTests,
|
|
302
358
|
_CONFIG: CONFIG,
|
|
303
359
|
};
|
package/pro/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "@aiox-
|
|
3
|
-
"version": "0.
|
|
2
|
+
"name": "@aiox-squads/pro",
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "AIOX Pro: Premium features for AIOX Framework",
|
|
5
5
|
"private": false,
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"
|
|
7
|
+
"node-machine-id": "^1.1.12",
|
|
8
|
+
"yaml": "^2.8.4"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"validate:publish-surface": "node scripts/validate-publish-surface.js"
|
|
11
12
|
},
|
|
12
13
|
"peerDependencies": {
|
|
13
|
-
"aiox-core": ">=5.0.
|
|
14
|
+
"aiox-core": ">=5.0.8"
|
|
14
15
|
},
|
|
15
16
|
"engines": {
|
|
16
17
|
"node": ">=18"
|
package/pro/squads/README.md
CHANGED
|
@@ -4,25 +4,25 @@ Pre-built agent squads installed during `npx aiox-core install` after Pro licens
|
|
|
4
4
|
|
|
5
5
|
## Available Squads
|
|
6
6
|
|
|
7
|
-
| Squad
|
|
8
|
-
|
|
9
|
-
| **aiox-sop**
|
|
10
|
-
| **brand**
|
|
11
|
-
| **claude-code-mastery** | 8
|
|
12
|
-
| **data**
|
|
13
|
-
| **db-sage**
|
|
14
|
-
| **squad-creator-pro**
|
|
15
|
-
| **design**
|
|
16
|
-
| **etl-ops**
|
|
17
|
-
| **hormozi**
|
|
18
|
-
| **spy**
|
|
19
|
-
| **squad-creator**
|
|
20
|
-
| **storytelling**
|
|
7
|
+
| Squad | Agents | Description |
|
|
8
|
+
| ----------------------- | ------ | ------------------------------------------------------------------------------ |
|
|
9
|
+
| **aiox-sop** | 6 | SOP factory, audit, extraction, and machine-readable operational playbooks |
|
|
10
|
+
| **brand** | 16 | Naming, identity, positioning, narrative, and brand activation systems |
|
|
11
|
+
| **claude-code-mastery** | 8 | Claude Code setup, hooks, MCP, swarm orchestration, and integration guardrails |
|
|
12
|
+
| **data** | 7 | Data intelligence, analytics, segmentation, and measurement workflows |
|
|
13
|
+
| **db-sage** | 1 | PostgreSQL and Supabase architecture, migrations, RLS, and query optimization |
|
|
14
|
+
| **squad-creator-pro** | 6 | Meta-squad for creating AI agent squads based on real elite minds |
|
|
15
|
+
| **design** | 8 | Design System squad — tokens, components, accessibility, DesignOps |
|
|
16
|
+
| **etl-ops** | 3 | Extraction and transformation pipelines for local and remote content sources |
|
|
17
|
+
| **hormozi** | 16 | Offers, leads, ads, pricing, retention, and growth systems inspired by Hormozi |
|
|
18
|
+
| **spy** | 3 | Competitive intelligence, viral content analysis, and benchmark workflows |
|
|
19
|
+
| **squad-creator** | 1 | Core squad creation, evolution, validation, and ecosystem orchestration |
|
|
20
|
+
| **storytelling** | 13 | Narrative systems, pitches, storytelling frameworks, and brand communication |
|
|
21
21
|
|
|
22
22
|
## How It Works
|
|
23
23
|
|
|
24
24
|
1. User runs `npx aiox-core install` and activates Pro license
|
|
25
|
-
2. `@aiox-
|
|
25
|
+
2. `@aiox-squads/pro` npm package is installed
|
|
26
26
|
3. Scaffolder copies all squads from this directory into the user's project `squads/`
|
|
27
27
|
4. Agent commands are auto-installed into active IDEs (Claude Code, Codex, Gemini, Cursor)
|
|
28
28
|
5. CI validates package and README coverage before the sync PR to `aiox-core`
|
|
@@ -39,4 +39,4 @@ Pre-built agent squads installed during `npx aiox-core install` after Pro licens
|
|
|
39
39
|
|
|
40
40
|
- `npm run validate:publish-surface` fails if a top-level squad is missing from `package.json` or this README
|
|
41
41
|
- `.github/workflows/sync-aiox-core.yml` opens or updates the `aiox-core` PR that advances the `pro` submodule
|
|
42
|
-
- Durante a transição do npm, o `aiox-core`
|
|
42
|
+
- Durante a transição do npm, o `aiox-core` permanece como pacote core canônico; novos fluxos Pro devem usar `aiox-core` + `@aiox-squads/pro`
|
package/pro/squads/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This pattern should be followed by all pro modules.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* const { PremiumSquads } = require('@aiox-
|
|
8
|
+
* const { PremiumSquads } = require('@aiox-squads/pro/squads');
|
|
9
9
|
*
|
|
10
10
|
* // The module will throw ProFeatureError if not licensed
|
|
11
11
|
* const squads = new PremiumSquads();
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Installed Project Skills E2E Smoke
|
|
7
|
+
*
|
|
8
|
+
* Packs the local aiox-core package, installs it into a temporary brownfield
|
|
9
|
+
* project, runs the installed CLI, and validates that skills are materialized
|
|
10
|
+
* and activatable from the installed project rather than from the source repo.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { spawnSync } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const repoRoot = path.resolve(__dirname, '..', '..');
|
|
19
|
+
const { getSkillId } = require(path.join(
|
|
20
|
+
repoRoot,
|
|
21
|
+
'.aiox-core',
|
|
22
|
+
'infrastructure',
|
|
23
|
+
'scripts',
|
|
24
|
+
'codex-skills-sync',
|
|
25
|
+
'index',
|
|
26
|
+
));
|
|
27
|
+
const verbose = process.env.AIOX_E2E_VERBOSE === '1';
|
|
28
|
+
const keepTemp = process.env.AIOX_E2E_KEEP_TEMP === '1';
|
|
29
|
+
const agentSet = (process.env.AIOX_E2E_AGENT_SET || 'dev,qa,aiox-master')
|
|
30
|
+
.split(',')
|
|
31
|
+
.map((agent) => agent.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
|
|
34
|
+
const packDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aiox-pack-'));
|
|
35
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'aiox-installed-skills-'));
|
|
36
|
+
const projectRoot = path.join(tempRoot, 'project');
|
|
37
|
+
const requiredCorePackages = ['fast-glob', 'fs-extra', 'js-yaml', 'semver', 'ajv', 'tar', 'chalk'];
|
|
38
|
+
|
|
39
|
+
function log(message) {
|
|
40
|
+
console.log(`[installed-skills-e2e] ${message}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function fail(message, details = '') {
|
|
44
|
+
const suffix = details ? `\n${details}` : '';
|
|
45
|
+
throw new Error(`${message}${suffix}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function run(command, args, options = {}) {
|
|
49
|
+
const cwd = options.cwd || repoRoot;
|
|
50
|
+
const env = { ...process.env, ...(options.env || {}) };
|
|
51
|
+
const label = `${command} ${args.join(' ')}`;
|
|
52
|
+
|
|
53
|
+
if (verbose) {
|
|
54
|
+
log(`$ ${label} (cwd=${cwd})`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const result = spawnSync(command, args, {
|
|
58
|
+
cwd,
|
|
59
|
+
env,
|
|
60
|
+
encoding: 'utf8',
|
|
61
|
+
timeout: options.timeout || 120000,
|
|
62
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (result.error) {
|
|
66
|
+
fail(`Command failed to start: ${label}`, result.error.message);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (verbose && result.stdout) process.stdout.write(result.stdout);
|
|
70
|
+
if (verbose && result.stderr) process.stderr.write(result.stderr);
|
|
71
|
+
|
|
72
|
+
if (result.status !== 0) {
|
|
73
|
+
fail(
|
|
74
|
+
`Command failed (${result.status}): ${label}`,
|
|
75
|
+
[
|
|
76
|
+
result.stdout && `STDOUT:\n${result.stdout}`,
|
|
77
|
+
result.stderr && `STDERR:\n${result.stderr}`,
|
|
78
|
+
].filter(Boolean).join('\n\n'),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result.stdout || '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function assertPathExists(relativePath, type = 'any') {
|
|
86
|
+
const absolutePath = path.join(projectRoot, relativePath);
|
|
87
|
+
if (!fs.existsSync(absolutePath)) {
|
|
88
|
+
fail(`Missing expected installed path: ${relativePath}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const stat = fs.statSync(absolutePath);
|
|
92
|
+
if (type === 'file' && !stat.isFile()) {
|
|
93
|
+
fail(`Expected file but found non-file: ${relativePath}`);
|
|
94
|
+
}
|
|
95
|
+
if (type === 'dir' && !stat.isDirectory()) {
|
|
96
|
+
fail(`Expected directory but found non-directory: ${relativePath}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return absolutePath;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function assertAbsolutePathExists(absolutePath, type = 'any') {
|
|
103
|
+
if (!fs.existsSync(absolutePath)) {
|
|
104
|
+
fail(`Missing expected path: ${absolutePath}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const stat = fs.statSync(absolutePath);
|
|
108
|
+
if (type === 'file' && !stat.isFile()) {
|
|
109
|
+
fail(`Expected file but found non-file: ${absolutePath}`);
|
|
110
|
+
}
|
|
111
|
+
if (type === 'dir' && !stat.isDirectory()) {
|
|
112
|
+
fail(`Expected directory but found non-directory: ${absolutePath}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return absolutePath;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function assertNoSourceRepoLeak(relativePath) {
|
|
119
|
+
const absolutePath = assertPathExists(relativePath, 'file');
|
|
120
|
+
const content = fs.readFileSync(absolutePath, 'utf8');
|
|
121
|
+
|
|
122
|
+
if (content.includes(repoRoot)) {
|
|
123
|
+
fail(`Installed artifact leaks source repo path: ${relativePath}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return content;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function assertContains(content, expected, relativePath) {
|
|
130
|
+
if (!content.includes(expected)) {
|
|
131
|
+
fail(`Expected ${relativePath} to contain: ${expected}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function parseDoctorJson(output) {
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(output);
|
|
138
|
+
} catch {
|
|
139
|
+
fail('Doctor --json did not return parseable JSON', output);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function cleanup() {
|
|
144
|
+
if (keepTemp) {
|
|
145
|
+
log(`Keeping temp root: ${tempRoot}`);
|
|
146
|
+
log(`Keeping pack dir: ${packDir}`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
151
|
+
fs.rmSync(packDir, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function main() {
|
|
155
|
+
log(`Repo root: ${repoRoot}`);
|
|
156
|
+
log(`Temp project: ${projectRoot}`);
|
|
157
|
+
|
|
158
|
+
fs.mkdirSync(projectRoot, { recursive: true });
|
|
159
|
+
|
|
160
|
+
log('Packing local aiox-core package');
|
|
161
|
+
run('npm', ['pack', '--pack-destination', packDir], { cwd: repoRoot, timeout: 180000 });
|
|
162
|
+
const tarballs = fs.readdirSync(packDir).filter((entry) => entry.endsWith('.tgz'));
|
|
163
|
+
if (tarballs.length !== 1) {
|
|
164
|
+
fail(`Expected exactly one packed tarball, found ${tarballs.length}`, tarballs.join('\n'));
|
|
165
|
+
}
|
|
166
|
+
const tarballPath = path.join(packDir, tarballs[0]);
|
|
167
|
+
|
|
168
|
+
log('Creating brownfield test project');
|
|
169
|
+
run('npm', ['init', '-y'], { cwd: projectRoot });
|
|
170
|
+
|
|
171
|
+
log('Installing packed aiox-core tarball');
|
|
172
|
+
run('npm', ['install', tarballPath], { cwd: projectRoot, timeout: 180000 });
|
|
173
|
+
|
|
174
|
+
const cliPath = path.join(projectRoot, 'node_modules', 'aiox-core', 'bin', 'aiox.js');
|
|
175
|
+
const packagedCoreDir = path.join(projectRoot, 'node_modules', 'aiox-core', '.aiox-core');
|
|
176
|
+
assertPathExists(path.join('node_modules', 'aiox-core', 'bin', 'aiox.js'), 'file');
|
|
177
|
+
|
|
178
|
+
log('Validating packaged .aiox-core dependencies');
|
|
179
|
+
assertAbsolutePathExists(path.join(packagedCoreDir, 'package.json'), 'file');
|
|
180
|
+
run('npm', ['install', '--production', '--ignore-scripts'], {
|
|
181
|
+
cwd: packagedCoreDir,
|
|
182
|
+
timeout: 180000,
|
|
183
|
+
});
|
|
184
|
+
for (const packageName of requiredCorePackages) {
|
|
185
|
+
assertAbsolutePathExists(path.join(packagedCoreDir, 'node_modules', packageName), 'dir');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
log('Running installed aiox install in CI mode');
|
|
189
|
+
run('node', [cliPath, 'install', '--ci', '--yes', '--ide', 'claude-code'], {
|
|
190
|
+
cwd: projectRoot,
|
|
191
|
+
timeout: 240000,
|
|
192
|
+
env: {
|
|
193
|
+
CI: '1',
|
|
194
|
+
AIOX_INSTALL_FORCE: '1',
|
|
195
|
+
AIOX_INSTALL_QUIET: '1',
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
log('Validating installed skill and agent artifacts');
|
|
200
|
+
assertPathExists('.aiox-core', 'dir');
|
|
201
|
+
assertPathExists('.aiox-core/development/agents', 'dir');
|
|
202
|
+
assertPathExists('.claude/skills/AIOX/agents', 'dir');
|
|
203
|
+
assertPathExists('.codex/agents', 'dir');
|
|
204
|
+
assertPathExists('.codex/skills', 'dir');
|
|
205
|
+
|
|
206
|
+
for (const agent of agentSet) {
|
|
207
|
+
const claudeSkill = `.claude/skills/AIOX/agents/${agent}/SKILL.md`;
|
|
208
|
+
const codexAgent = `.codex/agents/${agent}.md`;
|
|
209
|
+
const codexSkillId = getSkillId(agent);
|
|
210
|
+
const codexSkill = `.codex/skills/${codexSkillId}/SKILL.md`;
|
|
211
|
+
const sourceAgent = `.aiox-core/development/agents/${agent}.md`;
|
|
212
|
+
|
|
213
|
+
assertPathExists(sourceAgent, 'file');
|
|
214
|
+
|
|
215
|
+
const claudeSkillContent = assertNoSourceRepoLeak(claudeSkill);
|
|
216
|
+
assertContains(claudeSkillContent, 'activation_type: pipeline', claudeSkill);
|
|
217
|
+
assertContains(claudeSkillContent, `Source: .aiox-core/development/agents/${agent}.md`, claudeSkill);
|
|
218
|
+
|
|
219
|
+
const codexAgentContent = assertNoSourceRepoLeak(codexAgent);
|
|
220
|
+
assertContains(codexAgentContent, `id: ${agent}`, codexAgent);
|
|
221
|
+
|
|
222
|
+
const codexSkillContent = assertNoSourceRepoLeak(codexSkill);
|
|
223
|
+
assertContains(codexSkillContent, `name: ${codexSkillId}`, codexSkill);
|
|
224
|
+
assertContains(codexSkillContent, `.aiox-core/development/agents/${agent}.md`, codexSkill);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
log(`Activating installed agents: ${agentSet.join(', ')}`);
|
|
228
|
+
const greetingScript = path.join(projectRoot, '.aiox-core', 'development', 'scripts', 'generate-greeting.js');
|
|
229
|
+
assertPathExists('.aiox-core/development/scripts/generate-greeting.js', 'file');
|
|
230
|
+
|
|
231
|
+
for (const agent of agentSet) {
|
|
232
|
+
const greeting = run('node', [greetingScript, agent], { cwd: projectRoot, timeout: 30000 });
|
|
233
|
+
if (!/Agent .*ready|Agent .*loaded|ready/i.test(greeting)) {
|
|
234
|
+
fail(`Activation smoke did not produce a ready signal for ${agent}`, greeting);
|
|
235
|
+
}
|
|
236
|
+
if (!/Available Commands|\*help/.test(greeting)) {
|
|
237
|
+
fail(`Activation smoke did not expose commands/help for ${agent}`, greeting);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
log('Running installed doctor --json');
|
|
242
|
+
const doctorOutput = run('node', [cliPath, 'doctor', '--json'], {
|
|
243
|
+
cwd: projectRoot,
|
|
244
|
+
timeout: 120000,
|
|
245
|
+
});
|
|
246
|
+
const doctor = parseDoctorJson(doctorOutput);
|
|
247
|
+
if (!doctor.summary || doctor.summary.fail > 0) {
|
|
248
|
+
fail('Installed doctor reported FAIL results', JSON.stringify(doctor, null, 2));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
log(`PASS: installed project skills E2E completed for ${agentSet.length} agents`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
main()
|
|
255
|
+
.catch((error) => {
|
|
256
|
+
console.error(`\n[installed-skills-e2e] FAIL: ${error.message}`);
|
|
257
|
+
if (!keepTemp) {
|
|
258
|
+
console.error('[installed-skills-e2e] Re-run with AIOX_E2E_KEEP_TEMP=1 to preserve temp files.');
|
|
259
|
+
} else {
|
|
260
|
+
console.error(`[installed-skills-e2e] Temp root preserved: ${tempRoot}`);
|
|
261
|
+
}
|
|
262
|
+
process.exitCode = 1;
|
|
263
|
+
})
|
|
264
|
+
.finally(cleanup);
|
|
@@ -217,19 +217,19 @@ cp hook/synapse-engine.cjs <your-project>/.claude/hooks/
|
|
|
217
217
|
|
|
218
218
|
Add to \`.claude/settings.local.json\`:
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
~~~json
|
|
221
221
|
{
|
|
222
222
|
"hooks": {
|
|
223
223
|
"UserPromptSubmit": [
|
|
224
224
|
{
|
|
225
225
|
"type": "command",
|
|
226
|
-
"command": "node
|
|
226
|
+
"command": "node \\"$CLAUDE_PROJECT_DIR/.claude/hooks/synapse-engine.cjs\\"",
|
|
227
227
|
"timeout": 10
|
|
228
228
|
}
|
|
229
229
|
]
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
|
-
|
|
232
|
+
~~~
|
|
233
233
|
|
|
234
234
|
### 4. Copy Runtime Domains
|
|
235
235
|
|