@jaimevalasek/aioson 1.3.0
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/CHANGELOG.md +456 -0
- package/CODE_OF_CONDUCT.md +12 -0
- package/CONTRIBUTING.md +13 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/bin/aioson.js +4 -0
- package/docs/en/cli-reference.md +398 -0
- package/docs/en/i18n.md +52 -0
- package/docs/en/json-schemas.md +41 -0
- package/docs/en/mcp.md +56 -0
- package/docs/en/parallel.md +82 -0
- package/docs/en/qa-browser.md +339 -0
- package/docs/en/release-flow.md +22 -0
- package/docs/en/release-notes-template.md +41 -0
- package/docs/en/release.md +28 -0
- package/docs/en/schemas/agent-prompt.schema.json +17 -0
- package/docs/en/schemas/agents.schema.json +32 -0
- package/docs/en/schemas/context-validate.schema.json +36 -0
- package/docs/en/schemas/doctor.schema.json +89 -0
- package/docs/en/schemas/error.schema.json +24 -0
- package/docs/en/schemas/i18n-add.schema.json +15 -0
- package/docs/en/schemas/index.json +116 -0
- package/docs/en/schemas/info.schema.json +39 -0
- package/docs/en/schemas/init.schema.json +48 -0
- package/docs/en/schemas/install.schema.json +60 -0
- package/docs/en/schemas/locale-apply.schema.json +30 -0
- package/docs/en/schemas/mcp-doctor.schema.json +95 -0
- package/docs/en/schemas/mcp-init.schema.json +122 -0
- package/docs/en/schemas/package-test.schema.json +24 -0
- package/docs/en/schemas/parallel-assign.schema.json +57 -0
- package/docs/en/schemas/parallel-doctor.schema.json +86 -0
- package/docs/en/schemas/parallel-init.schema.json +53 -0
- package/docs/en/schemas/parallel-status.schema.json +94 -0
- package/docs/en/schemas/setup-context.schema.json +39 -0
- package/docs/en/schemas/smoke.schema.json +23 -0
- package/docs/en/schemas/update.schema.json +48 -0
- package/docs/en/schemas/workflow-plan.schema.json +30 -0
- package/docs/en/web3.md +54 -0
- package/docs/pt/README.md +46 -0
- package/docs/pt/advisor-spec.md +335 -0
- package/docs/pt/agentes.md +453 -0
- package/docs/pt/cenarios.md +1230 -0
- package/docs/pt/clientes-ai.md +224 -0
- package/docs/pt/comandos-cli.md +511 -0
- package/docs/pt/genome-3.0-spec.md +296 -0
- package/docs/pt/guia-engineer.md +226 -0
- package/docs/pt/inicio-rapido.md +138 -0
- package/docs/pt/profiler-system.md +214 -0
- package/docs/pt/runtime-observability.md +72 -0
- package/docs/pt/squad-genoma.md +777 -0
- package/docs/pt/web3.md +797 -0
- package/docs/testing/genome-2.0-manual-regression.md +23 -0
- package/docs/testing/genome-2.0-matrix.md +36 -0
- package/docs/testing/genome-2.0-rollout.md +184 -0
- package/package.json +50 -0
- package/src/agents.js +56 -0
- package/src/cli.js +497 -0
- package/src/commands/agents.js +142 -0
- package/src/commands/cloud.js +1767 -0
- package/src/commands/config.js +90 -0
- package/src/commands/context-validate.js +91 -0
- package/src/commands/doctor.js +123 -0
- package/src/commands/genome-doctor.js +41 -0
- package/src/commands/genome-migrate.js +49 -0
- package/src/commands/i18n-add.js +56 -0
- package/src/commands/info.js +41 -0
- package/src/commands/init.js +75 -0
- package/src/commands/install.js +68 -0
- package/src/commands/locale-apply.js +51 -0
- package/src/commands/locale-diff.js +126 -0
- package/src/commands/mcp-doctor.js +406 -0
- package/src/commands/mcp-init.js +379 -0
- package/src/commands/package-e2e.js +273 -0
- package/src/commands/parallel-assign.js +403 -0
- package/src/commands/parallel-doctor.js +437 -0
- package/src/commands/parallel-init.js +249 -0
- package/src/commands/parallel-status.js +290 -0
- package/src/commands/qa-doctor.js +185 -0
- package/src/commands/qa-init.js +161 -0
- package/src/commands/qa-report.js +58 -0
- package/src/commands/qa-run.js +873 -0
- package/src/commands/qa-scan.js +337 -0
- package/src/commands/runtime.js +948 -0
- package/src/commands/scan-project.js +1107 -0
- package/src/commands/setup-context.js +650 -0
- package/src/commands/smoke.js +426 -0
- package/src/commands/squad-doctor.js +358 -0
- package/src/commands/squad-export.js +46 -0
- package/src/commands/squad-pipeline.js +97 -0
- package/src/commands/squad-repair-genomes.js +39 -0
- package/src/commands/squad-status.js +424 -0
- package/src/commands/squad-validate.js +230 -0
- package/src/commands/test-agents.js +194 -0
- package/src/commands/update.js +55 -0
- package/src/commands/workflow-next.js +594 -0
- package/src/commands/workflow-plan.js +108 -0
- package/src/constants.js +314 -0
- package/src/context-parse-reason.js +22 -0
- package/src/context-writer.js +150 -0
- package/src/context.js +217 -0
- package/src/detector.js +261 -0
- package/src/doctor.js +289 -0
- package/src/execution-gateway.js +461 -0
- package/src/genome-files.js +198 -0
- package/src/genome-format.js +442 -0
- package/src/genome-schema.js +215 -0
- package/src/genomes/bindings.js +281 -0
- package/src/genomes.js +467 -0
- package/src/i18n/index.js +103 -0
- package/src/i18n/messages/en.js +784 -0
- package/src/i18n/messages/es.js +718 -0
- package/src/i18n/messages/fr.js +725 -0
- package/src/i18n/messages/pt-BR.js +818 -0
- package/src/i18n/scaffold.js +64 -0
- package/src/installer.js +232 -0
- package/src/lib/genomes/compat.js +206 -0
- package/src/lib/genomes/migrate.js +90 -0
- package/src/lib/squads/genome-repair.js +49 -0
- package/src/locales.js +84 -0
- package/src/onboarding.js +305 -0
- package/src/parser.js +53 -0
- package/src/prompt-tool.js +20 -0
- package/src/qa-html-report.js +472 -0
- package/src/runtime-store.js +1527 -0
- package/src/squads/apply-genome.js +21 -0
- package/src/squads/genome-binding-service.js +154 -0
- package/src/updater.js +32 -0
- package/src/utils.js +46 -0
- package/src/version.js +50 -0
- package/template/.aioson/advisors/.gitkeep +1 -0
- package/template/.aioson/agents/analyst.md +225 -0
- package/template/.aioson/agents/architect.md +221 -0
- package/template/.aioson/agents/dev.md +201 -0
- package/template/.aioson/agents/discovery-design-doc.md +196 -0
- package/template/.aioson/agents/genoma.md +300 -0
- package/template/.aioson/agents/orchestrator.md +107 -0
- package/template/.aioson/agents/pm.md +89 -0
- package/template/.aioson/agents/product.md +361 -0
- package/template/.aioson/agents/profiler-enricher.md +266 -0
- package/template/.aioson/agents/profiler-forge.md +188 -0
- package/template/.aioson/agents/profiler-researcher.md +245 -0
- package/template/.aioson/agents/qa.md +344 -0
- package/template/.aioson/agents/setup.md +381 -0
- package/template/.aioson/agents/squad.md +837 -0
- package/template/.aioson/agents/ux-ui.md +416 -0
- package/template/.aioson/config.md +56 -0
- package/template/.aioson/context/.gitkeep +0 -0
- package/template/.aioson/context/parallel/.gitkeep +0 -0
- package/template/.aioson/context/spec.md.template +37 -0
- package/template/.aioson/genomas/.gitkeep +0 -0
- package/template/.aioson/locales/en/agents/analyst.md +214 -0
- package/template/.aioson/locales/en/agents/architect.md +210 -0
- package/template/.aioson/locales/en/agents/dev.md +187 -0
- package/template/.aioson/locales/en/agents/discovery-design-doc.md +27 -0
- package/template/.aioson/locales/en/agents/genoma.md +212 -0
- package/template/.aioson/locales/en/agents/orchestrator.md +105 -0
- package/template/.aioson/locales/en/agents/pm.md +77 -0
- package/template/.aioson/locales/en/agents/product.md +310 -0
- package/template/.aioson/locales/en/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/en/agents/qa.md +214 -0
- package/template/.aioson/locales/en/agents/setup.md +342 -0
- package/template/.aioson/locales/en/agents/squad.md +247 -0
- package/template/.aioson/locales/en/agents/ux-ui.md +320 -0
- package/template/.aioson/locales/es/agents/analyst.md +203 -0
- package/template/.aioson/locales/es/agents/architect.md +208 -0
- package/template/.aioson/locales/es/agents/dev.md +183 -0
- package/template/.aioson/locales/es/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/es/agents/genoma.md +102 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/es/agents/pm.md +81 -0
- package/template/.aioson/locales/es/agents/product.md +310 -0
- package/template/.aioson/locales/es/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/es/agents/qa.md +163 -0
- package/template/.aioson/locales/es/agents/setup.md +347 -0
- package/template/.aioson/locales/es/agents/squad.md +247 -0
- package/template/.aioson/locales/es/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/fr/agents/analyst.md +203 -0
- package/template/.aioson/locales/fr/agents/architect.md +208 -0
- package/template/.aioson/locales/fr/agents/dev.md +183 -0
- package/template/.aioson/locales/fr/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/fr/agents/genoma.md +102 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/fr/agents/pm.md +81 -0
- package/template/.aioson/locales/fr/agents/product.md +310 -0
- package/template/.aioson/locales/fr/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/fr/agents/qa.md +163 -0
- package/template/.aioson/locales/fr/agents/setup.md +347 -0
- package/template/.aioson/locales/fr/agents/squad.md +247 -0
- package/template/.aioson/locales/fr/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/pt-BR/agents/analyst.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/architect.md +213 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/genoma.md +297 -0
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/pt-BR/agents/pm.md +81 -0
- package/template/.aioson/locales/pt-BR/agents/product.md +316 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/qa.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +371 -0
- package/template/.aioson/locales/pt-BR/agents/squad.md +772 -0
- package/template/.aioson/locales/pt-BR/agents/ux-ui.md +322 -0
- package/template/.aioson/mcp/servers.md +24 -0
- package/template/.aioson/profiler-reports/.gitkeep +1 -0
- package/template/.aioson/schemas/content-blueprint.schema.json +30 -0
- package/template/.aioson/schemas/genome-meta.schema.json +150 -0
- package/template/.aioson/schemas/genome.schema.json +115 -0
- package/template/.aioson/schemas/readiness.schema.json +27 -0
- package/template/.aioson/schemas/squad-blueprint.schema.json +172 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +276 -0
- package/template/.aioson/skills/dynamic/README.md +30 -0
- package/template/.aioson/skills/dynamic/cardano-docs.md +16 -0
- package/template/.aioson/skills/dynamic/ethereum-docs.md +17 -0
- package/template/.aioson/skills/dynamic/flux-ui-docs.md +13 -0
- package/template/.aioson/skills/dynamic/laravel-docs.md +41 -0
- package/template/.aioson/skills/dynamic/npm-packages.md +16 -0
- package/template/.aioson/skills/dynamic/solana-docs.md +16 -0
- package/template/.aioson/skills/references/premium-command-center-ui/master-application-prompt.md +79 -0
- package/template/.aioson/skills/references/premium-command-center-ui/operational-ux-playbook.md +253 -0
- package/template/.aioson/skills/references/premium-command-center-ui/quality-validation-checklist.md +82 -0
- package/template/.aioson/skills/references/premium-command-center-ui/visual-system-and-component-patterns.md +270 -0
- package/template/.aioson/skills/static/django-patterns.md +342 -0
- package/template/.aioson/skills/static/fastapi-patterns.md +344 -0
- package/template/.aioson/skills/static/filament-patterns.md +267 -0
- package/template/.aioson/skills/static/flux-ui-components.md +262 -0
- package/template/.aioson/skills/static/git-conventions.md +227 -0
- package/template/.aioson/skills/static/interface-design.md +372 -0
- package/template/.aioson/skills/static/jetstream-setup.md +200 -0
- package/template/.aioson/skills/static/laravel-conventions.md +491 -0
- package/template/.aioson/skills/static/nextjs-patterns.md +321 -0
- package/template/.aioson/skills/static/node-express-patterns.md +317 -0
- package/template/.aioson/skills/static/node-typescript-patterns.md +282 -0
- package/template/.aioson/skills/static/premium-command-center-ui.md +190 -0
- package/template/.aioson/skills/static/rails-conventions.md +307 -0
- package/template/.aioson/skills/static/react-motion-patterns.md +577 -0
- package/template/.aioson/skills/static/static-html-patterns.md +1935 -0
- package/template/.aioson/skills/static/tall-stack-patterns.md +286 -0
- package/template/.aioson/skills/static/ui-ux-modern.md +75 -0
- package/template/.aioson/skills/static/web3-cardano-patterns.md +337 -0
- package/template/.aioson/skills/static/web3-ethereum-patterns.md +310 -0
- package/template/.aioson/skills/static/web3-security-checklist.md +284 -0
- package/template/.aioson/skills/static/web3-solana-patterns.md +324 -0
- package/template/.aioson/squads/.artisan/.gitkeep +0 -0
- package/template/.aioson/squads/.gitkeep +0 -0
- package/template/.aioson/squads/memory.md +5 -0
- package/template/.aioson/tasks/squad-analyze.md +83 -0
- package/template/.aioson/tasks/squad-create.md +99 -0
- package/template/.aioson/tasks/squad-design.md +100 -0
- package/template/.aioson/tasks/squad-export.md +20 -0
- package/template/.aioson/tasks/squad-extend.md +68 -0
- package/template/.aioson/tasks/squad-pipeline.md +122 -0
- package/template/.aioson/tasks/squad-repair.md +85 -0
- package/template/.aioson/tasks/squad-validate.md +58 -0
- package/template/.aioson/templates/squads/content-basic/template.json +21 -0
- package/template/.aioson/templates/squads/media-channel/template.json +24 -0
- package/template/.aioson/templates/squads/research-analysis/template.json +22 -0
- package/template/.aioson/templates/squads/software-delivery/template.json +21 -0
- package/template/.claude/commands/aioson/analyst.md +5 -0
- package/template/.claude/commands/aioson/architect.md +5 -0
- package/template/.claude/commands/aioson/dev.md +5 -0
- package/template/.claude/commands/aioson/orchestrator.md +5 -0
- package/template/.claude/commands/aioson/pm.md +5 -0
- package/template/.claude/commands/aioson/qa.md +5 -0
- package/template/.claude/commands/aioson/setup.md +5 -0
- package/template/.claude/commands/aioson/ux-ui.md +5 -0
- package/template/.gemini/GEMINI.md +10 -0
- package/template/.gemini/commands/aios-analyst.toml +4 -0
- package/template/.gemini/commands/aios-architect.toml +7 -0
- package/template/.gemini/commands/aios-dev.toml +8 -0
- package/template/.gemini/commands/aios-discovery-design-doc.toml +4 -0
- package/template/.gemini/commands/aios-orchestrator.toml +8 -0
- package/template/.gemini/commands/aios-pm.toml +8 -0
- package/template/.gemini/commands/aios-product.toml +4 -0
- package/template/.gemini/commands/aios-qa.toml +6 -0
- package/template/.gemini/commands/aios-setup.toml +3 -0
- package/template/.gemini/commands/aios-ux-ui.toml +8 -0
- package/template/AGENTS.md +67 -0
- package/template/CLAUDE.md +31 -0
- package/template/OPENCODE.md +24 -0
- package/template/aioson-models.json +40 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { openRuntimeDb } = require('../runtime-store');
|
|
6
|
+
const { exists } = require('../utils');
|
|
7
|
+
const { runSquadValidate } = require('./squad-validate');
|
|
8
|
+
|
|
9
|
+
function normalizeRel(value) {
|
|
10
|
+
return String(value || '')
|
|
11
|
+
.replace(/\\/g, '/')
|
|
12
|
+
.replace(/^\.\//, '')
|
|
13
|
+
.replace(/\/+$/, '');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function asObject(value) {
|
|
17
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function asArray(value) {
|
|
21
|
+
return Array.isArray(value) ? value : [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function readJsonIfExists(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
27
|
+
return JSON.parse(raw);
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function pathExists(targetPath) {
|
|
34
|
+
try {
|
|
35
|
+
await fs.access(targetPath);
|
|
36
|
+
return true;
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function listFilesRecursive(rootDir) {
|
|
43
|
+
const result = [];
|
|
44
|
+
const queue = [rootDir];
|
|
45
|
+
|
|
46
|
+
while (queue.length > 0) {
|
|
47
|
+
const current = queue.shift();
|
|
48
|
+
const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const fullPath = path.join(current, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
queue.push(fullPath);
|
|
53
|
+
} else if (entry.isFile()) {
|
|
54
|
+
result.push(fullPath);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function resolveSquadSlug(targetDir, requestedSlug) {
|
|
63
|
+
if (requestedSlug) return String(requestedSlug).trim();
|
|
64
|
+
|
|
65
|
+
const metadataDir = path.join(targetDir, '.aioson', 'squads');
|
|
66
|
+
const agentsDir = path.join(targetDir, 'agents');
|
|
67
|
+
const candidates = new Set();
|
|
68
|
+
|
|
69
|
+
const metadataEntries = await fs.readdir(metadataDir, { withFileTypes: true }).catch(() => []);
|
|
70
|
+
for (const entry of metadataEntries) {
|
|
71
|
+
if (entry.isDirectory()) candidates.add(entry.name);
|
|
72
|
+
if (entry.isFile() && entry.name.endsWith('.md')) candidates.add(entry.name.replace(/\.md$/i, ''));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const agentEntries = await fs.readdir(agentsDir, { withFileTypes: true }).catch(() => []);
|
|
76
|
+
for (const entry of agentEntries) {
|
|
77
|
+
if (entry.isDirectory()) candidates.add(entry.name);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const slugs = Array.from(candidates).filter(Boolean);
|
|
81
|
+
if (slugs.length === 1) return slugs[0];
|
|
82
|
+
if (slugs.length === 0) {
|
|
83
|
+
throw new Error('No squad found. Create a squad first or provide --squad=<slug>.');
|
|
84
|
+
}
|
|
85
|
+
throw new Error('Multiple squads found. Provide --squad=<slug>.');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function parseSquadPaths(targetDir, slug) {
|
|
89
|
+
const packageDir = path.join(targetDir, '.aioson', 'squads', slug);
|
|
90
|
+
const hasPackageDir = await pathExists(packageDir);
|
|
91
|
+
const metadataPath = hasPackageDir
|
|
92
|
+
? path.join(packageDir, 'squad.md')
|
|
93
|
+
: path.join(targetDir, '.aioson', 'squads', `${slug}.md`);
|
|
94
|
+
const manifestPath = hasPackageDir
|
|
95
|
+
? path.join(packageDir, 'squad.manifest.json')
|
|
96
|
+
: path.join(targetDir, 'agents', slug, 'squad.manifest.json');
|
|
97
|
+
const rulesPath = hasPackageDir
|
|
98
|
+
? path.join(packageDir, 'agents', 'agents.md')
|
|
99
|
+
: path.join(targetDir, 'agents', slug, 'agents.md');
|
|
100
|
+
const designDocPath = hasPackageDir
|
|
101
|
+
? path.join(packageDir, 'docs', 'design-doc.md')
|
|
102
|
+
: path.join(targetDir, 'agents', slug, 'design-doc.md');
|
|
103
|
+
const readinessPath = hasPackageDir
|
|
104
|
+
? path.join(packageDir, 'docs', 'readiness.md')
|
|
105
|
+
: path.join(targetDir, 'agents', slug, 'readiness.md');
|
|
106
|
+
|
|
107
|
+
const manifest = (await readJsonIfExists(manifestPath)) || {};
|
|
108
|
+
const rules = asObject(manifest.rules) || {};
|
|
109
|
+
const packageInfo = asObject(manifest.package) || {};
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
packageDir,
|
|
113
|
+
metadataPath,
|
|
114
|
+
manifestPath,
|
|
115
|
+
rulesPath,
|
|
116
|
+
designDocPath,
|
|
117
|
+
readinessPath,
|
|
118
|
+
manifest,
|
|
119
|
+
agentsDir: hasPackageDir
|
|
120
|
+
? path.join(targetDir, normalizeRel(packageInfo.agentsDir || `.aioson/squads/${slug}/agents`))
|
|
121
|
+
: path.join(targetDir, 'agents', slug),
|
|
122
|
+
outputDir: path.join(targetDir, normalizeRel(rules.outputsDir || `output/${slug}`)),
|
|
123
|
+
logsDir: path.join(targetDir, normalizeRel(rules.logsDir || `aios-logs/${slug}`)),
|
|
124
|
+
mediaDir: path.join(targetDir, normalizeRel(rules.mediaDir || `media/${slug}`))
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function makeCheck(id, ok, severity, message, meta = {}) {
|
|
129
|
+
return { id, ok, severity, message, ...meta };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function inferStale(run, staleMinutes) {
|
|
133
|
+
const updatedAt = Date.parse(run.updated_at || run.updatedAt || '');
|
|
134
|
+
if (!updatedAt) return false;
|
|
135
|
+
return Date.now() - updatedAt > staleMinutes * 60 * 1000;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function detectContentCandidates(outputDir, projectRoot) {
|
|
139
|
+
const existsOutput = await pathExists(outputDir);
|
|
140
|
+
if (!existsOutput) return [];
|
|
141
|
+
|
|
142
|
+
const allFiles = await listFilesRecursive(outputDir);
|
|
143
|
+
const contentJsonDirs = new Set(
|
|
144
|
+
allFiles
|
|
145
|
+
.filter((filePath) => path.basename(filePath).toLowerCase() === 'content.json')
|
|
146
|
+
.map((filePath) => path.dirname(filePath))
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return allFiles
|
|
150
|
+
.filter((filePath) => {
|
|
151
|
+
const base = path.basename(filePath).toLowerCase();
|
|
152
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
153
|
+
if (base === 'content.json') return true;
|
|
154
|
+
if (ext !== '.md' && ext !== '.html' && ext !== '.htm') return false;
|
|
155
|
+
if (contentJsonDirs.has(path.dirname(filePath))) return false;
|
|
156
|
+
return true;
|
|
157
|
+
})
|
|
158
|
+
.map((filePath) => path.relative(projectRoot, filePath).replace(/\\/g, '/'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function runSquadDoctor({ args, options = {}, logger, t }) {
|
|
162
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
163
|
+
const slug = await resolveSquadSlug(targetDir, options.squad);
|
|
164
|
+
const staleMinutes = Number(options['stale-minutes'] || 5);
|
|
165
|
+
const paths = await parseSquadPaths(targetDir, slug);
|
|
166
|
+
const manifest = asObject(paths.manifest) || {};
|
|
167
|
+
const executors = asArray(manifest.executors).filter((executor) => asObject(executor));
|
|
168
|
+
|
|
169
|
+
const checks = [];
|
|
170
|
+
|
|
171
|
+
const metadataExists = await exists(paths.metadataPath);
|
|
172
|
+
checks.push(makeCheck('metadata', metadataExists, metadataExists ? 'info' : 'error', t('squad_doctor.check_metadata', { path: paths.metadataPath })));
|
|
173
|
+
|
|
174
|
+
const manifestExists = await exists(paths.manifestPath);
|
|
175
|
+
checks.push(makeCheck('manifest', manifestExists, manifestExists ? 'info' : 'error', t('squad_doctor.check_manifest', { path: paths.manifestPath })));
|
|
176
|
+
|
|
177
|
+
const rulesExists = await exists(paths.rulesPath);
|
|
178
|
+
checks.push(makeCheck('rules', rulesExists, rulesExists ? 'info' : 'error', t('squad_doctor.check_rules', { path: paths.rulesPath })));
|
|
179
|
+
|
|
180
|
+
const designDocExists = await exists(paths.designDocPath);
|
|
181
|
+
checks.push(makeCheck('design_doc', designDocExists, designDocExists ? 'info' : 'warn', t('squad_doctor.check_design_doc', { path: paths.designDocPath })));
|
|
182
|
+
|
|
183
|
+
const readinessExists = await exists(paths.readinessPath);
|
|
184
|
+
checks.push(makeCheck('readiness', readinessExists, readinessExists ? 'info' : 'warn', t('squad_doctor.check_readiness', { path: paths.readinessPath })));
|
|
185
|
+
|
|
186
|
+
const executorFilesMissing = [];
|
|
187
|
+
for (const executor of executors) {
|
|
188
|
+
const relFile = normalizeRel(executor.file || '');
|
|
189
|
+
if (!relFile) continue;
|
|
190
|
+
const absFile = path.join(targetDir, relFile);
|
|
191
|
+
if (!(await exists(absFile))) executorFilesMissing.push(relFile);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
checks.push(
|
|
195
|
+
makeCheck(
|
|
196
|
+
'executors',
|
|
197
|
+
executors.length > 0 && executorFilesMissing.length === 0,
|
|
198
|
+
executors.length === 0 || executorFilesMissing.length > 0 ? 'error' : 'info',
|
|
199
|
+
t('squad_doctor.check_executors', {
|
|
200
|
+
count: executors.length,
|
|
201
|
+
missing: executorFilesMissing.length
|
|
202
|
+
}),
|
|
203
|
+
{ missingExecutors: executorFilesMissing }
|
|
204
|
+
)
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const outputExists = await exists(paths.outputDir);
|
|
208
|
+
const mediaExists = await exists(paths.mediaDir);
|
|
209
|
+
checks.push(makeCheck('output_dir', outputExists, outputExists ? 'info' : 'warn', t('squad_doctor.check_output_dir', { path: paths.outputDir })));
|
|
210
|
+
checks.push(makeCheck('media_dir', mediaExists, mediaExists ? 'info' : 'warn', t('squad_doctor.check_media_dir', { path: paths.mediaDir })));
|
|
211
|
+
|
|
212
|
+
const runtimeHandle = await openRuntimeDb(targetDir, { mustExist: true });
|
|
213
|
+
if (!runtimeHandle) {
|
|
214
|
+
checks.push(makeCheck('runtime_store', false, 'warn', t('squad_doctor.check_runtime_missing')));
|
|
215
|
+
} else {
|
|
216
|
+
const { db } = runtimeHandle;
|
|
217
|
+
try {
|
|
218
|
+
const activeRuns = db
|
|
219
|
+
.prepare(
|
|
220
|
+
`
|
|
221
|
+
SELECT run_key, agent_name, title, status, updated_at
|
|
222
|
+
FROM agent_runs
|
|
223
|
+
WHERE squad_slug = ? AND status IN ('queued', 'running')
|
|
224
|
+
ORDER BY updated_at DESC
|
|
225
|
+
`
|
|
226
|
+
)
|
|
227
|
+
.all(slug);
|
|
228
|
+
const staleRuns = activeRuns.filter((run) => inferStale(run, staleMinutes));
|
|
229
|
+
checks.push(
|
|
230
|
+
makeCheck(
|
|
231
|
+
'runtime_active_runs',
|
|
232
|
+
staleRuns.length === 0,
|
|
233
|
+
staleRuns.length > 0 ? 'warn' : 'info',
|
|
234
|
+
t('squad_doctor.check_active_runs', {
|
|
235
|
+
count: activeRuns.length,
|
|
236
|
+
stale: staleRuns.length,
|
|
237
|
+
minutes: staleMinutes
|
|
238
|
+
}),
|
|
239
|
+
{ activeRuns, staleRuns }
|
|
240
|
+
)
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const indexedRows = db
|
|
244
|
+
.prepare(
|
|
245
|
+
`
|
|
246
|
+
SELECT content_key, json_path, html_path
|
|
247
|
+
FROM content_items
|
|
248
|
+
WHERE squad_slug = ?
|
|
249
|
+
`
|
|
250
|
+
)
|
|
251
|
+
.all(slug);
|
|
252
|
+
const indexedPaths = new Set();
|
|
253
|
+
for (const row of indexedRows) {
|
|
254
|
+
if (row.json_path) indexedPaths.add(normalizeRel(row.json_path));
|
|
255
|
+
if (row.html_path) indexedPaths.add(normalizeRel(row.html_path));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const candidatePaths = await detectContentCandidates(paths.outputDir, targetDir);
|
|
259
|
+
const unindexedPaths = candidatePaths.filter((candidate) => !indexedPaths.has(normalizeRel(candidate)));
|
|
260
|
+
checks.push(
|
|
261
|
+
makeCheck(
|
|
262
|
+
'content_indexing',
|
|
263
|
+
unindexedPaths.length === 0,
|
|
264
|
+
unindexedPaths.length > 0 ? 'warn' : 'info',
|
|
265
|
+
t('squad_doctor.check_content_indexing', {
|
|
266
|
+
indexed: indexedRows.length,
|
|
267
|
+
pending: unindexedPaths.length
|
|
268
|
+
}),
|
|
269
|
+
{ indexedRows, unindexedPaths }
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
} finally {
|
|
273
|
+
db.close();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Formal validation via squad-validate
|
|
278
|
+
const silentLogger = { log() {}, error() {} };
|
|
279
|
+
const validateResult = await runSquadValidate({
|
|
280
|
+
args: [targetDir],
|
|
281
|
+
options: { squad: slug },
|
|
282
|
+
logger: silentLogger
|
|
283
|
+
});
|
|
284
|
+
if (!validateResult.valid || validateResult.warnings.length > 0) {
|
|
285
|
+
checks.push(
|
|
286
|
+
makeCheck(
|
|
287
|
+
'formal_validation',
|
|
288
|
+
validateResult.valid,
|
|
289
|
+
validateResult.valid ? 'warn' : 'error',
|
|
290
|
+
validateResult.valid
|
|
291
|
+
? `Manifest valid with ${validateResult.warnings.length} warning(s)`
|
|
292
|
+
: `Manifest invalid: ${validateResult.errors[0] || 'see details'}`,
|
|
293
|
+
{ validateErrors: validateResult.errors, validateWarnings: validateResult.warnings }
|
|
294
|
+
)
|
|
295
|
+
);
|
|
296
|
+
} else {
|
|
297
|
+
checks.push(makeCheck('formal_validation', true, 'info', 'Manifest formally valid'));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const summary = {
|
|
301
|
+
failed: checks.filter((check) => check.severity === 'error' && !check.ok).length,
|
|
302
|
+
warned: checks.filter((check) => check.severity === 'warn' && !check.ok).length,
|
|
303
|
+
passed: checks.filter((check) => check.ok).length
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const output = {
|
|
307
|
+
ok: summary.failed === 0,
|
|
308
|
+
targetDir,
|
|
309
|
+
squad: slug,
|
|
310
|
+
staleMinutes,
|
|
311
|
+
summary,
|
|
312
|
+
checks
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
if (options.json) {
|
|
316
|
+
return output;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
logger.log(t('squad_doctor.report_title', { squad: slug, path: targetDir }));
|
|
320
|
+
for (const check of checks) {
|
|
321
|
+
const prefix = check.severity === 'warn'
|
|
322
|
+
? t('squad_doctor.prefix_warn')
|
|
323
|
+
: check.ok
|
|
324
|
+
? t('squad_doctor.prefix_ok')
|
|
325
|
+
: t('squad_doctor.prefix_fail');
|
|
326
|
+
logger.log(t('squad_doctor.check_line', { prefix, message: check.message }));
|
|
327
|
+
if (Array.isArray(check.missingExecutors) && check.missingExecutors.length > 0) {
|
|
328
|
+
for (const item of check.missingExecutors.slice(0, 10)) {
|
|
329
|
+
logger.log(` - ${item}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (Array.isArray(check.unindexedPaths) && check.unindexedPaths.length > 0) {
|
|
333
|
+
for (const item of check.unindexedPaths.slice(0, 10)) {
|
|
334
|
+
logger.log(` - ${item}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (Array.isArray(check.staleRuns) && check.staleRuns.length > 0) {
|
|
338
|
+
for (const item of check.staleRuns.slice(0, 10)) {
|
|
339
|
+
logger.log(` - ${item.agent_name} | ${item.title || '—'} | ${item.updated_at}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
logger.log('');
|
|
345
|
+
logger.log(
|
|
346
|
+
t('squad_doctor.summary', {
|
|
347
|
+
passed: summary.passed,
|
|
348
|
+
warned: summary.warned,
|
|
349
|
+
failed: summary.failed
|
|
350
|
+
})
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
return output;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
module.exports = {
|
|
357
|
+
runSquadDoctor
|
|
358
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { execSync } = require('node:child_process');
|
|
6
|
+
|
|
7
|
+
async function runSquadExport({ args = [], options = {}, logger = console } = {}) {
|
|
8
|
+
const projectDir = path.resolve(process.cwd(), args[0] || '.');
|
|
9
|
+
const slug = options.squad || args[1];
|
|
10
|
+
|
|
11
|
+
if (!slug) {
|
|
12
|
+
logger.error('Usage: aioson squad:export [path] --squad=<slug>');
|
|
13
|
+
return { ok: false, error: 'No slug provided' };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const squadDir = path.join(projectDir, '.aioson', 'squads', slug);
|
|
17
|
+
const exportsDir = path.join(projectDir, '.aioson', 'squads', 'exports');
|
|
18
|
+
const outputFile = path.join(exportsDir, `${slug}.aios-squad.tar.gz`);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(squadDir);
|
|
22
|
+
} catch {
|
|
23
|
+
logger.error(`Squad "${slug}" not found at ${squadDir}`);
|
|
24
|
+
return { ok: false, error: `Squad "${slug}" not found` };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await fs.mkdir(exportsDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
const relPath = path.relative(projectDir, squadDir).replace(/\\/g, '/');
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
execSync(`tar -czf "${outputFile}" -C "${projectDir}" "${relPath}"`, { stdio: 'pipe' });
|
|
33
|
+
} catch (err) {
|
|
34
|
+
logger.error(`Export failed: ${err.message}`);
|
|
35
|
+
return { ok: false, error: err.message };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const relOutput = path.relative(projectDir, outputFile);
|
|
39
|
+
logger.log('');
|
|
40
|
+
logger.log(`\u2705 Squad "${slug}" exported to: ${relOutput}`);
|
|
41
|
+
logger.log('');
|
|
42
|
+
|
|
43
|
+
return { ok: true, slug, outputFile: relOutput };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { runSquadExport };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const {
|
|
5
|
+
openRuntimeDb,
|
|
6
|
+
runtimeStoreExists,
|
|
7
|
+
listPipelines,
|
|
8
|
+
getPipelineDAG,
|
|
9
|
+
getTopologicalOrder
|
|
10
|
+
} = require('../runtime-store');
|
|
11
|
+
|
|
12
|
+
async function runSquadPipeline({ args = [], options = {}, logger = console } = {}) {
|
|
13
|
+
const projectDir = path.resolve(process.cwd(), args[0] || '.');
|
|
14
|
+
const subcommand = options.sub || args[1] || 'list';
|
|
15
|
+
const slugArg = options.pipeline || args[2];
|
|
16
|
+
|
|
17
|
+
const hasDb = await runtimeStoreExists(projectDir);
|
|
18
|
+
if (!hasDb) {
|
|
19
|
+
logger.error('Runtime store not initialized. Run: aioson runtime:init .');
|
|
20
|
+
return { ok: false, error: 'runtime_not_initialized' };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const result = await openRuntimeDb(projectDir, { mustExist: true });
|
|
24
|
+
if (!result) {
|
|
25
|
+
logger.error('Could not open runtime store.');
|
|
26
|
+
return { ok: false, error: 'db_open_failed' };
|
|
27
|
+
}
|
|
28
|
+
const { db } = result;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
if (subcommand === 'list') {
|
|
32
|
+
const pipelines = listPipelines(db);
|
|
33
|
+
if (pipelines.length === 0) {
|
|
34
|
+
logger.log('No pipelines found. Create one with: aioson squad:pipeline show --sub=create');
|
|
35
|
+
return { ok: true, pipelines: [] };
|
|
36
|
+
}
|
|
37
|
+
logger.log(`Pipelines (${pipelines.length}):`);
|
|
38
|
+
for (const p of pipelines) {
|
|
39
|
+
logger.log(` ${p.slug} [${p.status}] ${p.name || ''}`);
|
|
40
|
+
}
|
|
41
|
+
return { ok: true, pipelines };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (subcommand === 'show') {
|
|
45
|
+
if (!slugArg) {
|
|
46
|
+
logger.error('Usage: aioson squad:pipeline [path] --sub=show --pipeline=<slug>');
|
|
47
|
+
return { ok: false, error: 'missing_slug' };
|
|
48
|
+
}
|
|
49
|
+
const dag = getPipelineDAG(db, slugArg);
|
|
50
|
+
if (!dag) {
|
|
51
|
+
logger.error(`Pipeline not found: ${slugArg}`);
|
|
52
|
+
return { ok: false, error: 'not_found' };
|
|
53
|
+
}
|
|
54
|
+
const order = getTopologicalOrder(db, slugArg);
|
|
55
|
+
logger.log(`Pipeline: ${dag.pipeline.name || dag.pipeline.slug}`);
|
|
56
|
+
logger.log(`Status: ${dag.pipeline.status} Trigger: ${dag.pipeline.trigger_mode}`);
|
|
57
|
+
logger.log(`Nodes: ${dag.nodes.length} Edges: ${dag.edges.length}`);
|
|
58
|
+
if (order) {
|
|
59
|
+
logger.log(`Topological order: ${order.join(' → ')}`);
|
|
60
|
+
} else {
|
|
61
|
+
logger.log('⚠️ Cycle detected — invalid pipeline.');
|
|
62
|
+
}
|
|
63
|
+
for (const edge of dag.edges) {
|
|
64
|
+
logger.log(` [${edge.source_squad}:${edge.source_port}] → [${edge.target_squad}:${edge.target_port}]`);
|
|
65
|
+
}
|
|
66
|
+
return { ok: true, dag, topologicalOrder: order };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (subcommand === 'status') {
|
|
70
|
+
if (!slugArg) {
|
|
71
|
+
logger.error('Usage: aioson squad:pipeline [path] --sub=status --pipeline=<slug>');
|
|
72
|
+
return { ok: false, error: 'missing_slug' };
|
|
73
|
+
}
|
|
74
|
+
const dag = getPipelineDAG(db, slugArg);
|
|
75
|
+
if (!dag) {
|
|
76
|
+
logger.error(`Pipeline not found: ${slugArg}`);
|
|
77
|
+
return { ok: false, error: 'not_found' };
|
|
78
|
+
}
|
|
79
|
+
const handoffs = db
|
|
80
|
+
.prepare('SELECT * FROM squad_handoffs WHERE pipeline_slug = ? ORDER BY created_at DESC')
|
|
81
|
+
.all(slugArg);
|
|
82
|
+
const pending = handoffs.filter(h => h.status === 'pending').length;
|
|
83
|
+
const consumed = handoffs.filter(h => h.status === 'consumed').length;
|
|
84
|
+
const failed = handoffs.filter(h => h.status === 'failed').length;
|
|
85
|
+
logger.log(`Pipeline: ${dag.pipeline.slug} Status: ${dag.pipeline.status}`);
|
|
86
|
+
logger.log(`Handoffs — pending: ${pending} consumed: ${consumed} failed: ${failed}`);
|
|
87
|
+
return { ok: true, pipeline: dag.pipeline, handoffs: { pending, consumed, failed } };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
logger.error(`Unknown subcommand: ${subcommand}. Available: list, show, status`);
|
|
91
|
+
return { ok: false, error: 'unknown_subcommand' };
|
|
92
|
+
} finally {
|
|
93
|
+
db.close();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { runSquadPipeline };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { repairSquadManifestGenomeBindings } = require('../lib/squads/genome-repair');
|
|
5
|
+
|
|
6
|
+
async function runSquadRepairGenomes({ args, options = {}, logger }) {
|
|
7
|
+
const target = args[0];
|
|
8
|
+
if (!target) {
|
|
9
|
+
throw new Error('Usage: aioson squad:repair-genomes <manifest.json> [--write] [--no-backup]');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const manifestPath = path.resolve(process.cwd(), target);
|
|
13
|
+
const write = Boolean(options.write);
|
|
14
|
+
const backup = !Boolean(options['no-backup']);
|
|
15
|
+
const payload = await repairSquadManifestGenomeBindings(manifestPath, {
|
|
16
|
+
dryRun: !write,
|
|
17
|
+
write,
|
|
18
|
+
backup
|
|
19
|
+
});
|
|
20
|
+
const result = {
|
|
21
|
+
ok: true,
|
|
22
|
+
write,
|
|
23
|
+
dryRun: !write,
|
|
24
|
+
backup,
|
|
25
|
+
...payload
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (options.json) return result;
|
|
29
|
+
|
|
30
|
+
logger.log(`Squad genome repair target: ${manifestPath}`);
|
|
31
|
+
logger.log(`Mode: ${result.dryRun ? 'dry-run' : 'write'}`);
|
|
32
|
+
logger.log(`Changed: ${result.changed ? 'yes' : 'no'}`);
|
|
33
|
+
if (result.backupPath) logger.log(`Backup: ${result.backupPath}`);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
runSquadRepairGenomes
|
|
39
|
+
};
|