@chenmk/superflow 0.3.0 → 0.5.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/INSTALL.en.md +54 -12
- package/INSTALL.md +59 -33
- package/README.md +192 -49
- package/README.zh-CN.md +166 -46
- package/assets/brand/managed-review-loop.png +0 -0
- package/assets/manifest.json +15 -9
- package/assets/prompts/superflow-design.md +3 -0
- package/assets/prompts/superflow-docs.md +4 -1
- package/assets/scripts/superflow-coding-ready.mjs +135 -0
- package/assets/scripts/superflow-dependency-update-hook.sh +81 -8
- package/assets/scripts/superflow-document-audit.mjs +240 -0
- package/assets/scripts/superflow-environment-preflight.mjs +398 -0
- package/assets/scripts/superflow-hook-guard.sh +40 -1
- package/assets/scripts/superflow-managed-command-guard.py +309 -0
- package/assets/scripts/superflow-managed-executor-preflight.mjs +421 -0
- package/assets/scripts/superflow-managed-owner-verification.sh +117 -0
- package/assets/scripts/superflow-managed-work-check.mjs +455 -0
- package/assets/scripts/superflow-managed-work-guard.sh +17 -0
- package/assets/skills/openspec-propose/SKILL.md +11 -0
- package/assets/skills/superflow-clarify/SKILL.md +14 -0
- package/assets/skills/superflow-design/SKILL.md +46 -12
- package/assets/skills/superflow-docs/SKILL.md +109 -2
- package/assets/skills/superflow-pipeline/SKILL.md +205 -2
- package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +17 -9
- package/assets/skills/superflow-pipeline/references/document-readiness.md +157 -0
- package/assets/skills/superflow-pipeline/references/document-templates.md +17 -5
- package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +14 -0
- package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +20 -11
- package/assets/skills/superflow-pipeline/references/managed-work.md +166 -0
- package/assets/skills/superflow-pipeline/references/orchestration.md +2 -2
- package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +3 -2
- package/assets/skills/superflow-pipeline/references/quality-gate.md +8 -5
- package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +7 -0
- package/assets/skills/superflow-pipeline/references/superpower-technical-design-template.md +10 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-archive.sh +1 -1
- package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +282 -16
- package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
- package/assets/skills/superflow-requirement-review/SKILL.md +178 -0
- package/assets/skills/superflow-requirement-review/agents/openai.yaml +4 -0
- package/assets/skills-en/openspec-propose/SKILL.md +14 -0
- package/assets/skills-en/superflow-clarify/SKILL.md +17 -0
- package/assets/skills-en/superflow-design/SKILL.md +40 -11
- package/assets/skills-en/superflow-docs/SKILL.md +115 -0
- package/assets/skills-en/superflow-pipeline/SKILL.md +202 -0
- package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +4 -0
- package/assets/skills-en/superflow-pipeline/references/document-readiness.md +125 -0
- package/assets/skills-en/superflow-pipeline/references/document-templates.md +15 -0
- package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +7 -0
- package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +7 -0
- package/assets/skills-en/superflow-pipeline/references/managed-work.md +184 -0
- package/assets/skills-en/superflow-pipeline/references/quality-gate.md +20 -0
- package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +7 -0
- package/assets/skills-en/superflow-pipeline/references/superpower-technical-design-template.md +11 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-archive.sh +1 -1
- package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +279 -14
- package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
- package/assets/skills-en/superflow-requirement-review/SKILL.md +99 -0
- package/assets/skills-en/superflow-requirement-review/agents/openai.yaml +4 -0
- package/dist/app/cli.js +176 -103
- package/dist/app/cli.js.map +1 -1
- package/dist/app/commands/change-guard.js +19 -2
- package/dist/app/commands/change-guard.js.map +1 -1
- package/dist/app/commands/check.js +107 -45
- package/dist/app/commands/check.js.map +1 -1
- package/dist/app/commands/docs.js +7 -3
- package/dist/app/commands/docs.js.map +1 -1
- package/dist/app/commands/doctor.js +174 -198
- package/dist/app/commands/doctor.js.map +1 -1
- package/dist/app/commands/eval.js +64 -0
- package/dist/app/commands/eval.js.map +1 -0
- package/dist/app/commands/implement.js +7 -3
- package/dist/app/commands/implement.js.map +1 -1
- package/dist/app/commands/init.js +16 -24
- package/dist/app/commands/init.js.map +1 -1
- package/dist/app/commands/managed-service.js +5 -0
- package/dist/app/commands/managed-service.js.map +1 -0
- package/dist/app/commands/mcp.js +167 -0
- package/dist/app/commands/mcp.js.map +1 -0
- package/dist/app/commands/pipeline.js +757 -3
- package/dist/app/commands/pipeline.js.map +1 -1
- package/dist/app/commands/requirement-review.js +6 -0
- package/dist/app/commands/requirement-review.js.map +1 -0
- package/dist/app/commands/status.js +262 -77
- package/dist/app/commands/status.js.map +1 -1
- package/dist/app/commands/uninstall.js +8 -5
- package/dist/app/commands/uninstall.js.map +1 -1
- package/dist/app/commands/update.js +114 -15
- package/dist/app/commands/update.js.map +1 -1
- package/dist/domains/agent.js +3 -11
- package/dist/domains/agent.js.map +1 -1
- package/dist/domains/config/cli-help.js +145 -73
- package/dist/domains/config/cli-help.js.map +1 -1
- package/dist/domains/config/i18n.js +6 -8
- package/dist/domains/config/i18n.js.map +1 -1
- package/dist/domains/config/manifest.js +0 -3
- package/dist/domains/config/manifest.js.map +1 -1
- package/dist/domains/deps.js +70 -8
- package/dist/domains/deps.js.map +1 -1
- package/dist/domains/hook.js +71 -38
- package/dist/domains/hook.js.map +1 -1
- package/dist/domains/managed-work/completion-policy.js +174 -0
- package/dist/domains/managed-work/completion-policy.js.map +1 -0
- package/dist/domains/managed-work/context-manifest.js +208 -0
- package/dist/domains/managed-work/context-manifest.js.map +1 -0
- package/dist/domains/managed-work/contract.js +349 -0
- package/dist/domains/managed-work/contract.js.map +1 -0
- package/dist/domains/managed-work/control-signals.js +34 -0
- package/dist/domains/managed-work/control-signals.js.map +1 -0
- package/dist/domains/managed-work/control.js +628 -0
- package/dist/domains/managed-work/control.js.map +1 -0
- package/dist/domains/managed-work/evaluation.js +497 -0
- package/dist/domains/managed-work/evaluation.js.map +1 -0
- package/dist/domains/managed-work/evidence-path.js +14 -0
- package/dist/domains/managed-work/evidence-path.js.map +1 -0
- package/dist/domains/managed-work/execution-contract.js +167 -0
- package/dist/domains/managed-work/execution-contract.js.map +1 -0
- package/dist/domains/managed-work/executor-policy.js +65 -0
- package/dist/domains/managed-work/executor-policy.js.map +1 -0
- package/dist/domains/managed-work/failure.js +65 -0
- package/dist/domains/managed-work/failure.js.map +1 -0
- package/dist/domains/managed-work/host-review.js +77 -0
- package/dist/domains/managed-work/host-review.js.map +1 -0
- package/dist/domains/managed-work/human-messages.js +31 -0
- package/dist/domains/managed-work/human-messages.js.map +1 -0
- package/dist/domains/managed-work/i18n.js +12 -0
- package/dist/domains/managed-work/i18n.js.map +1 -0
- package/dist/domains/managed-work/input.js +137 -0
- package/dist/domains/managed-work/input.js.map +1 -0
- package/dist/domains/managed-work/journal.js +196 -0
- package/dist/domains/managed-work/journal.js.map +1 -0
- package/dist/domains/managed-work/lock.js +106 -0
- package/dist/domains/managed-work/lock.js.map +1 -0
- package/dist/domains/managed-work/notifications.js +38 -0
- package/dist/domains/managed-work/notifications.js.map +1 -0
- package/dist/domains/managed-work/pair-admission.js +108 -0
- package/dist/domains/managed-work/pair-admission.js.map +1 -0
- package/dist/domains/managed-work/paths.js +18 -0
- package/dist/domains/managed-work/paths.js.map +1 -0
- package/dist/domains/managed-work/prompts.js +453 -0
- package/dist/domains/managed-work/prompts.js.map +1 -0
- package/dist/domains/managed-work/redaction.js +90 -0
- package/dist/domains/managed-work/redaction.js.map +1 -0
- package/dist/domains/managed-work/review-convergence.js +41 -0
- package/dist/domains/managed-work/review-convergence.js.map +1 -0
- package/dist/domains/managed-work/review-facts.js +181 -0
- package/dist/domains/managed-work/review-facts.js.map +1 -0
- package/dist/domains/managed-work/rule-selection.js +84 -0
- package/dist/domains/managed-work/rule-selection.js.map +1 -0
- package/dist/domains/managed-work/runner.js +2418 -0
- package/dist/domains/managed-work/runner.js.map +1 -0
- package/dist/domains/managed-work/schemas.js +155 -0
- package/dist/domains/managed-work/schemas.js.map +1 -0
- package/dist/domains/managed-work/service.js +190 -0
- package/dist/domains/managed-work/service.js.map +1 -0
- package/dist/domains/managed-work/state.js +280 -0
- package/dist/domains/managed-work/state.js.map +1 -0
- package/dist/domains/managed-work/storage.js +223 -0
- package/dist/domains/managed-work/storage.js.map +1 -0
- package/dist/domains/managed-work/types.js +19 -0
- package/dist/domains/managed-work/types.js.map +1 -0
- package/dist/domains/managed-work/verification-categories.js +39 -0
- package/dist/domains/managed-work/verification-categories.js.map +1 -0
- package/dist/domains/managed-work/verification-policy.js +116 -0
- package/dist/domains/managed-work/verification-policy.js.map +1 -0
- package/dist/domains/managed-work/wait.js +58 -0
- package/dist/domains/managed-work/wait.js.map +1 -0
- package/dist/domains/managed-work/workspace-binding.js +133 -0
- package/dist/domains/managed-work/workspace-binding.js.map +1 -0
- package/dist/domains/sdd-readiness.js +44 -0
- package/dist/domains/sdd-readiness.js.map +1 -0
- package/dist/domains/skill/deploy.js +0 -6
- package/dist/domains/skill/deploy.js.map +1 -1
- package/dist/domains/skill/scripts.js +0 -8
- package/dist/domains/skill/scripts.js.map +1 -1
- package/dist/domains/state.js +1 -2
- package/dist/domains/state.js.map +1 -1
- package/dist/mcp/runtime-identity.js +21 -0
- package/dist/mcp/runtime-identity.js.map +1 -0
- package/dist/mcp/server.js +369 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/platform/agent-process.js +769 -0
- package/dist/platform/agent-process.js.map +1 -0
- package/dist/platform/executable.js +28 -0
- package/dist/platform/executable.js.map +1 -0
- package/dist/platform/paths.js +1 -17
- package/dist/platform/paths.js.map +1 -1
- package/dist/platform/process-liveness.js +10 -0
- package/dist/platform/process-liveness.js.map +1 -0
- package/dist/platform/process-tree.js +44 -0
- package/dist/platform/process-tree.js.map +1 -0
- package/dist/platform/process.js +1 -1
- package/dist/platform/process.js.map +1 -1
- package/dist/platform/version-check.js +30 -9
- package/dist/platform/version-check.js.map +1 -1
- package/package.json +16 -7
- package/scripts/check-design-principles.mjs +107 -0
- package/scripts/check-managed-work-design.mjs +507 -0
- package/scripts/clean-dist.js +9 -0
- package/scripts/ensure-bin-modes.js +11 -0
- package/dist/cli/index.js +0 -193
- package/dist/cli/index.js.map +0 -1
- package/dist/commands/archive.js +0 -6
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/clarify.js +0 -6
- package/dist/commands/clarify.js.map +0 -1
- package/dist/commands/design.js +0 -28
- package/dist/commands/design.js.map +0 -1
- package/dist/commands/docs.js +0 -6
- package/dist/commands/docs.js.map +0 -1
- package/dist/commands/doctor.js +0 -494
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/implement.js +0 -6
- package/dist/commands/implement.js.map +0 -1
- package/dist/commands/init.js +0 -520
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/pipeline.js +0 -6
- package/dist/commands/pipeline.js.map +0 -1
- package/dist/commands/scan.js +0 -63
- package/dist/commands/scan.js.map +0 -1
- package/dist/commands/status.js +0 -173
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/uninstall.js +0 -214
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/commands/update.js +0 -193
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/verify.js +0 -6
- package/dist/commands/verify.js.map +0 -1
- package/dist/core/assets.js +0 -27
- package/dist/core/assets.js.map +0 -1
- package/dist/core/cli-help.js +0 -98
- package/dist/core/cli-help.js.map +0 -1
- package/dist/core/context.js +0 -123
- package/dist/core/context.js.map +0 -1
- package/dist/core/dependencies.js +0 -146
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/detect.js +0 -64
- package/dist/core/detect.js.map +0 -1
- package/dist/core/i18n.js +0 -105
- package/dist/core/i18n.js.map +0 -1
- package/dist/core/integrity.js +0 -46
- package/dist/core/integrity.js.map +0 -1
- package/dist/core/manifest.js +0 -21
- package/dist/core/manifest.js.map +0 -1
- package/dist/core/prompts.js +0 -20
- package/dist/core/prompts.js.map +0 -1
- package/dist/core/registry.js +0 -134
- package/dist/core/registry.js.map +0 -1
- package/dist/core/rules.js +0 -17
- package/dist/core/rules.js.map +0 -1
- package/dist/core/scripts.js +0 -48
- package/dist/core/scripts.js.map +0 -1
- package/dist/core/skill-check.js +0 -32
- package/dist/core/skill-check.js.map +0 -1
- package/dist/core/skills.js +0 -62
- package/dist/core/skills.js.map +0 -1
- package/dist/core/state.js +0 -54
- package/dist/core/state.js.map +0 -1
- package/dist/utils/path.js +0 -11
- package/dist/utils/path.js.map +0 -1
- package/dist/utils/shell.js +0 -29
- package/dist/utils/shell.js.map +0 -1
|
Binary file
|
package/assets/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.2",
|
|
3
|
-
"agents": ["claude", "codex"
|
|
3
|
+
"agents": ["claude", "codex"],
|
|
4
4
|
"skills": [
|
|
5
5
|
"openspec-apply-change",
|
|
6
6
|
"openspec-archive-change",
|
|
@@ -13,13 +13,12 @@
|
|
|
13
13
|
"superflow-hotfix",
|
|
14
14
|
"superflow-implement",
|
|
15
15
|
"superflow-pipeline",
|
|
16
|
+
"superflow-requirement-review",
|
|
16
17
|
"superflow-table-impact-analysis",
|
|
17
18
|
"superflow-tweak",
|
|
18
19
|
"superflow-verify"
|
|
19
20
|
],
|
|
20
|
-
"rules": [
|
|
21
|
-
"superflow-phase-guard.md"
|
|
22
|
-
],
|
|
21
|
+
"rules": ["superflow-phase-guard.md"],
|
|
23
22
|
"scripts": [
|
|
24
23
|
"superflow-verify-integration.sh",
|
|
25
24
|
"superflow-delivery-check.sh",
|
|
@@ -31,13 +30,20 @@
|
|
|
31
30
|
"superflow-integration-evidence-hook.sh",
|
|
32
31
|
"superflow-archive-command-hook.sh",
|
|
33
32
|
"superflow-test-report-lint.py",
|
|
33
|
+
"superflow-managed-work-check.mjs",
|
|
34
|
+
"superflow-document-audit.mjs",
|
|
35
|
+
"superflow-environment-preflight.mjs",
|
|
36
|
+
"superflow-coding-ready.mjs",
|
|
37
|
+
"superflow-managed-executor-preflight.mjs",
|
|
38
|
+
"superflow-managed-owner-verification.sh",
|
|
39
|
+
"superflow-managed-command-guard.py",
|
|
40
|
+
"superflow-managed-work-guard.sh",
|
|
34
41
|
"install-sql-pre-commit.sh",
|
|
35
42
|
"sync-settings-json.py"
|
|
36
43
|
],
|
|
37
44
|
"agentScripts": {
|
|
38
45
|
"claude": ["claude-auto-backup-hook.sh"],
|
|
39
|
-
"codex": ["codex-auto-backup-hook.sh"]
|
|
40
|
-
"opencode": []
|
|
46
|
+
"codex": ["codex-auto-backup-hook.sh"]
|
|
41
47
|
},
|
|
42
48
|
"hooks": [
|
|
43
49
|
"superflow-enforce-hook.sh",
|
|
@@ -47,11 +53,11 @@
|
|
|
47
53
|
"superflow-sql-sync-hook.py",
|
|
48
54
|
"superflow-delivery-check.sh",
|
|
49
55
|
"superflow-integration-evidence-hook.sh",
|
|
50
|
-
"superflow-archive-command-hook.sh"
|
|
56
|
+
"superflow-archive-command-hook.sh",
|
|
57
|
+
"superflow-managed-work-guard.sh"
|
|
51
58
|
],
|
|
52
59
|
"agentHooks": {
|
|
53
60
|
"claude": ["claude-auto-backup-hook.sh"],
|
|
54
|
-
"codex": ["codex-auto-backup-hook.sh"]
|
|
55
|
-
"opencode": []
|
|
61
|
+
"codex": ["codex-auto-backup-hook.sh"]
|
|
56
62
|
}
|
|
57
63
|
}
|
|
@@ -8,3 +8,6 @@ Use the `superflow-design` skill for this request.
|
|
|
8
8
|
Let Superpowers handle source-level HOW: inspect real code, reverse impact,
|
|
9
9
|
field references, TDD strategy, worker split, worktree and port planning. Do not
|
|
10
10
|
rewrite OpenSpec contracts unless a conflict is found and returned to docs.
|
|
11
|
+
Complete the source-level Minimal Design Review before implementation planning:
|
|
12
|
+
prove reuse and necessity, choose the simplest implementation, and explicitly
|
|
13
|
+
remove or reject unsupported complexity.
|
|
@@ -7,4 +7,7 @@ Use the `superflow-docs` skill for this request.
|
|
|
7
7
|
|
|
8
8
|
Keep OpenSpec as the source of WHAT and contracts. Ensure API, DB, tests,
|
|
9
9
|
traceability, quality gates, and handoff files are complete before technical
|
|
10
|
-
design starts.
|
|
10
|
+
design starts. For full workflows, require a Minimal Design Review in
|
|
11
|
+
`design.md` and a PASS/BLOCKED verdict in `sdd-quality-gate.md`; prefer reuse
|
|
12
|
+
and remove unsupported tables, fields, APIs, services, caches, async flows,
|
|
13
|
+
jobs, and compatibility layers.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const changeArg = args.find((arg) => !arg.startsWith("--"));
|
|
11
|
+
const jsonOutput = args.includes("--json");
|
|
12
|
+
|
|
13
|
+
if (!changeArg) {
|
|
14
|
+
fail(["用法: superflow-coding-ready.mjs <change-dir> [--json]"]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const changeDir = fs.realpathSync(path.resolve(changeArg));
|
|
18
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const pipelineScripts = resolvePipelineScripts(scriptDir);
|
|
20
|
+
const checks = [
|
|
21
|
+
["YAML 状态", "bash", [path.join(pipelineScripts, "superflow-yaml-validate.sh"), changeDir]],
|
|
22
|
+
["docs 门禁", "bash", [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "docs"]],
|
|
23
|
+
["design 门禁", "bash", [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "design"]],
|
|
24
|
+
["implement 门禁", "bash", [path.join(pipelineScripts, "superflow-guard.sh"), changeDir, "implement"]],
|
|
25
|
+
["文档交付审计", process.execPath, [path.join(scriptDir, "superflow-document-audit.mjs"), changeDir, "--json"]],
|
|
26
|
+
["环境预检", process.execPath, [path.join(scriptDir, "superflow-environment-preflight.mjs"), changeDir, "--json"]],
|
|
27
|
+
];
|
|
28
|
+
const issues = [];
|
|
29
|
+
const evidence = [];
|
|
30
|
+
|
|
31
|
+
for (const [label, command, commandArgs] of checks) {
|
|
32
|
+
const result = spawnSync(command, commandArgs, {
|
|
33
|
+
cwd: projectRoot(changeDir),
|
|
34
|
+
encoding: "utf-8",
|
|
35
|
+
env: { ...process.env, SUPERFLOW_CODING_READY: "1" },
|
|
36
|
+
});
|
|
37
|
+
evidence.push({ label, exitCode: result.status ?? 1 });
|
|
38
|
+
if (result.status !== 0) {
|
|
39
|
+
issues.push(`${label}失败:${tail(result.stderr || result.stdout)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const openspec = spawnSync("openspec", ["validate", path.basename(changeDir), "--strict"], {
|
|
44
|
+
cwd: projectRoot(changeDir),
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
});
|
|
47
|
+
evidence.push({ label: "OpenSpec strict", exitCode: openspec.status ?? 1 });
|
|
48
|
+
if (openspec.status !== 0) {
|
|
49
|
+
issues.push(`OpenSpec strict 失败:${tail(openspec.stderr || openspec.stdout)}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const handoffHash = readHandoffHash(changeDir, issues);
|
|
53
|
+
const reviewRounds = readReviewRounds(changeDir);
|
|
54
|
+
const receipt = {
|
|
55
|
+
schemaVersion: "superflow.coding-ready.v1",
|
|
56
|
+
codingReady: issues.length === 0,
|
|
57
|
+
documentReadiness: issues.length === 0 ? "READY" : "BLOCKED",
|
|
58
|
+
environmentReadiness: issues.length === 0 ? "READY" : "BLOCKED",
|
|
59
|
+
reviewRounds,
|
|
60
|
+
handoffHash,
|
|
61
|
+
checkedAt: new Date().toISOString(),
|
|
62
|
+
evidence,
|
|
63
|
+
ownerDecisions: 0,
|
|
64
|
+
issues,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (issues.length > 0) fail(issues, receipt);
|
|
68
|
+
|
|
69
|
+
const receiptDir = path.join(changeDir, ".sdd", "readiness");
|
|
70
|
+
fs.mkdirSync(receiptDir, { recursive: true });
|
|
71
|
+
const target = path.join(receiptDir, "coding-ready.json");
|
|
72
|
+
const temporary = path.join(receiptDir, `.coding-ready-${process.pid}-${Date.now()}.tmp`);
|
|
73
|
+
fs.writeFileSync(temporary, `${JSON.stringify(receipt, null, 2)}\n`, { mode: 0o600 });
|
|
74
|
+
fs.renameSync(temporary, target);
|
|
75
|
+
|
|
76
|
+
if (jsonOutput) {
|
|
77
|
+
process.stdout.write(`${JSON.stringify(receipt, null, 2)}\n`);
|
|
78
|
+
} else {
|
|
79
|
+
process.stdout.write(`Coding Ready 通过,凭证已写入 ${target}\n`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolvePipelineScripts(currentScriptDir) {
|
|
83
|
+
const candidates = [
|
|
84
|
+
path.resolve(currentScriptDir, "..", "skills", "superflow-pipeline", "scripts"),
|
|
85
|
+
path.join(os.homedir(), ".codex", "skills", "superflow-pipeline", "scripts"),
|
|
86
|
+
path.join(os.homedir(), ".claude", "skills", "superflow-pipeline", "scripts"),
|
|
87
|
+
];
|
|
88
|
+
const found = candidates.find((candidate) =>
|
|
89
|
+
fs.existsSync(path.join(candidate, "superflow-guard.sh")),
|
|
90
|
+
);
|
|
91
|
+
if (!found) {
|
|
92
|
+
fail(["找不到 superflow-pipeline 门禁脚本,请先执行 superflow update"]);
|
|
93
|
+
}
|
|
94
|
+
return found;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function projectRoot(root) {
|
|
98
|
+
return path.resolve(root, "..", "..", "..");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function readHandoffHash(root, targetIssues) {
|
|
102
|
+
const file = path.join(root, ".sdd", "handoff", "sdd-context.sha256");
|
|
103
|
+
const content = fs.existsSync(file) ? fs.readFileSync(file, "utf-8") : "";
|
|
104
|
+
const match = content.match(/[a-f0-9]{64}/i);
|
|
105
|
+
if (!match) {
|
|
106
|
+
targetIssues.push("缺少合法的当前 handoff hash");
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
return match[0].toLowerCase();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function readReviewRounds(root) {
|
|
113
|
+
const file = path.join(root, ".sdd", "reviews", "document-review.json");
|
|
114
|
+
try {
|
|
115
|
+
const review = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
116
|
+
return Array.isArray(review.rounds) ? review.rounds.length : 0;
|
|
117
|
+
} catch {
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function tail(value) {
|
|
123
|
+
return String(value).trim().split("\n").slice(-4).join(" | ").slice(0, 800);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function fail(targetIssues, receipt = { codingReady: false, issues: targetIssues }) {
|
|
127
|
+
if (jsonOutput) {
|
|
128
|
+
process.stderr.write(`${JSON.stringify(receipt, null, 2)}\n`);
|
|
129
|
+
} else {
|
|
130
|
+
process.stderr.write(
|
|
131
|
+
`Coding Ready 未通过:\n${targetIssues.map((item) => `- ${item}`).join("\n")}\n`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
@@ -24,6 +24,22 @@ fi
|
|
|
24
24
|
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/superflow"
|
|
25
25
|
mkdir -p "$STATE_DIR" 2>/dev/null || exit 0
|
|
26
26
|
|
|
27
|
+
LANGUAGE="${SUPERFLOW_LANG:-}"
|
|
28
|
+
if [ -z "$LANGUAGE" ] && [ -f "$HOME/.sdd-state.json" ]; then
|
|
29
|
+
LANGUAGE=$(python3 -c '
|
|
30
|
+
import json, os
|
|
31
|
+
try:
|
|
32
|
+
with open(os.path.expanduser("~/.sdd-state.json"), encoding="utf-8") as f:
|
|
33
|
+
print((json.load(f).get("language") or "").strip())
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
' 2>/dev/null)
|
|
37
|
+
fi
|
|
38
|
+
case "${LANGUAGE%%_*}" in
|
|
39
|
+
en|en-*) LANGUAGE="en" ;;
|
|
40
|
+
*) LANGUAGE="zh" ;;
|
|
41
|
+
esac
|
|
42
|
+
|
|
27
43
|
SESSION_KEY=$(printf '%s' "$INPUT" | python3 -c '
|
|
28
44
|
import hashlib, json, os, sys
|
|
29
45
|
raw = sys.stdin.read()
|
|
@@ -111,15 +127,49 @@ if version:
|
|
|
111
127
|
update_npm_if_needed() {
|
|
112
128
|
local package_name="$1"
|
|
113
129
|
local current latest
|
|
114
|
-
command -v npm >/dev/null 2>&1 || return
|
|
130
|
+
command -v npm >/dev/null 2>&1 || return 1
|
|
115
131
|
latest="$(latest_npm_version "$package_name")"
|
|
116
|
-
[ -
|
|
132
|
+
if [ -z "$latest" ]; then
|
|
133
|
+
if [ "$LANGUAGE" = "en" ]; then
|
|
134
|
+
printf '\n[%s] Cannot confirm the latest version of %s\n' \
|
|
135
|
+
"$(date '+%Y-%m-%d %H:%M:%S')" "$package_name" \
|
|
136
|
+
>> "$LOG_FILE" 2>/dev/null || true
|
|
137
|
+
else
|
|
138
|
+
printf '\n[%s] 无法确认 %s 的最新版本\n' \
|
|
139
|
+
"$(date '+%Y-%m-%d %H:%M:%S')" "$package_name" \
|
|
140
|
+
>> "$LOG_FILE" 2>/dev/null || true
|
|
141
|
+
fi
|
|
142
|
+
return 1
|
|
143
|
+
fi
|
|
117
144
|
current="$(installed_npm_version "$package_name")"
|
|
118
145
|
if [ "$current" != "$latest" ]; then
|
|
119
|
-
run_logged npm install -g "$package_name@latest"
|
|
146
|
+
run_logged npm install -g "$package_name@latest"
|
|
120
147
|
fi
|
|
121
148
|
}
|
|
122
149
|
|
|
150
|
+
detected_agents() {
|
|
151
|
+
local agents=""
|
|
152
|
+
command -v codex >/dev/null 2>&1 && agents="codex"
|
|
153
|
+
if command -v claude >/dev/null 2>&1; then
|
|
154
|
+
agents="${agents:+$agents,}claude"
|
|
155
|
+
fi
|
|
156
|
+
printf '%s' "$agents"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
refresh_superflow_installation() {
|
|
160
|
+
local agents="$1"
|
|
161
|
+
[ -n "$agents" ] || return 1
|
|
162
|
+
if command -v superflow >/dev/null 2>&1; then
|
|
163
|
+
run_logged superflow update --agent "$agents" --scope global
|
|
164
|
+
return $?
|
|
165
|
+
fi
|
|
166
|
+
local npm_root cli
|
|
167
|
+
npm_root="$(npm root -g 2>/dev/null)" || return 1
|
|
168
|
+
cli="$npm_root/@chenmk/superflow/dist/app/cli.js"
|
|
169
|
+
[ -f "$cli" ] || return 1
|
|
170
|
+
run_logged node "$cli" update --agent "$agents" --scope global
|
|
171
|
+
}
|
|
172
|
+
|
|
123
173
|
check_npm_package() {
|
|
124
174
|
local package_name="$1"
|
|
125
175
|
local current latest
|
|
@@ -135,15 +185,34 @@ check_npm_package() {
|
|
|
135
185
|
}
|
|
136
186
|
|
|
137
187
|
if [ "$MODE" = "apply" ]; then
|
|
138
|
-
|
|
139
|
-
update_npm_if_needed "@
|
|
188
|
+
APPLY_FAILED=0
|
|
189
|
+
update_npm_if_needed "@chenmk/superflow" || APPLY_FAILED=1
|
|
190
|
+
update_npm_if_needed "@fission-ai/openspec" || APPLY_FAILED=1
|
|
140
191
|
|
|
141
192
|
if command -v claude >/dev/null 2>&1; then
|
|
142
|
-
run_logged claude plugin install superpowers@superpowers-marketplace ||
|
|
193
|
+
run_logged claude plugin install superpowers@superpowers-marketplace || APPLY_FAILED=1
|
|
143
194
|
fi
|
|
144
195
|
|
|
145
196
|
if command -v codex >/dev/null 2>&1; then
|
|
146
|
-
run_logged codex plugin add superpowers@openai-curated ||
|
|
197
|
+
run_logged codex plugin add superpowers@openai-api-curated || APPLY_FAILED=1
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
AGENT_VALUE="$(detected_agents)"
|
|
201
|
+
refresh_superflow_installation "$AGENT_VALUE" || APPLY_FAILED=1
|
|
202
|
+
|
|
203
|
+
if [ "$APPLY_FAILED" -ne 0 ]; then
|
|
204
|
+
rm -f "$STAMP" "$GLOBAL_STAMP" 2>/dev/null || true
|
|
205
|
+
if [ "$LANGUAGE" = "en" ]; then
|
|
206
|
+
printf '[Superflow] Automatic update was incomplete and remains retryable; details: %s\n' "$LOG_FILE" >&2
|
|
207
|
+
else
|
|
208
|
+
printf '[Superflow] 自动升级未完整完成,已保留重试资格;详情:%s\n' "$LOG_FILE" >&2
|
|
209
|
+
fi
|
|
210
|
+
else
|
|
211
|
+
if [ "$LANGUAGE" = "en" ]; then
|
|
212
|
+
printf '[Superflow] Automatic update refreshed CLI, dependencies, Skills, Hooks, rules, and managed MCP; restart the Agent to load the new runtime.\n' >&2
|
|
213
|
+
else
|
|
214
|
+
printf '[Superflow] 自动升级已完整更新 CLI、依赖、Skills、Hooks、规则和托管 MCP;请重启 Agent 加载新运行时。\n' >&2
|
|
215
|
+
fi
|
|
147
216
|
fi
|
|
148
217
|
else
|
|
149
218
|
STATUS_FILE="$STATE_DIR/dependency-update-status.txt"
|
|
@@ -154,7 +223,11 @@ else
|
|
|
154
223
|
printf 'superpowers=run superflow update --with-package to refresh selected agent plugins\n'
|
|
155
224
|
} > "$STATUS_FILE" 2>/dev/null || true
|
|
156
225
|
if grep -Eq '^@' "$STATUS_FILE" 2>/dev/null; then
|
|
157
|
-
|
|
226
|
+
if [ "$LANGUAGE" = "en" ]; then
|
|
227
|
+
printf '[Superflow] Core dependency updates are available. Run superflow update --with-package; details: %s\n' "$STATUS_FILE" >&2
|
|
228
|
+
else
|
|
229
|
+
printf '[Superflow] 有核心依赖更新,执行 superflow update --with-package 统一更新;详情:%s\n' "$STATUS_FILE" >&2
|
|
230
|
+
fi
|
|
158
231
|
fi
|
|
159
232
|
fi
|
|
160
233
|
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const changeArg = args.find((arg) => !arg.startsWith("--"));
|
|
8
|
+
const jsonOutput = args.includes("--json");
|
|
9
|
+
|
|
10
|
+
if (!changeArg) {
|
|
11
|
+
fail(["用法: superflow-document-audit.mjs <change-dir> [--json]"]);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const changeDir = fs.realpathSync(path.resolve(changeArg));
|
|
15
|
+
const issues = [];
|
|
16
|
+
const prompts = markdownFiles(path.join(changeDir, "prompt"));
|
|
17
|
+
const handoffHash = readHandoffHash(changeDir, issues);
|
|
18
|
+
const aggregateDocuments = [
|
|
19
|
+
"tasks.md",
|
|
20
|
+
"traceability-matrix.md",
|
|
21
|
+
"sdd-quality-gate.md",
|
|
22
|
+
"test-report.md",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
if (prompts.length === 0) {
|
|
26
|
+
issues.push("prompt/ 目录至少需要一份 Markdown 实现 Prompt");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const document of aggregateDocuments) {
|
|
30
|
+
const documentPath = path.join(changeDir, document);
|
|
31
|
+
if (!fs.existsSync(documentPath)) {
|
|
32
|
+
issues.push(`缺少文档: ${document}`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const links = markdownTargets(fs.readFileSync(documentPath, "utf-8"));
|
|
36
|
+
for (const prompt of prompts) {
|
|
37
|
+
const relativePrompt = path.relative(changeDir, prompt).replaceAll("\\", "/");
|
|
38
|
+
if (!links.has(relativePrompt)) {
|
|
39
|
+
issues.push(`${document} 未链接 ${relativePrompt}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const review = readJson(
|
|
45
|
+
path.join(changeDir, ".sdd", "reviews", "document-review.json"),
|
|
46
|
+
"文档多轮评审凭证",
|
|
47
|
+
issues,
|
|
48
|
+
);
|
|
49
|
+
validateReview(review, handoffHash, issues);
|
|
50
|
+
validateMermaid(changeDir, issues);
|
|
51
|
+
|
|
52
|
+
const result = {
|
|
53
|
+
schemaVersion: "superflow.document-audit.v1",
|
|
54
|
+
ready: issues.length === 0,
|
|
55
|
+
handoffHash,
|
|
56
|
+
promptCount: prompts.length,
|
|
57
|
+
reviewRounds: Array.isArray(review?.rounds) ? review.rounds.length : 0,
|
|
58
|
+
issues,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (issues.length > 0) {
|
|
62
|
+
fail(issues, result);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
print(result);
|
|
66
|
+
|
|
67
|
+
function validateReview(review, currentHash, targetIssues) {
|
|
68
|
+
if (!review) return;
|
|
69
|
+
if (review.schemaVersion !== "superflow.document-review.v1") {
|
|
70
|
+
targetIssues.push("文档评审凭证 schemaVersion 必须为 superflow.document-review.v1");
|
|
71
|
+
}
|
|
72
|
+
if (review.handoffHash !== currentHash) {
|
|
73
|
+
targetIssues.push("文档评审凭证未绑定当前 handoff hash");
|
|
74
|
+
}
|
|
75
|
+
if (review.verdict !== "PASS") {
|
|
76
|
+
targetIssues.push("文档评审最终结论必须为 PASS");
|
|
77
|
+
}
|
|
78
|
+
if ((review.openOwnerDecisions?.length ?? 0) > 0) {
|
|
79
|
+
targetIssues.push("仍有 owner 决策未关闭,应集中询问用户后重新评审");
|
|
80
|
+
}
|
|
81
|
+
if (!Array.isArray(review.rounds) || review.rounds.length < 3) {
|
|
82
|
+
targetIssues.push("文档交付前至少三轮独立视角评审");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const requiredLenses = new Set([
|
|
86
|
+
"source-contract",
|
|
87
|
+
"architecture-minimality",
|
|
88
|
+
"e2e-environment",
|
|
89
|
+
]);
|
|
90
|
+
for (const round of review.rounds) {
|
|
91
|
+
requiredLenses.delete(round.lens);
|
|
92
|
+
if (round.inputHash !== currentHash) {
|
|
93
|
+
targetIssues.push(`第 ${round.round ?? "?"} 轮评审不是基于当前 handoff hash`);
|
|
94
|
+
}
|
|
95
|
+
const openFindings = (round.findings ?? []).filter(
|
|
96
|
+
(finding) => finding.status !== "closed",
|
|
97
|
+
);
|
|
98
|
+
if (openFindings.length > 0) {
|
|
99
|
+
targetIssues.push(`第 ${round.round ?? "?"} 轮仍有未关闭评审发现`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (requiredLenses.size > 0) {
|
|
103
|
+
targetIssues.push(`缺少评审视角: ${[...requiredLenses].join(", ")}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function validateMermaid(root, targetIssues) {
|
|
108
|
+
const configPath = path.join(root, ".openspec.yaml");
|
|
109
|
+
const config = fs.existsSync(configPath)
|
|
110
|
+
? fs.readFileSync(configPath, "utf-8")
|
|
111
|
+
: "";
|
|
112
|
+
const required =
|
|
113
|
+
/^\s*mermaid:\s*required\s*$/m.test(config) ||
|
|
114
|
+
/^\s*complex_logic:\s*true\s*$/m.test(config) ||
|
|
115
|
+
/^\s*cross_repo:\s*true\s*$/m.test(config);
|
|
116
|
+
if (!required) return;
|
|
117
|
+
|
|
118
|
+
const markdown = ["design.md", "tests.md"]
|
|
119
|
+
.map((file) => path.join(root, file))
|
|
120
|
+
.filter((file) => fs.existsSync(file))
|
|
121
|
+
.map((file) => fs.readFileSync(file, "utf-8"))
|
|
122
|
+
.join("\n");
|
|
123
|
+
const blocks = [...markdown.matchAll(/```mermaid\s*\n([\s\S]*?)```/g)].map(
|
|
124
|
+
(match) => match[1].trim(),
|
|
125
|
+
);
|
|
126
|
+
for (const block of blocks) {
|
|
127
|
+
validateMermaidBlock(block, targetIssues);
|
|
128
|
+
}
|
|
129
|
+
if (!blocks.some((block) => /^sequenceDiagram\b/m.test(block))) {
|
|
130
|
+
targetIssues.push("复杂逻辑必须包含 Mermaid sequenceDiagram 端到端时序图");
|
|
131
|
+
}
|
|
132
|
+
if (
|
|
133
|
+
!blocks.some((block) =>
|
|
134
|
+
/^(flowchart\b|graph\b|stateDiagram(?:-v2)?\b)/m.test(block),
|
|
135
|
+
)
|
|
136
|
+
) {
|
|
137
|
+
targetIssues.push("复杂逻辑必须包含 Mermaid flowchart 或 stateDiagram");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function validateMermaidBlock(block, targetIssues) {
|
|
142
|
+
const lines = block
|
|
143
|
+
.split("\n")
|
|
144
|
+
.map((line) => line.trim())
|
|
145
|
+
.filter((line) => line && !line.startsWith("%%"));
|
|
146
|
+
const directive = lines[0] ?? "";
|
|
147
|
+
if (/^sequenceDiagram\b/.test(directive)) {
|
|
148
|
+
const arrows = lines.filter((line) => /-{1,2}>>|--?>/.test(line));
|
|
149
|
+
if (arrows.length === 0) {
|
|
150
|
+
targetIssues.push("Mermaid sequenceDiagram 缺少有效消息箭头");
|
|
151
|
+
}
|
|
152
|
+
const openings = lines.filter((line) => /^(alt|opt|loop|par|critical)\b/.test(line)).length;
|
|
153
|
+
const endings = lines.filter((line) => line === "end").length;
|
|
154
|
+
if (openings !== endings) {
|
|
155
|
+
targetIssues.push("Mermaid sequenceDiagram 分支块未正确闭合");
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (/^(flowchart|graph)\s+(TD|TB|BT|LR|RL)\b/.test(directive)) {
|
|
160
|
+
if (!lines.slice(1).some((line) => /-->|---|==>|-.->/.test(line))) {
|
|
161
|
+
targetIssues.push("Mermaid flowchart 缺少有效连线");
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (/^stateDiagram(?:-v2)?\b/.test(directive)) {
|
|
166
|
+
if (!lines.slice(1).some((line) => /-->/.test(line))) {
|
|
167
|
+
targetIssues.push("Mermaid stateDiagram 缺少状态迁移");
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
targetIssues.push(`不支持或不完整的 Mermaid 图声明: ${directive || "空图"}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function markdownFiles(directory) {
|
|
175
|
+
if (!fs.existsSync(directory)) return [];
|
|
176
|
+
return fs
|
|
177
|
+
.readdirSync(directory, { withFileTypes: true })
|
|
178
|
+
.flatMap((entry) => {
|
|
179
|
+
const target = path.join(directory, entry.name);
|
|
180
|
+
if (entry.isDirectory()) return markdownFiles(target);
|
|
181
|
+
return entry.isFile() && entry.name.endsWith(".md") ? [target] : [];
|
|
182
|
+
})
|
|
183
|
+
.sort();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function markdownTargets(content) {
|
|
187
|
+
return new Set(
|
|
188
|
+
[...content.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)].map((match) =>
|
|
189
|
+
match[1].split("#", 1)[0].replace(/^\.\//, ""),
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function readHandoffHash(root, targetIssues) {
|
|
195
|
+
const hashPath = path.join(root, ".sdd", "handoff", "sdd-context.sha256");
|
|
196
|
+
if (!fs.existsSync(hashPath)) {
|
|
197
|
+
targetIssues.push("缺少当前 handoff hash");
|
|
198
|
+
return "";
|
|
199
|
+
}
|
|
200
|
+
const match = fs.readFileSync(hashPath, "utf-8").match(/[a-f0-9]{64}/i);
|
|
201
|
+
if (!match) {
|
|
202
|
+
targetIssues.push("handoff hash 格式不正确");
|
|
203
|
+
return "";
|
|
204
|
+
}
|
|
205
|
+
return match[0].toLowerCase();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function readJson(file, label, targetIssues) {
|
|
209
|
+
if (!fs.existsSync(file)) {
|
|
210
|
+
targetIssues.push(`缺少${label}: ${path.relative(changeDir, file)}`);
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
215
|
+
} catch {
|
|
216
|
+
targetIssues.push(`${label}不是合法 JSON`);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function print(result) {
|
|
222
|
+
if (jsonOutput) {
|
|
223
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
process.stdout.write(
|
|
227
|
+
`文档交付审计通过:${result.reviewRounds} 轮评审,${result.promptCount} 份 Prompt\n`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function fail(targetIssues, result = { ready: false, issues: targetIssues }) {
|
|
232
|
+
if (jsonOutput) {
|
|
233
|
+
process.stderr.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
234
|
+
} else {
|
|
235
|
+
process.stderr.write(
|
|
236
|
+
`文档交付审计失败:\n${targetIssues.map((item) => `- ${item}`).join("\n")}\n`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|