@jaimevalasek/aioson 1.21.7 → 1.22.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 +39 -2
- package/docs/en/1-understand/ecosystem-map.md +1 -1
- package/docs/en/2-start/initial-decisions.md +1 -1
- package/docs/en/4-agents/README.md +8 -7
- package/docs/en/4-agents/discovery-design-doc.md +150 -0
- package/docs/en/5-reference/cli-reference.md +42 -16
- package/docs/en/README.md +2 -2
- package/docs/pt/4-agentes/README.md +8 -6
- package/docs/pt/4-agentes/briefing-refiner.md +122 -0
- package/docs/pt/4-agentes/discovery-design-doc.md +133 -74
- package/docs/pt/4-agentes/scope-check.md +65 -0
- package/docs/pt/5-referencia/README.md +1 -0
- package/docs/pt/5-referencia/comandos-cli.md +5 -4
- package/docs/pt/5-referencia/feature-archive.md +1 -0
- package/docs/pt/5-referencia/feature-export.md +155 -0
- package/docs/pt/README.md +2 -2
- package/docs/pt/agentes.md +3 -1
- package/package.json +1 -1
- package/src/agent-manifests.js +14 -3
- package/src/agents.js +21 -20
- package/src/cli.js +72 -52
- package/src/commands/briefing.js +28 -150
- package/src/commands/commit-prepare.js +5 -2
- package/src/commands/feature-archive.js +48 -12
- package/src/commands/feature-close.js +40 -0
- package/src/commands/feature-export.js +242 -0
- package/src/commands/gate-check.js +8 -3
- package/src/commands/git-guard.js +58 -0
- package/src/commands/harness-gate.js +120 -0
- package/src/commands/harness-status.js +157 -0
- package/src/commands/harness.js +18 -1
- package/src/commands/live.js +120 -115
- package/src/commands/parallel-doctor.js +2 -1
- package/src/commands/pulse-update.js +2 -2
- package/src/commands/scan-project.js +12 -2
- package/src/commands/self-implement-loop.js +305 -5
- package/src/commands/workflow-next.js +477 -425
- package/src/constants.js +21 -11
- package/src/context-search.js +3 -0
- package/src/doctor.js +24 -8
- package/src/dossier/schema.js +4 -3
- package/src/harness/active-contract.js +41 -0
- package/src/harness/attempt-artifacts.js +95 -0
- package/src/harness/budget-guard.js +127 -0
- package/src/harness/circuit-breaker.js +7 -0
- package/src/harness/contract-schema.js +324 -0
- package/src/harness/criteria-runner.js +136 -0
- package/src/harness/git-baseline.js +204 -0
- package/src/harness/glob-match.js +126 -0
- package/src/harness/guard-events.js +71 -0
- package/src/harness/human-gate.js +182 -0
- package/src/harness/scope-guard.js +115 -0
- package/src/i18n/messages/en.js +24 -21
- package/src/i18n/messages/es.js +11 -9
- package/src/i18n/messages/fr.js +11 -9
- package/src/i18n/messages/pt-BR.js +24 -21
- package/src/lib/briefing-refiner/apply-feedback.js +134 -0
- package/src/lib/briefing-refiner/briefing-paths.js +41 -0
- package/src/lib/briefing-refiner/briefing-registry.js +204 -0
- package/src/lib/briefing-refiner/briefing-sections.js +110 -0
- package/src/lib/briefing-refiner/feedback-schema.js +122 -0
- package/src/lib/briefing-refiner/refinement-report.js +39 -0
- package/src/lib/briefing-refiner/review-html.js +230 -0
- package/src/lib/dev-resume.js +94 -45
- package/src/parser.js +8 -5
- package/src/preflight-engine.js +88 -84
- package/src/runtime-store.js +2 -0
- package/src/sandbox.js +17 -3
- package/template/.aioson/agents/analyst.md +27 -23
- package/template/.aioson/agents/architect.md +7 -3
- package/template/.aioson/agents/briefing-refiner.md +121 -0
- package/template/.aioson/agents/briefing.md +83 -74
- package/template/.aioson/agents/committer.md +8 -0
- package/template/.aioson/agents/copywriter.md +19 -7
- package/template/.aioson/agents/design-hybrid-forge.md +16 -5
- package/template/.aioson/agents/dev.md +68 -66
- package/template/.aioson/agents/deyvin.md +97 -90
- package/template/.aioson/agents/discover.md +2 -2
- package/template/.aioson/agents/discovery-design-doc.md +34 -30
- package/template/.aioson/agents/genome.md +82 -71
- package/template/.aioson/agents/neo.md +11 -3
- package/template/.aioson/agents/orache.md +10 -0
- package/template/.aioson/agents/orchestrator.md +68 -68
- package/template/.aioson/agents/pentester.md +15 -6
- package/template/.aioson/agents/pm.md +30 -25
- package/template/.aioson/agents/product.md +108 -108
- package/template/.aioson/agents/profiler-enricher.md +10 -0
- package/template/.aioson/agents/profiler-forge.md +10 -0
- package/template/.aioson/agents/profiler-researcher.md +11 -0
- package/template/.aioson/agents/qa.md +28 -20
- package/template/.aioson/agents/scope-check.md +176 -164
- package/template/.aioson/agents/setup.md +11 -1
- package/template/.aioson/agents/sheldon.md +38 -38
- package/template/.aioson/agents/site-forge.md +15 -6
- package/template/.aioson/agents/squad.md +12 -0
- package/template/.aioson/agents/tester.md +209 -209
- package/template/.aioson/agents/ux-ui.md +2 -2
- package/template/.aioson/agents/validator.md +10 -2
- package/template/.aioson/config.md +31 -28
- package/template/.aioson/docs/autopilot-handoff.md +46 -0
- package/template/.aioson/docs/dossier/agent-templates.md +191 -0
- package/template/.aioson/docs/dossier/schema.md +218 -0
- package/template/.claude/commands/aioson/agent/briefing-refiner.md +17 -0
- package/template/AGENTS.md +50 -47
- package/template/CLAUDE.md +29 -27
package/src/cli.js
CHANGED
|
@@ -82,12 +82,12 @@ const { runSquadWebhook } = require('./commands/squad-webhook');
|
|
|
82
82
|
const { runSquadBus } = require('./commands/squad-bus');
|
|
83
83
|
const { runSquadAutorun } = require('./commands/squad-autorun');
|
|
84
84
|
const { runSquadDependencyGraph } = require('./commands/squad-dependency-graph');
|
|
85
|
-
const { runSquadToolRegister } = require('./commands/squad-tool-register');
|
|
86
|
-
const { runSquadReview } = require('./commands/squad-review');
|
|
87
|
-
const { runAgentAudit } = require('./commands/agent-audit');
|
|
88
|
-
const { runSkillAudit } = require('./commands/skill-audit');
|
|
89
|
-
const { runQualityAudit } = require('./commands/quality-audit');
|
|
90
|
-
const { runBriefGen } = require('./commands/brief-gen');
|
|
85
|
+
const { runSquadToolRegister } = require('./commands/squad-tool-register');
|
|
86
|
+
const { runSquadReview } = require('./commands/squad-review');
|
|
87
|
+
const { runAgentAudit } = require('./commands/agent-audit');
|
|
88
|
+
const { runSkillAudit } = require('./commands/skill-audit');
|
|
89
|
+
const { runQualityAudit } = require('./commands/quality-audit');
|
|
90
|
+
const { runBriefGen } = require('./commands/brief-gen');
|
|
91
91
|
const { runHarnessInit, runHarnessValidate, runHarnessApplyValidation } = require('./commands/harness');
|
|
92
92
|
const { runVerifyGate } = require('./commands/verify-gate');
|
|
93
93
|
const {
|
|
@@ -194,6 +194,7 @@ const { runOpReinforce } = require('./commands/op-reinforce');
|
|
|
194
194
|
const { runOpMigrate } = require('./commands/op-migrate');
|
|
195
195
|
const { runFeatureClose } = require('./commands/feature-close');
|
|
196
196
|
const { runFeatureArchive, runFeatureSweep } = require('./commands/feature-archive');
|
|
197
|
+
const { runFeatureExport } = require('./commands/feature-export');
|
|
197
198
|
const { runDossierInit, runDossierShow, runDossierAddFinding, runDossierAddCodemap, runDossierLinkRule, runDossierCompact } = require('./commands/dossier');
|
|
198
199
|
const { runDossierAddResearch } = require('./commands/dossier-add-research');
|
|
199
200
|
const { runDossierAudit } = require('./commands/dossier-audit');
|
|
@@ -204,10 +205,10 @@ const { runGateApprove } = require('./commands/gate-approve');
|
|
|
204
205
|
const { runArtifactValidate } = require('./commands/artifact-validate');
|
|
205
206
|
const { runWorkflowExecute } = require('./commands/workflow-execute');
|
|
206
207
|
const { runRunnerQueueFromPlan } = require('./commands/runner-queue-from-plan');
|
|
207
|
-
const { runLearningAutoPromote } = require('./commands/learning-auto-promote');
|
|
208
|
-
const { runBriefValidate } = require('./commands/brief-validate');
|
|
209
|
-
const { runIntakeAsk } = require('./commands/intake-ask');
|
|
210
|
-
const { runPreflightContext } = require('./commands/preflight-context');
|
|
208
|
+
const { runLearningAutoPromote } = require('./commands/learning-auto-promote');
|
|
209
|
+
const { runBriefValidate } = require('./commands/brief-validate');
|
|
210
|
+
const { runIntakeAsk } = require('./commands/intake-ask');
|
|
211
|
+
const { runPreflightContext } = require('./commands/preflight-context');
|
|
211
212
|
const { runContextCompact } = require('./commands/context-compact');
|
|
212
213
|
const { runSquadScaffold } = require('./commands/squad-scaffold');
|
|
213
214
|
const { runPatternDetect } = require('./commands/pattern-detect');
|
|
@@ -385,27 +386,33 @@ const JSON_SUPPORTED_COMMANDS = new Set([
|
|
|
385
386
|
'squad-tool-register',
|
|
386
387
|
'squad:review',
|
|
387
388
|
'squad-review',
|
|
388
|
-
'agent:audit',
|
|
389
|
-
'agent-audit',
|
|
390
|
-
'skill:audit',
|
|
391
|
-
'skill-audit',
|
|
392
|
-
'quality:audit',
|
|
393
|
-
'quality-audit',
|
|
394
|
-
'brief:gen',
|
|
389
|
+
'agent:audit',
|
|
390
|
+
'agent-audit',
|
|
391
|
+
'skill:audit',
|
|
392
|
+
'skill-audit',
|
|
393
|
+
'quality:audit',
|
|
394
|
+
'quality-audit',
|
|
395
|
+
'brief:gen',
|
|
395
396
|
'harness:init',
|
|
396
397
|
'harness-init',
|
|
397
398
|
'harness:validate',
|
|
398
399
|
'harness-validate',
|
|
399
400
|
'harness:apply-validation',
|
|
400
401
|
'harness-apply-validation',
|
|
402
|
+
'harness:approve',
|
|
403
|
+
'harness-approve',
|
|
404
|
+
'harness:reject',
|
|
405
|
+
'harness-reject',
|
|
406
|
+
'harness:status',
|
|
407
|
+
'harness-status',
|
|
401
408
|
'brief-gen',
|
|
402
409
|
'verify:gate',
|
|
403
410
|
'verify-gate',
|
|
404
|
-
'brief:validate',
|
|
405
|
-
'brief-validate',
|
|
406
|
-
'intake:ask',
|
|
407
|
-
'intake-ask',
|
|
408
|
-
'preflight:context',
|
|
411
|
+
'brief:validate',
|
|
412
|
+
'brief-validate',
|
|
413
|
+
'intake:ask',
|
|
414
|
+
'intake-ask',
|
|
415
|
+
'preflight:context',
|
|
409
416
|
'preflight-context',
|
|
410
417
|
'context:compact',
|
|
411
418
|
'context-compact',
|
|
@@ -640,6 +647,8 @@ const JSON_SUPPORTED_COMMANDS = new Set([
|
|
|
640
647
|
'feature-archive',
|
|
641
648
|
'feature:sweep',
|
|
642
649
|
'feature-sweep',
|
|
650
|
+
'feature:export',
|
|
651
|
+
'feature-export',
|
|
643
652
|
'dossier:init',
|
|
644
653
|
'dossier-init',
|
|
645
654
|
'dossier:show',
|
|
@@ -827,11 +836,11 @@ function printHelp(t, logger) {
|
|
|
827
836
|
logHelpLine(t, logger, 'cli.help_squad_daemon');
|
|
828
837
|
logHelpLine(t, logger, 'cli.help_squad_mcp');
|
|
829
838
|
logHelpLine(t, logger, 'cli.help_squad_roi');
|
|
830
|
-
logHelpLine(t, logger, 'cli.help_squad_score');
|
|
831
|
-
logHelpLine(t, logger, 'cli.help_squad_learning');
|
|
832
|
-
logHelpLine(t, logger, 'cli.help_agent_audit');
|
|
833
|
-
logHelpLine(t, logger, 'cli.help_quality_audit');
|
|
834
|
-
logHelpLine(t, logger, 'cli.help_learning');
|
|
839
|
+
logHelpLine(t, logger, 'cli.help_squad_score');
|
|
840
|
+
logHelpLine(t, logger, 'cli.help_squad_learning');
|
|
841
|
+
logHelpLine(t, logger, 'cli.help_agent_audit');
|
|
842
|
+
logHelpLine(t, logger, 'cli.help_quality_audit');
|
|
843
|
+
logHelpLine(t, logger, 'cli.help_learning');
|
|
835
844
|
logHelpLine(t, logger, 'cli.help_runtime_init');
|
|
836
845
|
logHelpLine(t, logger, 'cli.help_runtime_ingest');
|
|
837
846
|
logHelpLine(t, logger, 'cli.help_runtime_task_start');
|
|
@@ -840,10 +849,10 @@ function printHelp(t, logger) {
|
|
|
840
849
|
logHelpLine(t, logger, 'cli.help_runtime_task_finish');
|
|
841
850
|
logHelpLine(t, logger, 'cli.help_runtime_finish');
|
|
842
851
|
logHelpLine(t, logger, 'cli.help_runtime_task_fail');
|
|
843
|
-
logHelpLine(t, logger, 'cli.help_runtime_fail');
|
|
844
|
-
logHelpLine(t, logger, 'cli.help_runtime_status');
|
|
845
|
-
logHelpLine(t, logger, 'cli.help_agent_recover');
|
|
846
|
-
logHelpLine(t, logger, 'cli.help_runtime_session_start');
|
|
852
|
+
logHelpLine(t, logger, 'cli.help_runtime_fail');
|
|
853
|
+
logHelpLine(t, logger, 'cli.help_runtime_status');
|
|
854
|
+
logHelpLine(t, logger, 'cli.help_agent_recover');
|
|
855
|
+
logHelpLine(t, logger, 'cli.help_runtime_session_start');
|
|
847
856
|
logHelpLine(t, logger, 'cli.help_runtime_session_log');
|
|
848
857
|
logHelpLine(t, logger, 'cli.help_runtime_session_finish');
|
|
849
858
|
logHelpLine(t, logger, 'cli.help_runtime_session_status');
|
|
@@ -855,10 +864,10 @@ function printHelp(t, logger) {
|
|
|
855
864
|
logHelpLine(t, logger, 'cli.help_scaffold_complete');
|
|
856
865
|
logHelpLine(t, logger, 'cli.help_runtime_backup');
|
|
857
866
|
logHelpLine(t, logger, 'cli.help_runtime_restore');
|
|
858
|
-
logHelpLine(t, logger, 'cli.help_skill_install');
|
|
859
|
-
logHelpLine(t, logger, 'cli.help_skill_list');
|
|
860
|
-
logHelpLine(t, logger, 'cli.help_skill_remove');
|
|
861
|
-
logHelpLine(t, logger, 'cli.help_skill_audit');
|
|
867
|
+
logHelpLine(t, logger, 'cli.help_skill_install');
|
|
868
|
+
logHelpLine(t, logger, 'cli.help_skill_list');
|
|
869
|
+
logHelpLine(t, logger, 'cli.help_skill_remove');
|
|
870
|
+
logHelpLine(t, logger, 'cli.help_skill_audit');
|
|
862
871
|
logHelpLine(t, logger, 'cli.help_design_hybrid_options');
|
|
863
872
|
logHelpLine(t, logger, 'cli.help_cloud_import_squad');
|
|
864
873
|
logHelpLine(t, logger, 'cli.help_cloud_import_genome');
|
|
@@ -1239,29 +1248,38 @@ async function main() {
|
|
|
1239
1248
|
result = await runSquadToolRegister({ args, options, logger: commandLogger });
|
|
1240
1249
|
} else if (command === 'squad:review' || command === 'squad-review') {
|
|
1241
1250
|
result = await runSquadReview({ args, options, logger: commandLogger });
|
|
1242
|
-
} else if (command === 'agent:audit' || command === 'agent-audit') {
|
|
1243
|
-
result = await runAgentAudit({ args, options, logger: commandLogger });
|
|
1244
|
-
} else if (command === 'skill:audit' || command === 'skill-audit') {
|
|
1245
|
-
result = await runSkillAudit({ args, options, logger: commandLogger });
|
|
1246
|
-
} else if (command === 'quality:audit' || command === 'quality-audit') {
|
|
1247
|
-
result = await runQualityAudit({ args, options, logger: commandLogger });
|
|
1248
|
-
} else if (command === 'brief:gen' || command === 'brief-gen') {
|
|
1249
|
-
result = await runBriefGen({ args, options, logger: commandLogger, t });
|
|
1251
|
+
} else if (command === 'agent:audit' || command === 'agent-audit') {
|
|
1252
|
+
result = await runAgentAudit({ args, options, logger: commandLogger });
|
|
1253
|
+
} else if (command === 'skill:audit' || command === 'skill-audit') {
|
|
1254
|
+
result = await runSkillAudit({ args, options, logger: commandLogger });
|
|
1255
|
+
} else if (command === 'quality:audit' || command === 'quality-audit') {
|
|
1256
|
+
result = await runQualityAudit({ args, options, logger: commandLogger });
|
|
1257
|
+
} else if (command === 'brief:gen' || command === 'brief-gen') {
|
|
1258
|
+
result = await runBriefGen({ args, options, logger: commandLogger, t });
|
|
1250
1259
|
} else if (command === 'harness:init' || command === 'harness-init') {
|
|
1251
1260
|
result = await runHarnessInit({ args, options, logger: commandLogger, t });
|
|
1252
1261
|
} else if (command === 'harness:validate' || command === 'harness-validate') {
|
|
1253
1262
|
result = await runHarnessValidate({ args, options, logger: commandLogger, t });
|
|
1254
1263
|
} else if (command === 'harness:apply-validation' || command === 'harness-apply-validation') {
|
|
1255
1264
|
result = await runHarnessApplyValidation({ args, options, logger: commandLogger, t });
|
|
1265
|
+
} else if (command === 'harness:approve' || command === 'harness-approve') {
|
|
1266
|
+
const { runHarnessApprove } = require('./commands/harness-gate');
|
|
1267
|
+
result = await runHarnessApprove({ args, options, logger: commandLogger, t });
|
|
1268
|
+
} else if (command === 'harness:reject' || command === 'harness-reject') {
|
|
1269
|
+
const { runHarnessReject } = require('./commands/harness-gate');
|
|
1270
|
+
result = await runHarnessReject({ args, options, logger: commandLogger, t });
|
|
1271
|
+
} else if (command === 'harness:status' || command === 'harness-status') {
|
|
1272
|
+
const { runHarnessStatus } = require('./commands/harness-status');
|
|
1273
|
+
result = await runHarnessStatus({ args, options, logger: commandLogger, t });
|
|
1256
1274
|
} else if (command === 'verify:gate' || command === 'verify-gate') {
|
|
1257
1275
|
result = await runVerifyGate({ args, options, logger: commandLogger, t });
|
|
1258
1276
|
|
|
1259
|
-
} else if (command === 'brief:validate' || command === 'brief-validate') {
|
|
1260
|
-
result = await runBriefValidate({ args, options, logger: commandLogger });
|
|
1261
|
-
} else if (command === 'intake:ask' || command === 'intake-ask') {
|
|
1262
|
-
result = await runIntakeAsk({ args, options, logger: commandLogger });
|
|
1263
|
-
} else if (command === 'preflight:context' || command === 'preflight-context') {
|
|
1264
|
-
result = await runPreflightContext({ args, options, logger: commandLogger });
|
|
1277
|
+
} else if (command === 'brief:validate' || command === 'brief-validate') {
|
|
1278
|
+
result = await runBriefValidate({ args, options, logger: commandLogger });
|
|
1279
|
+
} else if (command === 'intake:ask' || command === 'intake-ask') {
|
|
1280
|
+
result = await runIntakeAsk({ args, options, logger: commandLogger });
|
|
1281
|
+
} else if (command === 'preflight:context' || command === 'preflight-context') {
|
|
1282
|
+
result = await runPreflightContext({ args, options, logger: commandLogger });
|
|
1265
1283
|
} else if (command === 'context:compact' || command === 'context-compact') {
|
|
1266
1284
|
result = await runContextCompact({ args, options, logger: commandLogger });
|
|
1267
1285
|
} else if (command === 'squad:scaffold' || command === 'squad-scaffold') {
|
|
@@ -1297,9 +1315,9 @@ async function main() {
|
|
|
1297
1315
|
result = await runSpecCheckpoint({ args, options, logger: commandLogger });
|
|
1298
1316
|
} else if (command === 'spec:tasks' || command === 'spec-tasks') {
|
|
1299
1317
|
result = await runSpecTasks({ args, options, logger: commandLogger });
|
|
1300
|
-
} else if (command.startsWith('learning:') || command === 'learning') {
|
|
1301
|
-
const sub = command === 'learning' ? (options.sub || args[1] || 'list') : command.split(':')[1];
|
|
1302
|
-
result = await runLearning({ args, options: { ...options, sub }, logger: commandLogger, t });
|
|
1318
|
+
} else if (command.startsWith('learning:') || command === 'learning') {
|
|
1319
|
+
const sub = command === 'learning' ? (options.sub || args[1] || 'list') : command.split(':')[1];
|
|
1320
|
+
result = await runLearning({ args, options: { ...options, sub }, logger: commandLogger, t });
|
|
1303
1321
|
} else if (command.startsWith('plan:') || command === 'plan') {
|
|
1304
1322
|
const sub = command === 'plan' ? (args[1] || 'show') : command.split(':')[1];
|
|
1305
1323
|
result = await runImplementationPlan({ args, options: { ...options, sub }, logger: commandLogger, t });
|
|
@@ -1523,6 +1541,8 @@ async function main() {
|
|
|
1523
1541
|
}
|
|
1524
1542
|
} else if (command === 'feature:sweep' || command === 'feature-sweep') {
|
|
1525
1543
|
result = await runFeatureSweep({ args, options, logger: commandLogger });
|
|
1544
|
+
} else if (command === 'feature:export' || command === 'feature-export') {
|
|
1545
|
+
result = await runFeatureExport({ args, options, logger: commandLogger });
|
|
1526
1546
|
} else if (command === 'dossier:init' || command === 'dossier-init') {
|
|
1527
1547
|
result = await runDossierInit({ args, options, logger: commandLogger });
|
|
1528
1548
|
} else if (command === 'dossier:show' || command === 'dossier-show') {
|
package/src/commands/briefing.js
CHANGED
|
@@ -14,134 +14,13 @@
|
|
|
14
14
|
* Format: YAML frontmatter (briefings: array) + Markdown table
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
const fs = require('node:fs/promises');
|
|
18
17
|
const path = require('node:path');
|
|
19
18
|
const readline = require('node:readline');
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ─── YAML frontmatter parser (briefings-specific) ────────────────────────────
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Parse .aioson/briefings/config.md frontmatter.
|
|
31
|
-
* Returns { updated_at, briefings: [...] }
|
|
32
|
-
*
|
|
33
|
-
* Expected format:
|
|
34
|
-
* ---
|
|
35
|
-
* updated_at: 2026-04-10
|
|
36
|
-
* briefings:
|
|
37
|
-
* - slug: foo
|
|
38
|
-
* status: draft
|
|
39
|
-
* source_plans: [plans/x.md]
|
|
40
|
-
* created_at: "2026-04-10"
|
|
41
|
-
* approved_at: null
|
|
42
|
-
* prd_generated: null
|
|
43
|
-
* ---
|
|
44
|
-
*/
|
|
45
|
-
function parseConfigFrontmatter(content) {
|
|
46
|
-
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
47
|
-
if (!match) return null;
|
|
48
|
-
|
|
49
|
-
const raw = match[1];
|
|
50
|
-
const lines = raw.split(/\r?\n/);
|
|
51
|
-
|
|
52
|
-
const result = { updated_at: null, briefings: [] };
|
|
53
|
-
let inBriefings = false;
|
|
54
|
-
let currentItem = null;
|
|
55
|
-
|
|
56
|
-
for (const line of lines) {
|
|
57
|
-
if (/^updated_at:\s*(.*)$/.test(line)) {
|
|
58
|
-
result.updated_at = line.replace(/^updated_at:\s*/, '').trim().replace(/^["']|["']$/g, '');
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (/^briefings:/.test(line)) {
|
|
63
|
-
inBriefings = true;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!inBriefings) continue;
|
|
68
|
-
|
|
69
|
-
// New item in array
|
|
70
|
-
if (/^\s{2}-\s+slug:\s*(.+)/.test(line)) {
|
|
71
|
-
if (currentItem) result.briefings.push(currentItem);
|
|
72
|
-
const slug = line.replace(/^\s{2}-\s+slug:\s*/, '').trim().replace(/^["']|["']$/g, '');
|
|
73
|
-
currentItem = { slug, status: 'draft', source_plans: [], created_at: null, approved_at: null, prd_generated: null };
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (currentItem) {
|
|
78
|
-
const fieldMatch = line.match(/^\s{4}(\w+):\s*(.*)/);
|
|
79
|
-
if (!fieldMatch) continue;
|
|
80
|
-
const [, key, rawVal] = fieldMatch;
|
|
81
|
-
const val = rawVal.trim().replace(/^["']|["']$/g, '');
|
|
82
|
-
|
|
83
|
-
if (key === 'source_plans') {
|
|
84
|
-
// Parse inline array: [plans/x.md, plans/y.md]
|
|
85
|
-
const arrMatch = val.match(/^\[(.+)\]$/);
|
|
86
|
-
currentItem.source_plans = arrMatch
|
|
87
|
-
? arrMatch[1].split(',').map((s) => s.trim().replace(/^["']|["']$/g, ''))
|
|
88
|
-
: val === '' ? [] : [val];
|
|
89
|
-
} else if (val === 'null' || val === '') {
|
|
90
|
-
currentItem[key] = null;
|
|
91
|
-
} else {
|
|
92
|
-
currentItem[key] = val;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (currentItem) result.briefings.push(currentItem);
|
|
98
|
-
return result;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ─── Config serializer ────────────────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
function serializeSourcePlans(plans) {
|
|
104
|
-
if (!plans || plans.length === 0) return '[]';
|
|
105
|
-
const items = plans.map((p) => `"${p}"`).join(', ');
|
|
106
|
-
return `[${items}]`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function serializeConfigFrontmatter(data) {
|
|
110
|
-
const lines = ['---', `updated_at: ${data.updated_at || new Date().toISOString().slice(0, 10)}`, 'briefings:'];
|
|
111
|
-
|
|
112
|
-
for (const b of data.briefings) {
|
|
113
|
-
lines.push(` - slug: ${b.slug}`);
|
|
114
|
-
lines.push(` status: ${b.status}`);
|
|
115
|
-
lines.push(` source_plans: ${serializeSourcePlans(b.source_plans)}`);
|
|
116
|
-
lines.push(` created_at: "${b.created_at || ''}"`);
|
|
117
|
-
lines.push(` approved_at: ${b.approved_at ? `"${b.approved_at}"` : 'null'}`);
|
|
118
|
-
lines.push(` prd_generated: ${b.prd_generated ? `"${b.prd_generated}"` : 'null'}`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
lines.push('---');
|
|
122
|
-
return lines.join('\n');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ─── Markdown table builder ───────────────────────────────────────────────────
|
|
126
|
-
|
|
127
|
-
function buildMarkdownTable(briefings) {
|
|
128
|
-
const header = '| slug | status | source_plans | created | approved | prd |';
|
|
129
|
-
const sep = '|------|--------|-------------|---------|----------|-----|';
|
|
130
|
-
const rows = briefings.map((b) => {
|
|
131
|
-
const sources = (b.source_plans || []).join(', ') || '—';
|
|
132
|
-
return `| ${b.slug} | ${b.status} | ${sources} | ${b.created_at || '—'} | ${b.approved_at || '—'} | ${b.prd_generated || '—'} |`;
|
|
133
|
-
});
|
|
134
|
-
return [header, sep, ...rows].join('\n');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// ─── Config writer ────────────────────────────────────────────────────────────
|
|
138
|
-
|
|
139
|
-
async function writeConfig(configFile, data) {
|
|
140
|
-
const frontmatter = serializeConfigFrontmatter(data);
|
|
141
|
-
const table = buildMarkdownTable(data.briefings);
|
|
142
|
-
const body = `\n# Briefings Registry\n\n${table}\n`;
|
|
143
|
-
await fs.writeFile(configFile, `${frontmatter}\n${body}`, 'utf8');
|
|
144
|
-
}
|
|
19
|
+
const {
|
|
20
|
+
configPath: registryConfigPath,
|
|
21
|
+
readBriefingRegistry,
|
|
22
|
+
writeBriefingRegistry
|
|
23
|
+
} = require('../lib/briefing-refiner/briefing-registry');
|
|
145
24
|
|
|
146
25
|
// ─── Interactive prompt helpers ───────────────────────────────────────────────
|
|
147
26
|
|
|
@@ -205,23 +84,21 @@ function promptCheckboxDeselect(items, promptText) {
|
|
|
205
84
|
async function runBriefingApprove({ args, options = {}, logger }) {
|
|
206
85
|
const projectDir = path.resolve(process.cwd(), args[0] || '.');
|
|
207
86
|
const slugOpt = String(options.slug || '').trim() || null;
|
|
208
|
-
const configFile =
|
|
87
|
+
const configFile = registryConfigPath(projectDir);
|
|
209
88
|
|
|
210
89
|
// ── Read config ────────────────────────────────────────────────────────────
|
|
211
|
-
let
|
|
90
|
+
let data;
|
|
212
91
|
try {
|
|
213
|
-
|
|
214
|
-
} catch {
|
|
92
|
+
data = await readBriefingRegistry(projectDir);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error && error.code === 'invalid_frontmatter') {
|
|
95
|
+
logger.error('config.md com frontmatter inválido. Verifique o arquivo manualmente.');
|
|
96
|
+
return { ok: false, error: 'invalid_frontmatter' };
|
|
97
|
+
}
|
|
215
98
|
logger.error('Nenhum briefing encontrado. Ative @briefing para criar o primeiro briefing.');
|
|
216
99
|
return { ok: false, error: 'no_config' };
|
|
217
100
|
}
|
|
218
101
|
|
|
219
|
-
const data = parseConfigFrontmatter(raw);
|
|
220
|
-
if (!data) {
|
|
221
|
-
logger.error('config.md com frontmatter inválido. Verifique o arquivo manualmente.');
|
|
222
|
-
return { ok: false, error: 'invalid_frontmatter' };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
102
|
const drafts = data.briefings.filter((b) => b.status === 'draft');
|
|
226
103
|
|
|
227
104
|
if (drafts.length === 0) {
|
|
@@ -259,7 +136,7 @@ async function runBriefingApprove({ args, options = {}, logger }) {
|
|
|
259
136
|
briefingEntry.approved_at = today;
|
|
260
137
|
data.updated_at = today;
|
|
261
138
|
|
|
262
|
-
await
|
|
139
|
+
await writeBriefingRegistry(projectDir, data);
|
|
263
140
|
|
|
264
141
|
logger.log(`✓ Briefing "${target.slug}" aprovado.`);
|
|
265
142
|
logger.log(' Ative @product para gerar o PRD — ele detectará o briefing aprovado automaticamente.');
|
|
@@ -272,25 +149,26 @@ async function runBriefingApprove({ args, options = {}, logger }) {
|
|
|
272
149
|
async function runBriefingUnapprove({ args, options = {}, logger }) {
|
|
273
150
|
const projectDir = path.resolve(process.cwd(), args[0] || '.');
|
|
274
151
|
const slugOpt = String(options.slug || '').trim() || null;
|
|
275
|
-
const configFile =
|
|
152
|
+
const configFile = registryConfigPath(projectDir);
|
|
276
153
|
|
|
277
154
|
// ── Read config ────────────────────────────────────────────────────────────
|
|
278
|
-
let
|
|
155
|
+
let data;
|
|
279
156
|
try {
|
|
280
|
-
|
|
281
|
-
} catch {
|
|
157
|
+
data = await readBriefingRegistry(projectDir);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error && error.code === 'invalid_frontmatter') {
|
|
160
|
+
logger.error('config.md com frontmatter inválido. Verifique o arquivo manualmente.');
|
|
161
|
+
return { ok: false, error: 'invalid_frontmatter' };
|
|
162
|
+
}
|
|
282
163
|
logger.error('Nenhum briefing encontrado. Ative @briefing para criar o primeiro briefing.');
|
|
283
164
|
return { ok: false, error: 'no_config' };
|
|
284
165
|
}
|
|
285
166
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
// Only approved and non-implemented briefings can be unapproved
|
|
293
|
-
const approveds = data.briefings.filter((b) => b.status === 'approved');
|
|
167
|
+
// Only approved briefings that have NOT yet generated a PRD can be unapproved.
|
|
168
|
+
// Reverting a prd_generated briefing would desync it from its downstream PRD,
|
|
169
|
+
// so it is excluded here (mirrors the registry-level guard in
|
|
170
|
+
// returnApprovedBriefingToDraft).
|
|
171
|
+
const approveds = data.briefings.filter((b) => b.status === 'approved' && !b.prd_generated);
|
|
294
172
|
|
|
295
173
|
if (approveds.length === 0) {
|
|
296
174
|
logger.log('Nenhum briefing aprovado disponível para retornar a draft.');
|
|
@@ -333,7 +211,7 @@ async function runBriefingUnapprove({ args, options = {}, logger }) {
|
|
|
333
211
|
}
|
|
334
212
|
data.updated_at = today;
|
|
335
213
|
|
|
336
|
-
await
|
|
214
|
+
await writeBriefingRegistry(projectDir, data);
|
|
337
215
|
|
|
338
216
|
const names = targets.map((b) => b.slug);
|
|
339
217
|
logger.log(`✓ ${names.length === 1 ? `Briefing "${names[0]}" retornado` : `Briefings retornados`} para draft: ${names.join(', ')}`);
|
|
@@ -33,7 +33,10 @@ function resolveGitRoot(projectDir) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function parseGitStatusShort(gitRoot) {
|
|
36
|
-
|
|
36
|
+
// core.quotePath=false makes git emit non-ASCII paths literally (UTF-8) instead
|
|
37
|
+
// of C-style double-quoted/escaped form, so the parsed path is the real path and
|
|
38
|
+
// `git add -- <path>` later targets the correct file.
|
|
39
|
+
const output = runGit(gitRoot, ['-c', 'core.quotePath=false', 'status', '--short']);
|
|
37
40
|
const lines = output.split('\n').filter(Boolean);
|
|
38
41
|
const staged = [];
|
|
39
42
|
const unstaged = [];
|
|
@@ -223,7 +226,7 @@ async function resolveGuardFindings(gitRoot, guardResult, logger) {
|
|
|
223
226
|
logger.log(` ✔ Adicionado a contentAllowPaths: ${finding.path}`);
|
|
224
227
|
}
|
|
225
228
|
} else if (action === 'block') {
|
|
226
|
-
const pattern =
|
|
229
|
+
const pattern = finding.path;
|
|
227
230
|
if (!guardConfig.blockPaths.includes(pattern)) {
|
|
228
231
|
guardConfig.blockPaths.push(pattern);
|
|
229
232
|
guardConfigChanged = true;
|
|
@@ -131,6 +131,42 @@ async function findArchivedFiles(archiveDir) {
|
|
|
131
131
|
return entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Enumerate every artefact that belongs to a feature slug — the exact surface
|
|
136
|
+
* `feature:archive` would move — but as a pure read-only discovery, for
|
|
137
|
+
* non-destructive consumers (e.g. `feature:export`). Never mutates the tree.
|
|
138
|
+
*
|
|
139
|
+
* Reuses the slug-collision guard (readOtherSlugs/findSlugFiles) so a longer
|
|
140
|
+
* sibling slug (`checkout-v2`) never leaks into `checkout`.
|
|
141
|
+
*
|
|
142
|
+
* @returns {{ rootFiles: string[], dirs: Array<{label:string, sourceDir:string}>, doneDir: string|null }}
|
|
143
|
+
* rootFiles are bare names under `.aioson/context/`; dirs/doneDir are absolute paths.
|
|
144
|
+
*/
|
|
145
|
+
async function collectFeatureArtifacts({ ctxDir, targetDir, slug, includeDone = true }) {
|
|
146
|
+
const featuresPath = path.join(ctxDir, 'features.md');
|
|
147
|
+
const otherSlugs = await readOtherSlugs(featuresPath, slug);
|
|
148
|
+
const rootFiles = await findSlugFiles(ctxDir, slug, otherSlugs);
|
|
149
|
+
|
|
150
|
+
const slugDirCandidates = [
|
|
151
|
+
{ label: 'dossier', sourceDir: path.join(ctxDir, 'features', slug) },
|
|
152
|
+
{ label: 'plans', sourceDir: path.join(targetDir, '.aioson', 'plans', slug) },
|
|
153
|
+
{ label: 'briefings', sourceDir: path.join(targetDir, '.aioson', 'briefings', slug) }
|
|
154
|
+
];
|
|
155
|
+
const dirs = [];
|
|
156
|
+
for (const d of slugDirCandidates) {
|
|
157
|
+
// eslint-disable-next-line no-await-in-loop
|
|
158
|
+
if (await dirExists(d.sourceDir)) dirs.push(d);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let doneDir = null;
|
|
162
|
+
if (includeDone) {
|
|
163
|
+
const candidate = path.join(ctxDir, 'done', slug);
|
|
164
|
+
if (await dirExists(candidate)) doneDir = candidate;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return { rootFiles, dirs, doneDir };
|
|
168
|
+
}
|
|
169
|
+
|
|
134
170
|
async function extractSummary(prdPath) {
|
|
135
171
|
const content = await readFileSafe(prdPath);
|
|
136
172
|
if (!content) return null;
|
|
@@ -338,18 +374,18 @@ async function runFeatureArchive({ args = [], options = {}, logger }) {
|
|
|
338
374
|
action: d.action,
|
|
339
375
|
reason: d.reason
|
|
340
376
|
}));
|
|
341
|
-
const result = {
|
|
342
|
-
ok: true,
|
|
343
|
-
dryRun: true,
|
|
344
|
-
slug,
|
|
377
|
+
const result = {
|
|
378
|
+
ok: true,
|
|
379
|
+
dryRun: true,
|
|
380
|
+
slug,
|
|
345
381
|
targetDir: path.relative(targetDir, archiveDir),
|
|
346
|
-
move: toMove,
|
|
347
|
-
skip: toSkip,
|
|
348
|
-
dirs,
|
|
349
|
-
dossier: dirs.find((d) => d.label === 'dossier') || null,
|
|
350
|
-
manifestEntry: {
|
|
351
|
-
slug,
|
|
352
|
-
completed,
|
|
382
|
+
move: toMove,
|
|
383
|
+
skip: toSkip,
|
|
384
|
+
dirs,
|
|
385
|
+
dossier: dirs.find((d) => d.label === 'dossier') || null,
|
|
386
|
+
manifestEntry: {
|
|
387
|
+
slug,
|
|
388
|
+
completed,
|
|
353
389
|
files: String(toMove.length + alreadyArchived.length),
|
|
354
390
|
summary: summary || '—'
|
|
355
391
|
}
|
|
@@ -628,4 +664,4 @@ async function runFeatureSweep({ args = [], options = {}, logger }) {
|
|
|
628
664
|
return result;
|
|
629
665
|
}
|
|
630
666
|
|
|
631
|
-
module.exports = { runFeatureArchive, runFeatureSweep };
|
|
667
|
+
module.exports = { runFeatureArchive, runFeatureSweep, collectFeatureArtifacts };
|
|
@@ -333,6 +333,46 @@ async function runFeatureClose({ args, options = {}, logger }) {
|
|
|
333
333
|
const contractContent = await readFileSafe(contractPath);
|
|
334
334
|
const progressContent = await readFileSafe(progressPath);
|
|
335
335
|
|
|
336
|
+
// REQ-13 (loop-guardrails): tema `publish` é gate de COMANDO — intercepta
|
|
337
|
+
// o feature:close quando o contrato ativo o exige e não há gate publish
|
|
338
|
+
// aprovado. Nunca detectado por diff. `--force` NÃO bypassa: a aprovação
|
|
339
|
+
// humana é o propósito do gate (decisão registrada no spec da feature).
|
|
340
|
+
if (contractContent) {
|
|
341
|
+
try {
|
|
342
|
+
const contract = JSON.parse(contractContent);
|
|
343
|
+
const requiredFor = contract && contract.human_gate && Array.isArray(contract.human_gate.required_for)
|
|
344
|
+
? contract.human_gate.required_for
|
|
345
|
+
: [];
|
|
346
|
+
if (requiredFor.includes('publish')) {
|
|
347
|
+
const { hasApprovedPublishGate, pendingGates, createGate } = require('../harness/human-gate');
|
|
348
|
+
const { emitGuardEvent } = require('../harness/guard-events');
|
|
349
|
+
if (!hasApprovedPublishGate(planDir)) {
|
|
350
|
+
let gate = pendingGates(planDir).find((g) => g.theme === 'publish');
|
|
351
|
+
if (!gate) {
|
|
352
|
+
gate = createGate(planDir, {
|
|
353
|
+
theme: 'publish',
|
|
354
|
+
attempt: 0,
|
|
355
|
+
triggeredBy: [],
|
|
356
|
+
diffSummary: `feature:close ${slug}`,
|
|
357
|
+
runId: null
|
|
358
|
+
});
|
|
359
|
+
await emitGuardEvent(targetDir, {
|
|
360
|
+
eventType: 'human_gate_requested',
|
|
361
|
+
message: `publish gate ${gate.id} requested by feature:close`,
|
|
362
|
+
payload: { slug, gate_id: gate.id, theme: 'publish' }
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
const errMsg = `[Publish Gate BLOCKED] Feature "${slug}" requires human approval before closing (human_gate.required_for includes "publish"). Approve with: aioson harness:approve . --slug=${slug} --gate=${gate.id}`;
|
|
366
|
+
if (options.json) {
|
|
367
|
+
return { ok: false, reason: 'publish_gate_pending', feature: slug, gate: gate.id, error: errMsg };
|
|
368
|
+
}
|
|
369
|
+
logger.log(errMsg);
|
|
370
|
+
return { ok: false };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} catch { /* contrato ilegível — o done gate abaixo lida com o estado */ }
|
|
374
|
+
}
|
|
375
|
+
|
|
336
376
|
if (contractContent && progressContent) {
|
|
337
377
|
const force = options.force === true;
|
|
338
378
|
let progress = null;
|