@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,594 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { getAgentDefinition, resolveInstructionPath, buildAgentPrompt } = require('../agents');
|
|
6
|
+
const { resolveAgentLocale } = require('../locales');
|
|
7
|
+
const { validateProjectContextFile } = require('../context');
|
|
8
|
+
const { exists, ensureDir } = require('../utils');
|
|
9
|
+
const { syncWorkflowRuntime } = require('../execution-gateway');
|
|
10
|
+
|
|
11
|
+
const STATE_RELATIVE_PATH = '.aioson/context/workflow.state.json';
|
|
12
|
+
const CONFIG_RELATIVE_PATH = '.aioson/context/workflow.config.json';
|
|
13
|
+
const EVENTS_RELATIVE_PATH = '.aioson/context/workflow.events.jsonl';
|
|
14
|
+
|
|
15
|
+
const DEFAULT_FEATURE_WORKFLOW_BY_CLASSIFICATION = {
|
|
16
|
+
MICRO: ['product', 'dev', 'qa'],
|
|
17
|
+
SMALL: ['product', 'analyst', 'dev', 'qa'],
|
|
18
|
+
MEDIUM: ['product', 'analyst', 'dev', 'qa']
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function normalizeAgentName(input) {
|
|
22
|
+
return String(input || '')
|
|
23
|
+
.trim()
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.replace(/^@/, '');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeClassification(value, fallback = 'MICRO') {
|
|
29
|
+
const text = String(value || '').trim().toUpperCase();
|
|
30
|
+
if (text === 'MICRO' || text === 'SMALL' || text === 'MEDIUM') return text;
|
|
31
|
+
return fallback;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildDefaultWorkflowConfig() {
|
|
35
|
+
return {
|
|
36
|
+
version: 1,
|
|
37
|
+
project: {
|
|
38
|
+
MICRO: ['setup', 'dev'],
|
|
39
|
+
SMALL: ['setup', 'product', 'analyst', 'architect', 'dev', 'qa'],
|
|
40
|
+
MEDIUM: ['setup', 'product', 'analyst', 'architect', 'ux-ui', 'pm', 'orchestrator', 'dev', 'qa']
|
|
41
|
+
},
|
|
42
|
+
feature: DEFAULT_FEATURE_WORKFLOW_BY_CLASSIFICATION,
|
|
43
|
+
rules: {
|
|
44
|
+
required: ['dev'],
|
|
45
|
+
allowDetours: true
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function parseFeaturesMarkdown(markdown) {
|
|
51
|
+
return String(markdown || '')
|
|
52
|
+
.split(/\r?\n/)
|
|
53
|
+
.slice(3)
|
|
54
|
+
.map((line) => line.trim())
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
.filter((line) => line.startsWith('|'))
|
|
57
|
+
.map((line) => line.split('|').map((part) => part.trim()))
|
|
58
|
+
.filter((parts) => parts.length >= 5)
|
|
59
|
+
.map((parts) => ({
|
|
60
|
+
slug: parts[1],
|
|
61
|
+
status: parts[2],
|
|
62
|
+
started: parts[3],
|
|
63
|
+
completed: parts[4]
|
|
64
|
+
}))
|
|
65
|
+
.filter((row) => row.slug && row.slug !== 'slug');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function readJsonIfExists(filePath) {
|
|
69
|
+
if (!(await exists(filePath))) return null;
|
|
70
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
71
|
+
return JSON.parse(content);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function writeJson(filePath, payload) {
|
|
75
|
+
await ensureDir(path.dirname(filePath));
|
|
76
|
+
await fs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function appendJsonLine(filePath, payload) {
|
|
80
|
+
await ensureDir(path.dirname(filePath));
|
|
81
|
+
await fs.appendFile(filePath, `${JSON.stringify(payload)}\n`, 'utf8');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildWorkflowEventMessage({ created, state, activation, completedStage, options }) {
|
|
85
|
+
const requestedAgent = options.requestedAgent ? normalizeAgentName(options.requestedAgent) : null;
|
|
86
|
+
if (requestedAgent && activation.agent && requestedAgent !== activation.agent) {
|
|
87
|
+
return `Workflow enforced @${activation.agent} after direct request for @${requestedAgent}.`;
|
|
88
|
+
}
|
|
89
|
+
if (completedStage && activation.agent) {
|
|
90
|
+
return `Completed @${completedStage}. Next stage ready: @${activation.agent}.`;
|
|
91
|
+
}
|
|
92
|
+
if (completedStage && !activation.agent) {
|
|
93
|
+
return `Completed @${completedStage}. Workflow has no pending stage.`;
|
|
94
|
+
}
|
|
95
|
+
if (state.detour && state.detour.active) {
|
|
96
|
+
return `Detour started with @${state.detour.agent}. Return to ${
|
|
97
|
+
state.detour.returnTo ? `@${state.detour.returnTo}` : 'the main flow'
|
|
98
|
+
}.`;
|
|
99
|
+
}
|
|
100
|
+
if (options.skip && activation.agent) {
|
|
101
|
+
return `Workflow advanced to @${activation.agent} after skip.`;
|
|
102
|
+
}
|
|
103
|
+
if (activation.agent) {
|
|
104
|
+
return created
|
|
105
|
+
? `Workflow initialized at @${activation.agent}.`
|
|
106
|
+
: `Stage @${activation.agent} is active.`;
|
|
107
|
+
}
|
|
108
|
+
return 'Workflow has no pending stage.';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildWorkflowEventType({ completedStage, state, activation, options }) {
|
|
112
|
+
const requestedAgent = options.requestedAgent ? normalizeAgentName(options.requestedAgent) : null;
|
|
113
|
+
if (requestedAgent && activation.agent && requestedAgent !== activation.agent) return 'routed';
|
|
114
|
+
if (completedStage) return 'completed';
|
|
115
|
+
if (state.detour && state.detour.active) return 'workflow';
|
|
116
|
+
if (options.skip) return 'workflow';
|
|
117
|
+
if (activation.agent) return 'start';
|
|
118
|
+
return 'workflow';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function appendWorkflowEvent(targetDir, payload) {
|
|
122
|
+
const eventsPath = path.join(targetDir, EVENTS_RELATIVE_PATH);
|
|
123
|
+
await appendJsonLine(eventsPath, payload);
|
|
124
|
+
return eventsPath;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function readWorkflowConfig(targetDir) {
|
|
128
|
+
const configPath = path.join(targetDir, CONFIG_RELATIVE_PATH);
|
|
129
|
+
const userConfig = await readJsonIfExists(configPath);
|
|
130
|
+
const base = buildDefaultWorkflowConfig();
|
|
131
|
+
if (!userConfig || typeof userConfig !== 'object') {
|
|
132
|
+
return { configPath, config: base, exists: false };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const merged = {
|
|
136
|
+
...base,
|
|
137
|
+
...userConfig,
|
|
138
|
+
project: {
|
|
139
|
+
...base.project,
|
|
140
|
+
...(userConfig.project || {})
|
|
141
|
+
},
|
|
142
|
+
feature: {
|
|
143
|
+
...base.feature,
|
|
144
|
+
...(userConfig.feature || {})
|
|
145
|
+
},
|
|
146
|
+
rules: {
|
|
147
|
+
...base.rules,
|
|
148
|
+
...(userConfig.rules || {})
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return { configPath, config: merged, exists: true };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function resolveLocaleForTarget(targetDir, options) {
|
|
156
|
+
const fromOption = options.language || options.lang;
|
|
157
|
+
if (fromOption) return resolveAgentLocale(fromOption);
|
|
158
|
+
|
|
159
|
+
const context = await validateProjectContextFile(targetDir);
|
|
160
|
+
if (context.parsed && context.data && context.data.conversation_language) {
|
|
161
|
+
return resolveAgentLocale(context.data.conversation_language);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return 'en';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function resolveExistingInstructionPath(targetDir, agent, locale) {
|
|
168
|
+
const candidate = resolveInstructionPath(agent, locale);
|
|
169
|
+
const candidateAbs = path.join(targetDir, candidate);
|
|
170
|
+
if (await exists(candidateAbs)) return candidate;
|
|
171
|
+
return agent.path;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function detectWorkflowMode(targetDir) {
|
|
175
|
+
const prdPath = path.join(targetDir, '.aioson/context/prd.md');
|
|
176
|
+
const featuresPath = path.join(targetDir, '.aioson/context/features.md');
|
|
177
|
+
const hasProjectPrd = await exists(prdPath);
|
|
178
|
+
const featuresMarkdown = await fs.readFile(featuresPath, 'utf8').catch(() => '');
|
|
179
|
+
const features = parseFeaturesMarkdown(featuresMarkdown);
|
|
180
|
+
const activeFeature = features.find((feature) => feature.status === 'in_progress') || null;
|
|
181
|
+
|
|
182
|
+
if (activeFeature) {
|
|
183
|
+
return {
|
|
184
|
+
mode: 'feature',
|
|
185
|
+
featureSlug: activeFeature.slug,
|
|
186
|
+
features
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
mode: hasProjectPrd ? 'project' : 'project',
|
|
192
|
+
featureSlug: null,
|
|
193
|
+
features
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function getSequenceForMode(config, mode, classification) {
|
|
198
|
+
const group = mode === 'feature' ? config.feature : config.project;
|
|
199
|
+
const sequence = group[normalizeClassification(classification, 'MICRO')];
|
|
200
|
+
return Array.isArray(sequence) && sequence.length > 0 ? [...sequence] : [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function validateStageArtifacts(targetDir, state, stage) {
|
|
204
|
+
const base = path.join(targetDir, '.aioson/context');
|
|
205
|
+
const slug = state.featureSlug;
|
|
206
|
+
|
|
207
|
+
if (stage === 'setup') {
|
|
208
|
+
const context = await validateProjectContextFile(targetDir);
|
|
209
|
+
return context.valid;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (stage === 'product') {
|
|
213
|
+
if (state.mode === 'feature' && slug) {
|
|
214
|
+
const prdFeature = path.join(base, `prd-${slug}.md`);
|
|
215
|
+
const prdFix = path.join(base, `prd-${slug}-fix.md`);
|
|
216
|
+
return (await exists(prdFeature)) || (await exists(prdFix));
|
|
217
|
+
}
|
|
218
|
+
return await exists(path.join(base, 'prd.md'));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (stage === 'analyst') {
|
|
222
|
+
if (state.mode === 'feature' && slug) {
|
|
223
|
+
const requirements = path.join(base, `requirements-${slug}.md`);
|
|
224
|
+
const spec = path.join(base, `spec-${slug}.md`);
|
|
225
|
+
return (await exists(requirements)) && (await exists(spec));
|
|
226
|
+
}
|
|
227
|
+
return await exists(path.join(base, 'discovery.md'));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (stage === 'architect') {
|
|
231
|
+
return await exists(path.join(base, 'architecture.md'));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (stage === 'ux-ui') {
|
|
235
|
+
return await exists(path.join(base, 'ui-spec.md'));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (stage === 'orchestrator') {
|
|
239
|
+
return await exists(path.join(base, 'parallel'));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function isRequiredAgent(config, agentName) {
|
|
246
|
+
return Array.isArray(config.rules?.required)
|
|
247
|
+
? config.rules.required.map(normalizeAgentName).includes(agentName)
|
|
248
|
+
: false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function buildStatePayload(input) {
|
|
252
|
+
return {
|
|
253
|
+
version: 1,
|
|
254
|
+
mode: input.mode,
|
|
255
|
+
classification: input.classification,
|
|
256
|
+
sequence: input.sequence,
|
|
257
|
+
current: input.current || null,
|
|
258
|
+
next: input.next || null,
|
|
259
|
+
completed: Array.isArray(input.completed) ? input.completed : [],
|
|
260
|
+
skipped: Array.isArray(input.skipped) ? input.skipped : [],
|
|
261
|
+
featureSlug: input.featureSlug || null,
|
|
262
|
+
detour: input.detour || null,
|
|
263
|
+
updatedAt: new Date().toISOString()
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function findNextFromSequence(sequence, completed, skipped) {
|
|
268
|
+
const done = new Set([...(completed || []), ...(skipped || [])].map(normalizeAgentName));
|
|
269
|
+
return sequence.find((stage) => !done.has(normalizeAgentName(stage))) || null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function isInferableStage(stage) {
|
|
273
|
+
return ['setup', 'product', 'analyst', 'architect', 'ux-ui', 'orchestrator'].includes(
|
|
274
|
+
normalizeAgentName(stage)
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function inferCompletedStages(targetDir, draftState) {
|
|
279
|
+
const completed = [];
|
|
280
|
+
for (const stage of draftState.sequence) {
|
|
281
|
+
if (!isInferableStage(stage)) break;
|
|
282
|
+
const valid = await validateStageArtifacts(targetDir, draftState, stage);
|
|
283
|
+
if (!valid) break;
|
|
284
|
+
completed.push(normalizeAgentName(stage));
|
|
285
|
+
}
|
|
286
|
+
return completed;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function loadOrCreateState(targetDir, options = {}) {
|
|
290
|
+
const statePath = path.join(targetDir, STATE_RELATIVE_PATH);
|
|
291
|
+
const existing = await readJsonIfExists(statePath);
|
|
292
|
+
if (existing && typeof existing === 'object' && Array.isArray(existing.sequence)) {
|
|
293
|
+
return { statePath, state: existing, created: false };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const context = await validateProjectContextFile(targetDir);
|
|
297
|
+
const classification = normalizeClassification(
|
|
298
|
+
options.classification || (context.data && context.data.classification) || 'MICRO',
|
|
299
|
+
'MICRO'
|
|
300
|
+
);
|
|
301
|
+
const modeInfo = await detectWorkflowMode(targetDir);
|
|
302
|
+
const { config } = await readWorkflowConfig(targetDir);
|
|
303
|
+
const sequence = getSequenceForMode(config, modeInfo.mode, classification);
|
|
304
|
+
const draftState = buildStatePayload({
|
|
305
|
+
mode: modeInfo.mode,
|
|
306
|
+
classification,
|
|
307
|
+
sequence,
|
|
308
|
+
current: null,
|
|
309
|
+
next: null,
|
|
310
|
+
completed: [],
|
|
311
|
+
skipped: [],
|
|
312
|
+
featureSlug: modeInfo.featureSlug,
|
|
313
|
+
detour: null
|
|
314
|
+
});
|
|
315
|
+
const completed = await inferCompletedStages(targetDir, draftState);
|
|
316
|
+
const next = findNextFromSequence(sequence, completed, []);
|
|
317
|
+
const state = buildStatePayload({
|
|
318
|
+
mode: modeInfo.mode,
|
|
319
|
+
classification,
|
|
320
|
+
sequence,
|
|
321
|
+
current: null,
|
|
322
|
+
next,
|
|
323
|
+
completed,
|
|
324
|
+
skipped: [],
|
|
325
|
+
featureSlug: modeInfo.featureSlug,
|
|
326
|
+
detour: null
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
await writeJson(statePath, state);
|
|
330
|
+
return { statePath, state, created: true };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async function persistState(targetDir, nextState) {
|
|
334
|
+
const statePath = path.join(targetDir, STATE_RELATIVE_PATH);
|
|
335
|
+
await writeJson(statePath, nextState);
|
|
336
|
+
return statePath;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function ensureAgentInSequence(state, agentName) {
|
|
340
|
+
if (state.sequence.includes(agentName)) return;
|
|
341
|
+
throw new Error(`Agent ${agentName} is not part of the active workflow sequence.`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function ensureSkippableTarget(config, state, targetAgent) {
|
|
345
|
+
const normalizedTarget = normalizeAgentName(targetAgent);
|
|
346
|
+
ensureAgentInSequence(state, normalizedTarget);
|
|
347
|
+
|
|
348
|
+
const currentIndex = state.next ? state.sequence.indexOf(state.next) : -1;
|
|
349
|
+
const targetIndex = state.sequence.indexOf(normalizedTarget);
|
|
350
|
+
const devIndex = state.sequence.indexOf('dev');
|
|
351
|
+
|
|
352
|
+
if (currentIndex === -1) {
|
|
353
|
+
throw new Error('No next stage is available to skip from.');
|
|
354
|
+
}
|
|
355
|
+
if (targetIndex === -1 || targetIndex < currentIndex) {
|
|
356
|
+
throw new Error(`Cannot skip backwards to ${targetAgent}.`);
|
|
357
|
+
}
|
|
358
|
+
if (normalizedTarget === 'dev') return;
|
|
359
|
+
if (devIndex !== -1 && targetIndex > devIndex) {
|
|
360
|
+
throw new Error('Cannot skip past @dev because @dev is mandatory.');
|
|
361
|
+
}
|
|
362
|
+
if (isRequiredAgent(config, normalizedTarget) && normalizedTarget !== 'dev') {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function finalizeCurrentStage(targetDir, config, state, stageName) {
|
|
368
|
+
const normalizedStage = normalizeAgentName(stageName || state.current || state.next);
|
|
369
|
+
if (!normalizedStage) {
|
|
370
|
+
throw new Error('No stage is active to complete.');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (state.detour && state.detour.active && normalizeAgentName(state.detour.agent) === normalizedStage) {
|
|
374
|
+
const validDetour = await validateStageArtifacts(targetDir, state, normalizedStage);
|
|
375
|
+
if (!validDetour) {
|
|
376
|
+
throw new Error(`Cannot complete detour ${normalizedStage}; expected artifacts are missing.`);
|
|
377
|
+
}
|
|
378
|
+
const nextState = buildStatePayload({
|
|
379
|
+
...state,
|
|
380
|
+
current: null,
|
|
381
|
+
next: state.detour.returnTo,
|
|
382
|
+
detour: null
|
|
383
|
+
});
|
|
384
|
+
return { state: nextState, completedStage: normalizedStage };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
ensureAgentInSequence(state, normalizedStage);
|
|
388
|
+
const valid = await validateStageArtifacts(targetDir, state, normalizedStage);
|
|
389
|
+
if (!valid) {
|
|
390
|
+
throw new Error(`Cannot complete ${normalizedStage}; expected artifacts are missing.`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const completed = Array.from(new Set([...(state.completed || []), normalizedStage]));
|
|
394
|
+
const next = findNextFromSequence(state.sequence, completed, state.skipped || []);
|
|
395
|
+
const nextState = buildStatePayload({
|
|
396
|
+
...state,
|
|
397
|
+
completed,
|
|
398
|
+
current: null,
|
|
399
|
+
next,
|
|
400
|
+
detour: null
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
return { state: nextState, completedStage: normalizedStage };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function applySkip(config, state, target) {
|
|
407
|
+
const normalizedTarget = normalizeAgentName(target);
|
|
408
|
+
ensureSkippableTarget(config, state, normalizedTarget);
|
|
409
|
+
const currentIndex = state.sequence.indexOf(state.next);
|
|
410
|
+
const targetIndex = state.sequence.indexOf(normalizedTarget);
|
|
411
|
+
const toSkip = state.sequence.slice(currentIndex, targetIndex);
|
|
412
|
+
if (toSkip.some((agent) => normalizeAgentName(agent) === 'dev')) {
|
|
413
|
+
throw new Error('Cannot skip @dev because it is mandatory.');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const skipped = Array.from(new Set([...(state.skipped || []), ...toSkip]));
|
|
417
|
+
return buildStatePayload({
|
|
418
|
+
...state,
|
|
419
|
+
skipped,
|
|
420
|
+
current: null,
|
|
421
|
+
next: normalizedTarget
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function activateStage(targetDir, state, locale, tool, explicitAgent = null) {
|
|
426
|
+
const stageName = normalizeAgentName(explicitAgent || state.current || state.next);
|
|
427
|
+
if (!stageName) {
|
|
428
|
+
return {
|
|
429
|
+
state,
|
|
430
|
+
agent: null,
|
|
431
|
+
instructionPath: null,
|
|
432
|
+
prompt: null
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const agent = getAgentDefinition(stageName);
|
|
437
|
+
if (!agent) {
|
|
438
|
+
throw new Error(`Unknown agent: ${stageName}`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const instructionPath = await resolveExistingInstructionPath(targetDir, agent, locale);
|
|
442
|
+
const prompt = buildAgentPrompt(agent, tool, { instructionPath });
|
|
443
|
+
|
|
444
|
+
let nextState = state;
|
|
445
|
+
if (explicitAgent && stageName !== normalizeAgentName(state.next)) {
|
|
446
|
+
nextState = buildStatePayload({
|
|
447
|
+
...state,
|
|
448
|
+
current: stageName,
|
|
449
|
+
detour: {
|
|
450
|
+
active: true,
|
|
451
|
+
agent: stageName,
|
|
452
|
+
returnTo: state.next
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
} else {
|
|
456
|
+
nextState = buildStatePayload({
|
|
457
|
+
...state,
|
|
458
|
+
current: stageName
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
state: nextState,
|
|
464
|
+
agent: stageName,
|
|
465
|
+
instructionPath,
|
|
466
|
+
prompt
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async function runWorkflowNext({ args, options, logger, t }) {
|
|
471
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
472
|
+
const tool = options.tool || 'codex';
|
|
473
|
+
const locale = await resolveLocaleForTarget(targetDir, options);
|
|
474
|
+
const { config } = await readWorkflowConfig(targetDir);
|
|
475
|
+
const loaded = await loadOrCreateState(targetDir, options);
|
|
476
|
+
let state = loaded.state;
|
|
477
|
+
let completedStage = null;
|
|
478
|
+
|
|
479
|
+
if (options.complete || options['complete-current']) {
|
|
480
|
+
const result = await finalizeCurrentStage(
|
|
481
|
+
targetDir,
|
|
482
|
+
config,
|
|
483
|
+
state,
|
|
484
|
+
options.complete === true ? state.current || state.next : options.complete
|
|
485
|
+
);
|
|
486
|
+
state = result.state;
|
|
487
|
+
completedStage = result.completedStage;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (options.skip) {
|
|
491
|
+
state = applySkip(config, state, options.skip);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const requestedAgent = options.agent ? normalizeAgentName(options.agent) : null;
|
|
495
|
+
const activation = await activateStage(targetDir, state, locale, tool, requestedAgent);
|
|
496
|
+
state = activation.state;
|
|
497
|
+
const statePath = await persistState(targetDir, state);
|
|
498
|
+
const eventPayload = {
|
|
499
|
+
id: Date.now(),
|
|
500
|
+
kind: 'workflow',
|
|
501
|
+
createdAt: new Date().toISOString(),
|
|
502
|
+
eventType: buildWorkflowEventType({ completedStage, state, activation, options }),
|
|
503
|
+
message: buildWorkflowEventMessage({
|
|
504
|
+
created: loaded.created,
|
|
505
|
+
state,
|
|
506
|
+
activation,
|
|
507
|
+
completedStage,
|
|
508
|
+
options
|
|
509
|
+
}),
|
|
510
|
+
mode: state.mode,
|
|
511
|
+
classification: state.classification,
|
|
512
|
+
featureSlug: state.featureSlug,
|
|
513
|
+
current: state.current,
|
|
514
|
+
next: state.detour && state.detour.active ? state.detour.returnTo : state.next,
|
|
515
|
+
completedStage,
|
|
516
|
+
detour: state.detour,
|
|
517
|
+
requestedAgent: options.requestedAgent ? normalizeAgentName(options.requestedAgent) : null,
|
|
518
|
+
completed: state.completed,
|
|
519
|
+
skipped: state.skipped,
|
|
520
|
+
sequence: state.sequence
|
|
521
|
+
};
|
|
522
|
+
await appendWorkflowEvent(targetDir, eventPayload);
|
|
523
|
+
const runtime = await syncWorkflowRuntime(targetDir, {
|
|
524
|
+
state,
|
|
525
|
+
eventPayload,
|
|
526
|
+
activationAgent: activation.agent,
|
|
527
|
+
completedStage
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const payload = {
|
|
531
|
+
ok: true,
|
|
532
|
+
targetDir,
|
|
533
|
+
locale,
|
|
534
|
+
tool,
|
|
535
|
+
statePath: STATE_RELATIVE_PATH,
|
|
536
|
+
configPath: CONFIG_RELATIVE_PATH,
|
|
537
|
+
created: loaded.created,
|
|
538
|
+
mode: state.mode,
|
|
539
|
+
classification: state.classification,
|
|
540
|
+
current: state.current,
|
|
541
|
+
next: state.detour && state.detour.active ? state.detour.returnTo : state.next,
|
|
542
|
+
detour: state.detour,
|
|
543
|
+
completed: state.completed,
|
|
544
|
+
skipped: state.skipped,
|
|
545
|
+
completedStage,
|
|
546
|
+
featureSlug: state.featureSlug,
|
|
547
|
+
runtime,
|
|
548
|
+
agent: activation.agent,
|
|
549
|
+
instructionPath: activation.instructionPath,
|
|
550
|
+
prompt: activation.prompt
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
logger.log(t('workflow_next.title', {
|
|
554
|
+
mode: state.mode,
|
|
555
|
+
classification: state.classification
|
|
556
|
+
}));
|
|
557
|
+
if (completedStage) {
|
|
558
|
+
logger.log(t('workflow_next.completed', { agent: `@${completedStage}` }));
|
|
559
|
+
}
|
|
560
|
+
if (state.detour && state.detour.active) {
|
|
561
|
+
logger.log(
|
|
562
|
+
t('workflow_next.detour', {
|
|
563
|
+
agent: `@${state.detour.agent}`,
|
|
564
|
+
returnTo: `@${state.detour.returnTo}`
|
|
565
|
+
})
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
if (activation.agent) {
|
|
569
|
+
logger.log(t('workflow_next.current_agent', { agent: `@${activation.agent}` }));
|
|
570
|
+
if (payload.next) {
|
|
571
|
+
logger.log(t('workflow_next.next_agent', { agent: `@${payload.next}` }));
|
|
572
|
+
}
|
|
573
|
+
logger.log(activation.prompt);
|
|
574
|
+
} else {
|
|
575
|
+
logger.log(t('workflow_next.done'));
|
|
576
|
+
}
|
|
577
|
+
logger.log(t('workflow_next.state_file', { path: STATE_RELATIVE_PATH }));
|
|
578
|
+
|
|
579
|
+
return payload;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
module.exports = {
|
|
583
|
+
STATE_RELATIVE_PATH,
|
|
584
|
+
CONFIG_RELATIVE_PATH,
|
|
585
|
+
EVENTS_RELATIVE_PATH,
|
|
586
|
+
buildDefaultWorkflowConfig,
|
|
587
|
+
parseFeaturesMarkdown,
|
|
588
|
+
readWorkflowConfig,
|
|
589
|
+
detectWorkflowMode,
|
|
590
|
+
loadOrCreateState,
|
|
591
|
+
finalizeCurrentStage,
|
|
592
|
+
applySkip,
|
|
593
|
+
runWorkflowNext
|
|
594
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { validateProjectContextFile } = require('../context');
|
|
5
|
+
|
|
6
|
+
const WORKFLOW_BY_CLASSIFICATION = {
|
|
7
|
+
MICRO: ['setup', 'dev'],
|
|
8
|
+
SMALL: ['setup', 'product', 'analyst', 'architect', 'dev', 'qa'],
|
|
9
|
+
MEDIUM: ['setup', 'product', 'analyst', 'architect', 'ux-ui', 'pm', 'orchestrator', 'dev', 'qa']
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function normalizeClassification(value, fallback = 'MICRO') {
|
|
13
|
+
const text = String(value || '').trim().toUpperCase();
|
|
14
|
+
if (Object.prototype.hasOwnProperty.call(WORKFLOW_BY_CLASSIFICATION, text)) return text;
|
|
15
|
+
return fallback;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function withAgentPrefix(sequence) {
|
|
19
|
+
return sequence.map((id) => `@${id}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function buildWorkflowPlan(input = {}) {
|
|
23
|
+
const classification = normalizeClassification(input.classification, 'MICRO');
|
|
24
|
+
const projectType = String(input.projectType || 'web_app');
|
|
25
|
+
const frameworkInstalled = Boolean(input.frameworkInstalled);
|
|
26
|
+
const sequence = WORKFLOW_BY_CLASSIFICATION[classification] || WORKFLOW_BY_CLASSIFICATION.MICRO;
|
|
27
|
+
const noteKeys = [];
|
|
28
|
+
|
|
29
|
+
if (!frameworkInstalled) {
|
|
30
|
+
noteKeys.push('framework_not_installed');
|
|
31
|
+
}
|
|
32
|
+
if (projectType === 'dapp' || Boolean(input.web3Enabled)) {
|
|
33
|
+
noteKeys.push('dapp_context');
|
|
34
|
+
}
|
|
35
|
+
if (classification === 'MICRO') {
|
|
36
|
+
noteKeys.push('micro_scope');
|
|
37
|
+
noteKeys.push('product_optional');
|
|
38
|
+
}
|
|
39
|
+
if (classification === 'SMALL' || classification === 'MEDIUM') {
|
|
40
|
+
noteKeys.push('feature_flow');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
classification,
|
|
45
|
+
sequence,
|
|
46
|
+
commands: withAgentPrefix(sequence),
|
|
47
|
+
noteKeys
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function runWorkflowPlan({ args, options = {}, logger, t }) {
|
|
52
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
53
|
+
const jsonMode = Boolean(options.json);
|
|
54
|
+
const context = await validateProjectContextFile(targetDir);
|
|
55
|
+
const contextData = context.parsed && context.data ? context.data : {};
|
|
56
|
+
|
|
57
|
+
const plan = buildWorkflowPlan({
|
|
58
|
+
classification: options.classification || contextData.classification || 'MICRO',
|
|
59
|
+
projectType: contextData.project_type || options['project-type'] || 'web_app',
|
|
60
|
+
frameworkInstalled:
|
|
61
|
+
contextData.framework_installed !== undefined
|
|
62
|
+
? contextData.framework_installed
|
|
63
|
+
: options['framework-installed'] === 'true',
|
|
64
|
+
web3Enabled:
|
|
65
|
+
contextData.web3_enabled !== undefined
|
|
66
|
+
? contextData.web3_enabled
|
|
67
|
+
: options['web3-enabled'] === 'true'
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const output = {
|
|
71
|
+
ok: true,
|
|
72
|
+
targetDir,
|
|
73
|
+
contextExists: context.exists,
|
|
74
|
+
contextParsed: context.parsed,
|
|
75
|
+
classification: plan.classification,
|
|
76
|
+
sequence: plan.sequence,
|
|
77
|
+
commands: plan.commands,
|
|
78
|
+
notes: plan.noteKeys.map((key) => t(`workflow_plan.note_${key}`)),
|
|
79
|
+
noteKeys: plan.noteKeys
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (jsonMode) {
|
|
83
|
+
return output;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!context.exists) {
|
|
87
|
+
logger.log(t('workflow_plan.context_missing'));
|
|
88
|
+
}
|
|
89
|
+
logger.log(t('workflow_plan.title', { classification: plan.classification }));
|
|
90
|
+
for (const command of plan.commands) {
|
|
91
|
+
logger.log(t('workflow_plan.command_line', { command }));
|
|
92
|
+
}
|
|
93
|
+
if (output.notes.length > 0) {
|
|
94
|
+
logger.log(t('workflow_plan.notes'));
|
|
95
|
+
for (const note of output.notes) {
|
|
96
|
+
logger.log(t('workflow_plan.note_line', { note }));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return output;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
runWorkflowPlan,
|
|
105
|
+
normalizeClassification,
|
|
106
|
+
buildWorkflowPlan,
|
|
107
|
+
WORKFLOW_BY_CLASSIFICATION
|
|
108
|
+
};
|