@williambeto/ai-workflow 2.6.4 → 2.7.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 +50 -0
- package/bin/ai-workflow.js +342 -213
- package/bin/ai-workflow.js.map +1 -1
- package/{chunk-6EMG6FAO.js → chunk-BDZPUAEX.js} +2 -2
- package/{chunk-UHLZX2EG.js → chunk-W4RTQWVQ.js} +2 -2
- package/{chunk-2AOV2ATY.js → chunk-XW747GIG.js} +11 -5
- package/core/index.js +2 -2
- package/package.json +1 -1
- package/{validate-Q2NLLQ5G.js → validate-A46WUBVZ.js} +3 -3
package/bin/ai-workflow.js
CHANGED
|
@@ -21,13 +21,13 @@ import {
|
|
|
21
21
|
isTerminalFailure,
|
|
22
22
|
readPackageFile,
|
|
23
23
|
resolveWorkflowProfile
|
|
24
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-W4RTQWVQ.js";
|
|
25
25
|
import {
|
|
26
26
|
EvidenceCollector,
|
|
27
27
|
QualityGuard,
|
|
28
28
|
ValidationPlanner
|
|
29
|
-
} from "../chunk-
|
|
30
|
-
import "../chunk-
|
|
29
|
+
} from "../chunk-BDZPUAEX.js";
|
|
30
|
+
import "../chunk-XW747GIG.js";
|
|
31
31
|
import "../chunk-5WRI5ZAA.js";
|
|
32
32
|
|
|
33
33
|
// src/cli/commands/init.ts
|
|
@@ -193,13 +193,14 @@ function isValidProfile(profile) {
|
|
|
193
193
|
function getTemplateFiles(profile = "standard") {
|
|
194
194
|
return PROFILE_FILES[profile] ?? PROFILE_FILES.standard;
|
|
195
195
|
}
|
|
196
|
-
function buildAiWorkflowConfig({ profile }) {
|
|
196
|
+
function buildAiWorkflowConfig({ profile, runtimes = ["opencode"] }) {
|
|
197
197
|
const version = getPackageVersion();
|
|
198
198
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
199
|
+
const primaryRuntime = runtimes.join("+");
|
|
199
200
|
return {
|
|
200
201
|
package: "@williambeto/ai-workflow",
|
|
201
202
|
version,
|
|
202
|
-
runtime:
|
|
203
|
+
runtime: primaryRuntime,
|
|
203
204
|
profile,
|
|
204
205
|
installMode: "project-local",
|
|
205
206
|
generatedAt: now,
|
|
@@ -779,7 +780,7 @@ Every task completion MUST provide the following payload:
|
|
|
779
780
|
* - Skills -> .agents/skills/
|
|
780
781
|
* - Commands -> .codex/prompts/
|
|
781
782
|
*/
|
|
782
|
-
async deploy(installRoot = ".ai-workflow") {
|
|
783
|
+
async deploy(installRoot = ".ai-workflow", force) {
|
|
783
784
|
const sourceAgentsDir = path7.join(this.cwd, installRoot, "opencode/agents");
|
|
784
785
|
const sourceSkillsDir = path7.join(this.cwd, installRoot, "opencode/skills");
|
|
785
786
|
const sourceCommandsDir = path7.join(this.cwd, installRoot, "opencode/commands");
|
|
@@ -791,7 +792,7 @@ Every task completion MUST provide the following payload:
|
|
|
791
792
|
const transformed = await this.transformAgent(path7.join(sourceAgentsDir, file));
|
|
792
793
|
await fs5.writeFile(path7.join(codexAgentsDir, `${transformed.name}.md`), transformed.content);
|
|
793
794
|
}
|
|
794
|
-
const codexSkillsDir = path7.join(this.cwd, ".
|
|
795
|
+
const codexSkillsDir = path7.join(this.cwd, ".codex/skills");
|
|
795
796
|
await fs5.mkdir(codexSkillsDir, { recursive: true });
|
|
796
797
|
const skillFolders = await fs5.readdir(sourceSkillsDir).catch(() => []);
|
|
797
798
|
for (const folder of skillFolders) {
|
|
@@ -812,7 +813,7 @@ Every task completion MUST provide the following payload:
|
|
|
812
813
|
const content = await fs5.readFile(path7.join(sourceCommandsDir, file), "utf8");
|
|
813
814
|
await fs5.writeFile(path7.join(codexPromptsDir, targetFileName), content);
|
|
814
815
|
}
|
|
815
|
-
const codexPoliciesDir = path7.join(this.cwd, ".
|
|
816
|
+
const codexPoliciesDir = path7.join(this.cwd, ".codex", "docs", "policies");
|
|
816
817
|
await fs5.mkdir(codexPoliciesDir, { recursive: true });
|
|
817
818
|
const sourcePoliciesDir = path7.join(this.cwd, installRoot, "opencode", "docs", "policies");
|
|
818
819
|
const policyFiles = await fs5.readdir(sourcePoliciesDir).catch(() => []);
|
|
@@ -821,6 +822,70 @@ Every task completion MUST provide the following payload:
|
|
|
821
822
|
const policyContent = await fs5.readFile(path7.join(sourcePoliciesDir, file), "utf8");
|
|
822
823
|
await fs5.writeFile(path7.join(codexPoliciesDir, file), policyContent);
|
|
823
824
|
}
|
|
825
|
+
await this.deployRootCopilotInstructions();
|
|
826
|
+
await this.deployRootCodexMd(force);
|
|
827
|
+
await this.deployRootAgentsMd(force);
|
|
828
|
+
}
|
|
829
|
+
async deployRootCopilotInstructions() {
|
|
830
|
+
const githubDir = path7.join(this.cwd, ".github");
|
|
831
|
+
await fs5.mkdir(githubDir, { recursive: true });
|
|
832
|
+
const targetPath = path7.join(githubDir, "copilot-instructions.md");
|
|
833
|
+
const instructions = `# AI Workflow Kit Governance
|
|
834
|
+
|
|
835
|
+
This repository uses the **AI Workflow Kit** (OpenCode-first) workflow.
|
|
836
|
+
|
|
837
|
+
## Mandate: Atlas Authority Protocol
|
|
838
|
+
All architectural changes must align with the specifications issued by the **Specification Authority**.
|
|
839
|
+
|
|
840
|
+
## Rules & Personas
|
|
841
|
+
- Always follow the **Branch Gate** policy (never work on \`main\`).
|
|
842
|
+
- Always follow the **SDD Workflow** (Spec -> Plan -> PR -> Evidence).
|
|
843
|
+
- When acting as an agent, consult the instructions in \`.github/agents/\`.
|
|
844
|
+
- When utilizing skills, consult the files in \`.codex/skills/\`.
|
|
845
|
+
|
|
846
|
+
## Completion Contract
|
|
847
|
+
Every task completion MUST follow the payload format defined in the individual agent files.
|
|
848
|
+
`;
|
|
849
|
+
await fs5.writeFile(targetPath, instructions);
|
|
850
|
+
}
|
|
851
|
+
async deployRootCodexMd(force) {
|
|
852
|
+
const targetPath = path7.join(this.cwd, "CODEX.md");
|
|
853
|
+
if (!force && await this.exists(targetPath)) return;
|
|
854
|
+
const templatePath = path7.join(this.cwd, ".ai-workflow", "templates", "CODEX.md.template");
|
|
855
|
+
if (await this.exists(templatePath)) {
|
|
856
|
+
const content = await fs5.readFile(templatePath, "utf8");
|
|
857
|
+
await fs5.writeFile(targetPath, content);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
const instructions = `# AI Workflow Kit Governance
|
|
861
|
+
|
|
862
|
+
This repository uses the **AI Workflow Kit** (OpenCode-first) workflow.
|
|
863
|
+
|
|
864
|
+
## Mandate: Atlas Authority Protocol
|
|
865
|
+
All architectural changes must align with the specifications issued by the **Specification Authority**.
|
|
866
|
+
|
|
867
|
+
## Rules & Personas
|
|
868
|
+
- Always follow the **Branch Gate** policy (never work on \`main\`).
|
|
869
|
+
- Always follow the **SDD Workflow** (Spec -> Plan -> PR -> Evidence).
|
|
870
|
+
- When acting as an agent, consult the instructions in \`.github/agents/\`.
|
|
871
|
+
- When utilizing skills, consult the files in \`.codex/skills/\`.
|
|
872
|
+
|
|
873
|
+
## Completion Contract
|
|
874
|
+
Every task completion MUST follow the payload format defined in the individual agent files.
|
|
875
|
+
`;
|
|
876
|
+
await fs5.writeFile(targetPath, instructions);
|
|
877
|
+
}
|
|
878
|
+
async deployRootAgentsMd(force) {
|
|
879
|
+
const targetPath = path7.join(this.cwd, "AGENTS.md");
|
|
880
|
+
if (!force && await this.exists(targetPath)) return;
|
|
881
|
+
const templatePath = path7.join(this.cwd, ".ai-workflow", "AGENTS.md");
|
|
882
|
+
if (await this.exists(templatePath)) {
|
|
883
|
+
let content = await fs5.readFile(templatePath, "utf8");
|
|
884
|
+
content = content.replace(/\.agents\/skills/g, ".codex/skills");
|
|
885
|
+
content = content.replace(/opencode\/commands/g, ".codex/prompts");
|
|
886
|
+
content = content.replace(/opencode\/docs\/policies/g, ".codex/docs/policies");
|
|
887
|
+
await fs5.writeFile(targetPath, content);
|
|
888
|
+
}
|
|
824
889
|
}
|
|
825
890
|
async exists(p) {
|
|
826
891
|
try {
|
|
@@ -1256,7 +1321,7 @@ function resolveProfile(profile) {
|
|
|
1256
1321
|
}
|
|
1257
1322
|
return selectedProfile;
|
|
1258
1323
|
}
|
|
1259
|
-
async function writeInstallFiles(cwd, actions, selectedProfile, force, noOverwrite, backupRoot = ".ai-workflow-backups") {
|
|
1324
|
+
async function writeInstallFiles(cwd, actions, selectedProfile, platforms, force, noOverwrite, backupRoot = ".ai-workflow-backups") {
|
|
1260
1325
|
const skipped = [];
|
|
1261
1326
|
const backups = [];
|
|
1262
1327
|
for (const action of actions) {
|
|
@@ -1272,7 +1337,7 @@ async function writeInstallFiles(cwd, actions, selectedProfile, force, noOverwri
|
|
|
1272
1337
|
});
|
|
1273
1338
|
backups.push(backupPath);
|
|
1274
1339
|
}
|
|
1275
|
-
const content = action.relativePath === ".ai-workflow/config.json" ? `${JSON.stringify(buildAiWorkflowConfig({ profile: selectedProfile }), null, 2)}
|
|
1340
|
+
const content = action.relativePath === ".ai-workflow/config.json" ? `${JSON.stringify(buildAiWorkflowConfig({ profile: selectedProfile, runtimes: platforms.length > 0 ? platforms : ["opencode"] }), null, 2)}
|
|
1276
1341
|
` : action.content;
|
|
1277
1342
|
await writeFileSafe(action.absolutePath, content || "");
|
|
1278
1343
|
}
|
|
@@ -1319,7 +1384,7 @@ async function writeSymlinks(cwd, linkEntries, force, backupRoot = ".ai-workflow
|
|
|
1319
1384
|
}
|
|
1320
1385
|
return { linkBackups, linkCreated };
|
|
1321
1386
|
}
|
|
1322
|
-
async function executePlatformMigrations(cwd, installRoot, platforms, claude, codex, antigravity) {
|
|
1387
|
+
async function executePlatformMigrations(cwd, installRoot, platforms, claude, codex, antigravity, force) {
|
|
1323
1388
|
if (platforms.length === 0) return;
|
|
1324
1389
|
console.log("ai-workflow: migrating to native platforms...");
|
|
1325
1390
|
const agentsDir = path9.join(cwd, installRoot, "opencode/agents");
|
|
@@ -1332,7 +1397,7 @@ async function executePlatformMigrations(cwd, installRoot, platforms, claude, co
|
|
|
1332
1397
|
}
|
|
1333
1398
|
if (codex) {
|
|
1334
1399
|
const adapter = new CodexAdapter({ cwd });
|
|
1335
|
-
await adapter.deploy(installRoot);
|
|
1400
|
+
await adapter.deploy(installRoot, force);
|
|
1336
1401
|
console.log("- codex: deployed AI Workflow Kit agents to .agents/, .codex/, and .github/ (native discovery enabled)");
|
|
1337
1402
|
}
|
|
1338
1403
|
if (antigravity) {
|
|
@@ -1433,7 +1498,7 @@ async function runInit({
|
|
|
1433
1498
|
}
|
|
1434
1499
|
return;
|
|
1435
1500
|
}
|
|
1436
|
-
const { skipped, backups } = await writeInstallFiles(cwd, actions, selectedProfile, force, noOverwrite, backupRoot);
|
|
1501
|
+
const { skipped, backups } = await writeInstallFiles(cwd, actions, selectedProfile, platforms, force, noOverwrite, backupRoot);
|
|
1437
1502
|
const { linkBackups, linkCreated } = await writeSymlinks(cwd, linkEntries, force, backupRoot);
|
|
1438
1503
|
await setupInternalSymlinks(cwd, installRoot);
|
|
1439
1504
|
const opencodeResult = await mergeOpencodeConfig(cwd, { force, backupRoot });
|
|
@@ -1457,7 +1522,7 @@ async function runInit({
|
|
|
1457
1522
|
}
|
|
1458
1523
|
const gitignoreResult = await upsertGitignoreBlock(cwd, generatedIgnoreEntries);
|
|
1459
1524
|
const scriptInjected = await injectScripts(cwd);
|
|
1460
|
-
await executePlatformMigrations(cwd, installRoot, platforms, claude, codex, antigravity);
|
|
1525
|
+
await executePlatformMigrations(cwd, installRoot, platforms, claude, codex, antigravity, force);
|
|
1461
1526
|
printInstallationSummary({
|
|
1462
1527
|
selectedProfile,
|
|
1463
1528
|
opencodeResult,
|
|
@@ -1486,11 +1551,7 @@ async function isSymlink(filePath) {
|
|
|
1486
1551
|
return false;
|
|
1487
1552
|
}
|
|
1488
1553
|
}
|
|
1489
|
-
async function
|
|
1490
|
-
let hasFailure = false;
|
|
1491
|
-
let hasWarning = false;
|
|
1492
|
-
let managedBlocks = [];
|
|
1493
|
-
console.log("Doctor report:");
|
|
1554
|
+
async function checkOpenCodeRuntime(cwd, state) {
|
|
1494
1555
|
const adapter = new OpenCodeAdapter({ cwd });
|
|
1495
1556
|
const inspection = await adapter.inspect();
|
|
1496
1557
|
if (inspection.available) {
|
|
@@ -1500,22 +1561,26 @@ async function runDoctor({ cwd }) {
|
|
|
1500
1561
|
if (inspection.supports.agent) {
|
|
1501
1562
|
console.log("PASS opencode --agent selection is supported");
|
|
1502
1563
|
} else {
|
|
1503
|
-
hasWarning = true;
|
|
1564
|
+
state.hasWarning = true;
|
|
1504
1565
|
console.log("WARN opencode --agent selection is not supported; prompt fallback is available");
|
|
1505
1566
|
}
|
|
1506
1567
|
} else {
|
|
1507
|
-
hasFailure = true;
|
|
1568
|
+
state.hasFailure = true;
|
|
1508
1569
|
console.log("FAIL opencode CLI is not installed or not available in PATH");
|
|
1509
1570
|
}
|
|
1571
|
+
}
|
|
1572
|
+
async function checkRequiredFiles(cwd, state) {
|
|
1510
1573
|
for (const relativePath of REQUIRED_FILES) {
|
|
1511
1574
|
const ok = await exists(path10.join(cwd, relativePath));
|
|
1512
1575
|
if (ok) {
|
|
1513
1576
|
console.log(`PASS ${relativePath}`);
|
|
1514
1577
|
} else {
|
|
1515
|
-
hasFailure = true;
|
|
1578
|
+
state.hasFailure = true;
|
|
1516
1579
|
console.log(`FAIL ${relativePath} missing`);
|
|
1517
1580
|
}
|
|
1518
1581
|
}
|
|
1582
|
+
}
|
|
1583
|
+
async function checkAiWorkflowConfig(cwd, state) {
|
|
1519
1584
|
const configPathV2 = path10.join(cwd, ".ai-workflow/config.json");
|
|
1520
1585
|
const configPathLegacy = path10.join(cwd, ".ai-workflow.json");
|
|
1521
1586
|
let configPath = null;
|
|
@@ -1524,122 +1589,172 @@ async function runDoctor({ cwd }) {
|
|
|
1524
1589
|
} else if (await exists(configPathLegacy)) {
|
|
1525
1590
|
configPath = configPathLegacy;
|
|
1526
1591
|
}
|
|
1527
|
-
if (configPath)
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1592
|
+
if (!configPath) return;
|
|
1593
|
+
try {
|
|
1594
|
+
const config = await readJson(configPath);
|
|
1595
|
+
const basename = path10.basename(configPath);
|
|
1596
|
+
if (config.installMode === "project-local" || config.mode === "standalone") {
|
|
1597
|
+
console.log("PASS install mode is project-local/standalone");
|
|
1598
|
+
} else {
|
|
1599
|
+
state.hasWarning = true;
|
|
1600
|
+
console.log(`WARN install mode is not project-local/standalone in ${basename}`);
|
|
1601
|
+
}
|
|
1602
|
+
if (config.profile) {
|
|
1603
|
+
console.log(`PASS profile detected: ${config.profile}`);
|
|
1604
|
+
} else {
|
|
1605
|
+
state.hasWarning = true;
|
|
1606
|
+
console.log(`WARN profile is missing in ${basename}`);
|
|
1607
|
+
}
|
|
1608
|
+
if (config.runtime) {
|
|
1609
|
+
console.log(`PASS runtime: ${config.runtime}`);
|
|
1610
|
+
state.runtimes = config.runtime.split("+");
|
|
1611
|
+
} else {
|
|
1612
|
+
console.log(`NOTE runtime field not present in ${basename}`);
|
|
1613
|
+
state.runtimes = ["opencode"];
|
|
1614
|
+
}
|
|
1615
|
+
if (Array.isArray(config.managedBlocks)) {
|
|
1616
|
+
state.managedBlocks = config.managedBlocks;
|
|
1617
|
+
} else {
|
|
1618
|
+
console.log(`NOTE managedBlocks not present in ${basename} (legacy field)`);
|
|
1619
|
+
}
|
|
1620
|
+
if (Array.isArray(config.managedFiles)) {
|
|
1621
|
+
for (const relativePath of config.managedFiles) {
|
|
1622
|
+
const fileExists = await exists(path10.join(cwd, relativePath));
|
|
1623
|
+
if (!fileExists) {
|
|
1624
|
+
state.hasWarning = true;
|
|
1625
|
+
console.log(`WARN managed file missing: ${relativePath}`);
|
|
1559
1626
|
}
|
|
1560
1627
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
console.log(`WARN managed link is not a symlink: ${relativePath}`);
|
|
1574
|
-
}
|
|
1628
|
+
}
|
|
1629
|
+
if (Array.isArray(config.managedLinks)) {
|
|
1630
|
+
for (const relativePath of config.managedLinks) {
|
|
1631
|
+
const absolutePath = path10.join(cwd, relativePath);
|
|
1632
|
+
if (!await exists(absolutePath)) {
|
|
1633
|
+
state.hasWarning = true;
|
|
1634
|
+
console.log(`WARN managed link missing: ${relativePath}`);
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
if (!await isSymlink(absolutePath)) {
|
|
1638
|
+
state.hasWarning = true;
|
|
1639
|
+
console.log(`WARN managed link is not a symlink: ${relativePath}`);
|
|
1575
1640
|
}
|
|
1576
1641
|
}
|
|
1577
|
-
} catch {
|
|
1578
|
-
hasFailure = true;
|
|
1579
|
-
console.log(`FAIL ${path10.basename(configPath)} is not valid JSON`);
|
|
1580
1642
|
}
|
|
1643
|
+
} catch {
|
|
1644
|
+
state.hasFailure = true;
|
|
1645
|
+
console.log(`FAIL ${path10.basename(configPath)} is not valid JSON`);
|
|
1581
1646
|
}
|
|
1647
|
+
}
|
|
1648
|
+
async function checkOpencodeJson(cwd, state) {
|
|
1582
1649
|
const opencodePath = path10.join(cwd, "opencode.jsonc");
|
|
1583
1650
|
if (!await exists(opencodePath)) {
|
|
1584
|
-
hasFailure = true;
|
|
1651
|
+
state.hasFailure = true;
|
|
1585
1652
|
console.log("FAIL opencode.jsonc missing");
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
try {
|
|
1656
|
+
const opencodeConfig = await readJsonc(opencodePath);
|
|
1657
|
+
console.log("PASS opencode.jsonc is valid JSONC");
|
|
1658
|
+
const needsAgentDefault = state.managedBlocks.includes("opencode.jsonc:agent.default");
|
|
1659
|
+
if (!needsAgentDefault || opencodeConfig.agent?.default) {
|
|
1660
|
+
console.log("PASS opencode agent.default available");
|
|
1661
|
+
} else {
|
|
1662
|
+
state.hasFailure = true;
|
|
1663
|
+
console.log("FAIL opencode agent.default missing");
|
|
1664
|
+
}
|
|
1665
|
+
const requiredCommands = state.managedBlocks.filter((block) => block.startsWith("opencode.jsonc:command.")).map((block) => block.replace("opencode.jsonc:command.", ""));
|
|
1666
|
+
if (requiredCommands.length === 0) {
|
|
1667
|
+
console.log("PASS no managed opencode commands required");
|
|
1668
|
+
} else {
|
|
1669
|
+
const missingCommands = requiredCommands.filter((command) => !opencodeConfig.command?.[command]);
|
|
1670
|
+
if (missingCommands.length === 0) {
|
|
1671
|
+
console.log(`PASS opencode managed commands available (${requiredCommands.length})`);
|
|
1600
1672
|
} else {
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
console.log(`PASS opencode managed commands available (${requiredCommands.length})`);
|
|
1604
|
-
} else {
|
|
1605
|
-
hasFailure = true;
|
|
1606
|
-
console.log(`FAIL opencode command entries missing: ${missingCommands.join(", ")}`);
|
|
1607
|
-
}
|
|
1673
|
+
state.hasFailure = true;
|
|
1674
|
+
console.log(`FAIL opencode command entries missing: ${missingCommands.join(", ")}`);
|
|
1608
1675
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1676
|
+
}
|
|
1677
|
+
const requiredAgents = state.managedBlocks.filter((block) => block.startsWith("opencode.jsonc:agent.") && !block.endsWith(".default")).map((block) => block.replace("opencode.jsonc:agent.", ""));
|
|
1678
|
+
if (requiredAgents.length === 0) {
|
|
1679
|
+
console.log("PASS no managed opencode agents required");
|
|
1680
|
+
} else {
|
|
1681
|
+
const missingAgents = requiredAgents.filter((agent) => !opencodeConfig.agent?.[agent]);
|
|
1682
|
+
if (missingAgents.length === 0) {
|
|
1683
|
+
console.log(`PASS opencode managed agents available (${requiredAgents.length})`);
|
|
1612
1684
|
} else {
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
console.log(`PASS opencode managed agents available (${requiredAgents.length})`);
|
|
1616
|
-
} else {
|
|
1617
|
-
hasFailure = true;
|
|
1618
|
-
console.log(`FAIL opencode agent entries missing: ${missingAgents.join(", ")}`);
|
|
1619
|
-
}
|
|
1685
|
+
state.hasFailure = true;
|
|
1686
|
+
console.log(`FAIL opencode agent entries missing: ${missingAgents.join(", ")}`);
|
|
1620
1687
|
}
|
|
1621
|
-
} catch {
|
|
1622
|
-
hasFailure = true;
|
|
1623
|
-
console.log("FAIL opencode.jsonc is not valid JSONC");
|
|
1624
1688
|
}
|
|
1689
|
+
} catch {
|
|
1690
|
+
state.hasFailure = true;
|
|
1691
|
+
console.log("FAIL opencode.jsonc is not valid JSONC");
|
|
1625
1692
|
}
|
|
1693
|
+
}
|
|
1694
|
+
async function checkPackageJson(cwd, state) {
|
|
1626
1695
|
const packageJsonPath = path10.join(cwd, "package.json");
|
|
1627
1696
|
if (await exists(packageJsonPath)) {
|
|
1628
1697
|
try {
|
|
1629
1698
|
const packageJson = await readJson(packageJsonPath);
|
|
1630
1699
|
const scripts = packageJson.scripts ?? {};
|
|
1631
1700
|
if (!scripts.validate) {
|
|
1632
|
-
hasWarning = true;
|
|
1701
|
+
state.hasWarning = true;
|
|
1633
1702
|
console.log("WARN package.json has no validate script");
|
|
1634
1703
|
}
|
|
1635
1704
|
} catch {
|
|
1636
|
-
hasFailure = true;
|
|
1705
|
+
state.hasFailure = true;
|
|
1637
1706
|
console.log("FAIL package.json is not valid JSON");
|
|
1638
1707
|
}
|
|
1639
1708
|
}
|
|
1640
|
-
|
|
1709
|
+
}
|
|
1710
|
+
async function checkCodexRuntime(cwd, state) {
|
|
1711
|
+
const checks = [
|
|
1712
|
+
{ name: "root AGENTS.md exists", path: "AGENTS.md" },
|
|
1713
|
+
{ name: ".codex/skills exists", path: ".codex/skills" },
|
|
1714
|
+
{ name: ".codex prompts detected", path: ".codex/prompts" },
|
|
1715
|
+
{ name: ".codex policies detected", path: ".codex/docs/policies" }
|
|
1716
|
+
];
|
|
1717
|
+
for (const check of checks) {
|
|
1718
|
+
const ok = await exists(path10.join(cwd, check.path));
|
|
1719
|
+
if (ok) {
|
|
1720
|
+
console.log(`PASS ${check.name}`);
|
|
1721
|
+
} else {
|
|
1722
|
+
state.hasFailure = true;
|
|
1723
|
+
console.log(`FAIL ${check.name}`);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
const codexMdOk = await exists(path10.join(cwd, "CODEX.md"));
|
|
1727
|
+
if (codexMdOk) {
|
|
1728
|
+
console.log(`PASS root CODEX.md exists`);
|
|
1729
|
+
}
|
|
1730
|
+
if (state.runtimes.includes("codex")) {
|
|
1731
|
+
console.log("PASS config runtime includes codex");
|
|
1732
|
+
} else {
|
|
1733
|
+
state.hasFailure = true;
|
|
1734
|
+
console.log("FAIL config runtime includes codex");
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
async function runDoctor({ cwd }) {
|
|
1738
|
+
const state = {
|
|
1739
|
+
hasFailure: false,
|
|
1740
|
+
hasWarning: false,
|
|
1741
|
+
managedBlocks: [],
|
|
1742
|
+
runtimes: ["opencode"]
|
|
1743
|
+
};
|
|
1744
|
+
console.log("Doctor report:");
|
|
1745
|
+
await checkRequiredFiles(cwd, state);
|
|
1746
|
+
await checkAiWorkflowConfig(cwd, state);
|
|
1747
|
+
if (state.runtimes.includes("opencode")) {
|
|
1748
|
+
await checkOpenCodeRuntime(cwd, state);
|
|
1749
|
+
await checkOpencodeJson(cwd, state);
|
|
1750
|
+
}
|
|
1751
|
+
if (state.runtimes.includes("codex")) {
|
|
1752
|
+
await checkCodexRuntime(cwd, state);
|
|
1753
|
+
}
|
|
1754
|
+
await checkPackageJson(cwd, state);
|
|
1755
|
+
const finalStatus = state.hasFailure ? "FAIL" : state.hasWarning ? "PASS_WITH_NOTES" : "PASS";
|
|
1641
1756
|
console.log(`Final status: ${finalStatus}`);
|
|
1642
|
-
if (hasFailure) {
|
|
1757
|
+
if (state.hasFailure) {
|
|
1643
1758
|
process.exitCode = 1;
|
|
1644
1759
|
}
|
|
1645
1760
|
}
|
|
@@ -2288,6 +2403,71 @@ async function generateFinalHandoff(plan, taskSlug, cwd, finalState, evidence) {
|
|
|
2288
2403
|
`);
|
|
2289
2404
|
return handoffPath;
|
|
2290
2405
|
}
|
|
2406
|
+
async function runFastTrackValidation(cwd, result, checkBranchSafety) {
|
|
2407
|
+
console.log(`
|
|
2408
|
+
--- [FAST-TRACK] Running Lightweight Validation Gates ---`);
|
|
2409
|
+
checkBranchSafety();
|
|
2410
|
+
const changedFiles = result.evidence?.changedFiles || [];
|
|
2411
|
+
console.log(`[FAST-TRACK] Changed files detected:`, changedFiles);
|
|
2412
|
+
const evidencePath = path14.join(cwd, "EVIDENCE.json");
|
|
2413
|
+
const hasEvidence = await fs12.access(evidencePath).then(() => true).catch(() => false);
|
|
2414
|
+
if (hasEvidence) {
|
|
2415
|
+
const { EvidenceValidator } = await import("../evidence-validator-76ZQQYDU.js");
|
|
2416
|
+
const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
2417
|
+
const evidenceValidator = new EvidenceValidator({ cwd, skipFileCheck: isTest, skipGitCheck: isTest });
|
|
2418
|
+
const valResult = await evidenceValidator.validate();
|
|
2419
|
+
if (!valResult.valid) {
|
|
2420
|
+
console.error(`
|
|
2421
|
+
[FAST-TRACK BLOCKED] EVIDENCE.json validation failed: ${valResult.reason}`);
|
|
2422
|
+
return "FAIL_QUALITY_GATE";
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
return result.overallStatus;
|
|
2426
|
+
}
|
|
2427
|
+
async function runFidelityValidation(naturalRequest, result, finalizer) {
|
|
2428
|
+
const fidelityResult = await finalizer.verifyFidelity({
|
|
2429
|
+
userRequest: naturalRequest,
|
|
2430
|
+
projectContext: result.evidence?.deliveryDecision?.projectContext || null,
|
|
2431
|
+
deliveryDecision: result.evidence?.deliveryDecision || null,
|
|
2432
|
+
changedFiles: result.evidence?.changedFiles || [],
|
|
2433
|
+
evidence: result.evidence || null,
|
|
2434
|
+
explicitApprovals: process.env.AI_WORKFLOW_APPROVALS ? process.env.AI_WORKFLOW_APPROVALS.split(",").map((a) => a.trim()) : []
|
|
2435
|
+
});
|
|
2436
|
+
if (!fidelityResult.passed) {
|
|
2437
|
+
console.log(`
|
|
2438
|
+
[FINALIZER BLOCKED] BLOCKED: Artifact fidelity check failed!`);
|
|
2439
|
+
console.log(`Reason: ${fidelityResult.reason}`);
|
|
2440
|
+
return "FAIL_QUALITY_GATE";
|
|
2441
|
+
}
|
|
2442
|
+
return result.overallStatus;
|
|
2443
|
+
}
|
|
2444
|
+
function computeFinalState(finalStatus) {
|
|
2445
|
+
if (finalStatus === "PASS") return "COMPLETED";
|
|
2446
|
+
if (finalStatus === "PASS_WITH_NOTES") return "COMPLETED_WITH_NOTES";
|
|
2447
|
+
return "BLOCKED";
|
|
2448
|
+
}
|
|
2449
|
+
async function executeReadOnlyWorkflow(cwd, stateMachine, readOnlyStateBefore, fastTrack) {
|
|
2450
|
+
await runReadOnlyConfinementCheck(readOnlyStateBefore, cwd, stateMachine, fastTrack);
|
|
2451
|
+
stateMachine.transitionTo("IMPLEMENTED");
|
|
2452
|
+
stateMachine.transitionTo("VALIDATING");
|
|
2453
|
+
stateMachine.transitionTo("COMPLETED");
|
|
2454
|
+
console.log("\n--- Final Handoff ---");
|
|
2455
|
+
console.log(`
|
|
2456
|
+
[AI WORKFLOW COMPLETE] COMPLETED`);
|
|
2457
|
+
console.log(`Handoff Packet: [IN-MEMORY] (No handoff file written in read-only mode)
|
|
2458
|
+
`);
|
|
2459
|
+
return {
|
|
2460
|
+
overallStatus: "PASS",
|
|
2461
|
+
evidence: {
|
|
2462
|
+
internalStatus: "PASS",
|
|
2463
|
+
commandsRun: []
|
|
2464
|
+
},
|
|
2465
|
+
quality: {
|
|
2466
|
+
overallStatus: "PASS"
|
|
2467
|
+
},
|
|
2468
|
+
stateHistory: stateMachine.getHistory()
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2291
2471
|
async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
2292
2472
|
if (!naturalRequest || !naturalRequest.trim()) {
|
|
2293
2473
|
throw new Error("Missing request. Please provide a natural request string via positional arguments or --request flag.");
|
|
@@ -2303,16 +2483,15 @@ async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
|
2303
2483
|
const stateMachine = new WorkflowStateMachine();
|
|
2304
2484
|
const { plan: planObj, classification } = await classifyAndPlanExecution(naturalRequest, cwd, taskSlug, delegationController, stateMachine);
|
|
2305
2485
|
plan = planObj;
|
|
2306
|
-
const fastTrack = plan.riskLevel === "low" && (plan.branchNeeded
|
|
2486
|
+
const fastTrack = plan.riskLevel === "low" && (!plan.branchNeeded || plan.evidencePolicy === "optional");
|
|
2307
2487
|
const branchGate = new BranchGate({ memoryDir: path14.join(cwd, ".ai-workflow"), cwd });
|
|
2308
2488
|
const gateResult = runBranchGate(plan, taskSlug, cwd, stateMachine, branchGate);
|
|
2309
2489
|
const checkBranchSafety = () => {
|
|
2310
|
-
if (
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
}
|
|
2490
|
+
if (!planObj.branchNeeded) return;
|
|
2491
|
+
const currentBranch = branchGate.getCurrentBranch();
|
|
2492
|
+
if (branchGate.protectedBranches.includes(currentBranch)) {
|
|
2493
|
+
stateMachine.transitionTo("BLOCKED");
|
|
2494
|
+
throw new Error(`[WORKFLOW BLOCKED] Security violation: current branch is protected '${currentBranch}'!`);
|
|
2316
2495
|
}
|
|
2317
2496
|
};
|
|
2318
2497
|
await createSpecIfRequired(plan, classification, taskSlug, cwd);
|
|
@@ -2321,27 +2500,8 @@ async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
|
2321
2500
|
readOnlyStateBefore = await captureReadOnlyState(cwd, fastTrack);
|
|
2322
2501
|
}
|
|
2323
2502
|
await runImplementation(plan, classification, delegationController, stateMachine, fastTrack);
|
|
2324
|
-
if (!plan.branchNeeded) {
|
|
2325
|
-
await
|
|
2326
|
-
stateMachine.transitionTo("IMPLEMENTED");
|
|
2327
|
-
stateMachine.transitionTo("VALIDATING");
|
|
2328
|
-
stateMachine.transitionTo("COMPLETED");
|
|
2329
|
-
console.log("\n--- Final Handoff ---");
|
|
2330
|
-
console.log(`
|
|
2331
|
-
[AI WORKFLOW COMPLETE] COMPLETED`);
|
|
2332
|
-
console.log(`Handoff Packet: [IN-MEMORY] (No handoff file written in read-only mode)
|
|
2333
|
-
`);
|
|
2334
|
-
return {
|
|
2335
|
-
overallStatus: "PASS",
|
|
2336
|
-
evidence: {
|
|
2337
|
-
internalStatus: "PASS",
|
|
2338
|
-
commandsRun: []
|
|
2339
|
-
},
|
|
2340
|
-
quality: {
|
|
2341
|
-
overallStatus: "PASS"
|
|
2342
|
-
},
|
|
2343
|
-
stateHistory: stateMachine.getHistory()
|
|
2344
|
-
};
|
|
2503
|
+
if (!plan.branchNeeded && readOnlyStateBefore) {
|
|
2504
|
+
return await executeReadOnlyWorkflow(cwd, stateMachine, readOnlyStateBefore, fastTrack);
|
|
2345
2505
|
}
|
|
2346
2506
|
stateMachine.transitionTo("IMPLEMENTED");
|
|
2347
2507
|
checkBranchSafety();
|
|
@@ -2369,7 +2529,6 @@ async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
|
2369
2529
|
}
|
|
2370
2530
|
checkBranchSafety();
|
|
2371
2531
|
let integrity = { valid: true, changes: { added: [], modified: [], deleted: [] } };
|
|
2372
|
-
let stabResult = result;
|
|
2373
2532
|
if (!fastTrack) {
|
|
2374
2533
|
const stab = await runFinalizerStabilization(
|
|
2375
2534
|
plan,
|
|
@@ -2382,54 +2541,23 @@ async function runExecute({ cwd, naturalRequest, taskSlug: taskSlugOverride }) {
|
|
|
2382
2541
|
snapshotRef,
|
|
2383
2542
|
checkBranchSafety
|
|
2384
2543
|
);
|
|
2385
|
-
|
|
2544
|
+
result = stab.result;
|
|
2386
2545
|
integrity = stab.integrity;
|
|
2387
2546
|
}
|
|
2388
|
-
result = stabResult;
|
|
2389
2547
|
let finalStatus = result.overallStatus;
|
|
2390
2548
|
if (fastTrack) {
|
|
2391
|
-
|
|
2392
|
-
--- [FAST-TRACK] Running Lightweight Validation Gates ---`);
|
|
2393
|
-
checkBranchSafety();
|
|
2394
|
-
const changedFiles = result.evidence?.changedFiles || [];
|
|
2395
|
-
console.log(`[FAST-TRACK] Changed files detected:`, changedFiles);
|
|
2396
|
-
const evidencePath = path14.join(cwd, "EVIDENCE.json");
|
|
2397
|
-
const hasEvidence = await fs12.access(evidencePath).then(() => true).catch(() => false);
|
|
2398
|
-
if (hasEvidence) {
|
|
2399
|
-
const { EvidenceValidator } = await import("../evidence-validator-76ZQQYDU.js");
|
|
2400
|
-
const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
2401
|
-
const evidenceValidator = new EvidenceValidator({ cwd, skipFileCheck: isTest, skipGitCheck: isTest });
|
|
2402
|
-
const valResult2 = await evidenceValidator.validate();
|
|
2403
|
-
if (!valResult2.valid) {
|
|
2404
|
-
console.error(`
|
|
2405
|
-
[FAST-TRACK BLOCKED] EVIDENCE.json validation failed: ${valResult2.reason}`);
|
|
2406
|
-
finalStatus = "FAIL_QUALITY_GATE";
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2549
|
+
finalStatus = await runFastTrackValidation(cwd, result, checkBranchSafety);
|
|
2409
2550
|
} else if (!integrity.valid) {
|
|
2410
2551
|
console.log(`
|
|
2411
2552
|
[FINALIZER BLOCKED] BLOCKED: workspace did not stabilize`);
|
|
2412
2553
|
console.log(`Final changes detected:`, integrity.changes);
|
|
2413
2554
|
finalStatus = "FAIL_QUALITY_GATE";
|
|
2414
2555
|
} else {
|
|
2415
|
-
|
|
2416
|
-
userRequest: naturalRequest,
|
|
2417
|
-
projectContext: result.evidence?.deliveryDecision?.projectContext || null,
|
|
2418
|
-
deliveryDecision: result.evidence?.deliveryDecision || null,
|
|
2419
|
-
changedFiles: result.evidence?.changedFiles || [],
|
|
2420
|
-
evidence: result.evidence || null,
|
|
2421
|
-
explicitApprovals: process.env.AI_WORKFLOW_APPROVALS ? process.env.AI_WORKFLOW_APPROVALS.split(",").map((a) => a.trim()) : []
|
|
2422
|
-
});
|
|
2423
|
-
if (!fidelityResult.passed) {
|
|
2424
|
-
console.log(`
|
|
2425
|
-
[FINALIZER BLOCKED] BLOCKED: Artifact fidelity check failed!`);
|
|
2426
|
-
console.log(`Reason: ${fidelityResult.reason}`);
|
|
2427
|
-
finalStatus = "FAIL_QUALITY_GATE";
|
|
2428
|
-
}
|
|
2556
|
+
finalStatus = await runFidelityValidation(naturalRequest, result, finalizer);
|
|
2429
2557
|
}
|
|
2430
|
-
const finalState = finalStatus
|
|
2558
|
+
const finalState = computeFinalState(finalStatus);
|
|
2431
2559
|
stateMachine.transitionTo(finalState);
|
|
2432
|
-
|
|
2560
|
+
await generateFinalHandoff(plan, taskSlug, cwd, finalState, result.evidence);
|
|
2433
2561
|
return { ...result, overallStatus: finalStatus, stateHistory: stateMachine.getHistory() };
|
|
2434
2562
|
} finally {
|
|
2435
2563
|
if (plan && !plan.branchNeeded) {
|
|
@@ -2589,19 +2717,9 @@ function parseFlags(args) {
|
|
|
2589
2717
|
purgeAgents: args.includes("--purge-agents") || args.includes("--purge")
|
|
2590
2718
|
};
|
|
2591
2719
|
}
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
console.log(getPackageVersion() || "unknown");
|
|
2596
|
-
return;
|
|
2597
|
-
}
|
|
2598
|
-
if (!command || command === "--help" || command === "-h") {
|
|
2599
|
-
printHelp();
|
|
2600
|
-
return;
|
|
2601
|
-
}
|
|
2602
|
-
if (command === "execute") {
|
|
2603
|
-
const flags = parseFlags(args.slice(1));
|
|
2604
|
-
const positionals = args.slice(1).filter((arg) => !arg.startsWith("-"));
|
|
2720
|
+
var commandMap = {
|
|
2721
|
+
execute: async (args, flags) => {
|
|
2722
|
+
const positionals = args.filter((arg) => !arg.startsWith("-"));
|
|
2605
2723
|
const request = flags.request || positionals.join(" ");
|
|
2606
2724
|
const result = await runExecute({
|
|
2607
2725
|
cwd: process.cwd(),
|
|
@@ -2611,25 +2729,23 @@ async function runCli(args) {
|
|
|
2611
2729
|
if (result && (result.overallStatus === "FAIL_QUALITY_GATE" || result.overallStatus === "BLOCKED" || result.overallStatus === "FAIL")) {
|
|
2612
2730
|
process.exit(1);
|
|
2613
2731
|
}
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
if (command === "run") {
|
|
2732
|
+
},
|
|
2733
|
+
run: async (_, flags) => {
|
|
2617
2734
|
await runMasterOrchestrator({
|
|
2618
2735
|
cwd: process.cwd(),
|
|
2619
|
-
...
|
|
2736
|
+
...flags
|
|
2620
2737
|
});
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
if (command === "init") {
|
|
2738
|
+
},
|
|
2739
|
+
init: async (_, flags) => {
|
|
2624
2740
|
await runInit({
|
|
2625
2741
|
cwd: process.cwd(),
|
|
2626
|
-
...
|
|
2742
|
+
...flags
|
|
2627
2743
|
});
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2744
|
+
},
|
|
2745
|
+
"collect-evidence": async (_, flags) => {
|
|
2746
|
+
if (flags.mode && !["quick", "standard", "full"].includes(flags.mode)) {
|
|
2747
|
+
throw new Error("--mode must be quick, standard, or full");
|
|
2748
|
+
}
|
|
2633
2749
|
await runCollectEvidence({
|
|
2634
2750
|
cwd: process.cwd(),
|
|
2635
2751
|
taskSlug: flags.taskSlug,
|
|
@@ -2639,11 +2755,9 @@ async function runCli(args) {
|
|
|
2639
2755
|
visualDist: flags.visualDist,
|
|
2640
2756
|
port: flags.port
|
|
2641
2757
|
});
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
const flags = parseFlags(args.slice(1));
|
|
2646
|
-
const { runValidate } = await import("../validate-Q2NLLQ5G.js");
|
|
2758
|
+
},
|
|
2759
|
+
validate: async (_, flags) => {
|
|
2760
|
+
const { runValidate } = await import("../validate-A46WUBVZ.js");
|
|
2647
2761
|
await runValidate({
|
|
2648
2762
|
cwd: process.cwd(),
|
|
2649
2763
|
taskSlug: flags.taskSlug,
|
|
@@ -2651,17 +2765,32 @@ async function runCli(args) {
|
|
|
2651
2765
|
visualDist: flags.visualDist,
|
|
2652
2766
|
port: flags.port
|
|
2653
2767
|
});
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
if (command === "doctor") {
|
|
2768
|
+
},
|
|
2769
|
+
doctor: async () => {
|
|
2657
2770
|
await runDoctor({ cwd: process.cwd() });
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
if (command === "clean") {
|
|
2771
|
+
},
|
|
2772
|
+
clean: async (_, flags) => {
|
|
2661
2773
|
await runClean({
|
|
2662
2774
|
cwd: process.cwd(),
|
|
2663
|
-
...
|
|
2775
|
+
...flags
|
|
2664
2776
|
});
|
|
2777
|
+
}
|
|
2778
|
+
};
|
|
2779
|
+
async function runCli(args) {
|
|
2780
|
+
const [command] = args;
|
|
2781
|
+
if (command === "--version" || command === "-v") {
|
|
2782
|
+
console.log(getPackageVersion() || "unknown");
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
if (!command || command === "--help" || command === "-h") {
|
|
2786
|
+
printHelp();
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
const handler = commandMap[command];
|
|
2790
|
+
if (handler) {
|
|
2791
|
+
const argsSlice = args.slice(1);
|
|
2792
|
+
const flags = parseFlags(argsSlice);
|
|
2793
|
+
await handler(argsSlice, flags);
|
|
2665
2794
|
return;
|
|
2666
2795
|
}
|
|
2667
2796
|
throw new Error(`unknown command: ${command}`);
|