@michaelxwb/code-flow 0.6.5 → 0.8.1
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/README.md +47 -3
- package/docs/USAGE.md +1529 -0
- package/docs/acceptance-contract.md +217 -0
- package/docs/ci/acceptance-evidence.md +77 -0
- package/docs/migrations/runtime-0.7.0.md +41 -0
- package/docs/migrations/runtime-0.8.0.md +154 -0
- package/docs/migrations/runtime-0.9.0.md +40 -0
- package/docs/spec-injection-hardening.md +54 -0
- package/docs/spec-injection-performance.json +73 -0
- package/docs/spec-optimization.md +12 -0
- package/package.json +18 -3
- package/src/adapters/claude/CLAUDE.md +1 -1
- package/src/adapters/claude/commands/cf-init.md +1 -3
- package/src/adapters/claude/commands/cf-spec.md +23 -3
- package/src/adapters/claude/commands/cf-stats.md +11 -1
- package/src/adapters/claude/commands/cf-sync.md +20 -6
- package/src/adapters/claude/commands/cf-task/align.md +25 -3
- package/src/adapters/claude/commands/cf-task/archive.md +36 -42
- package/src/adapters/claude/commands/cf-task/block.md +15 -2
- package/src/adapters/claude/commands/cf-task/graph.md +13 -1
- package/src/adapters/claude/commands/cf-task/note.md +13 -1
- package/src/adapters/claude/commands/cf-task/plan.md +137 -23
- package/src/adapters/claude/commands/cf-task/prd.md +13 -2
- package/src/adapters/claude/commands/cf-task/start.md +52 -39
- package/src/adapters/claude/commands/cf-task/status.md +12 -0
- package/src/adapters/claude/commands/cf-task/verify-e2e.md +68 -35
- package/src/adapters/claude/commands/cf-validate.md +30 -5
- package/src/adapters/codex/AGENTS.md +1 -1
- package/src/adapters/codex/hooks.json +14 -10
- package/src/adapters/codex/skills/cf-init/SKILL.md +4 -4
- package/src/adapters/codex/skills/cf-spec/SKILL.md +34 -6
- package/src/adapters/codex/skills/cf-stats/SKILL.md +11 -1
- package/src/adapters/codex/skills/cf-sync/SKILL.md +21 -6
- package/src/adapters/codex/skills/cf-task-align/SKILL.md +30 -8
- package/src/adapters/codex/skills/cf-task-archive/SKILL.md +36 -42
- package/src/adapters/codex/skills/cf-task-block/SKILL.md +15 -2
- package/src/adapters/codex/skills/cf-task-graph/SKILL.md +13 -1
- package/src/adapters/codex/skills/cf-task-note/SKILL.md +13 -1
- package/src/adapters/codex/skills/cf-task-plan/SKILL.md +175 -21
- package/src/adapters/codex/skills/cf-task-prd/SKILL.md +13 -2
- package/src/adapters/codex/skills/cf-task-start/SKILL.md +52 -39
- package/src/adapters/codex/skills/cf-task-status/SKILL.md +12 -0
- package/src/adapters/codex/skills/cf-task-verify-e2e/SKILL.md +69 -35
- package/src/adapters/codex/skills/cf-validate/SKILL.md +31 -5
- package/src/adapters/commands.json +115 -0
- package/src/adapters/costrict/CLAUDE.md +1 -1
- package/src/adapters/costrict/commands/cf-init.md +1 -3
- package/src/adapters/costrict/commands/cf-spec.md +23 -3
- package/src/adapters/costrict/commands/cf-stats.md +11 -1
- package/src/adapters/costrict/commands/cf-sync.md +20 -6
- package/src/adapters/costrict/commands/cf-task/align.md +25 -3
- package/src/adapters/costrict/commands/cf-task/archive.md +36 -42
- package/src/adapters/costrict/commands/cf-task/block.md +15 -2
- package/src/adapters/costrict/commands/cf-task/graph.md +13 -1
- package/src/adapters/costrict/commands/cf-task/note.md +13 -1
- package/src/adapters/costrict/commands/cf-task/plan.md +137 -23
- package/src/adapters/costrict/commands/cf-task/prd.md +13 -2
- package/src/adapters/costrict/commands/cf-task/start.md +52 -39
- package/src/adapters/costrict/commands/cf-task/status.md +12 -0
- package/src/adapters/costrict/commands/cf-task/verify-e2e.md +68 -35
- package/src/adapters/costrict/commands/cf-validate.md +30 -5
- package/src/adapters/install-plan.json +100 -0
- package/src/adapters/opencode/AGENTS.md +1 -1
- package/src/adapters/opencode/commands/cf-init.md +7 -7
- package/src/adapters/opencode/commands/cf-learn.md +6 -6
- package/src/adapters/opencode/commands/cf-spec.md +34 -2
- package/src/adapters/opencode/commands/cf-stats.md +17 -7
- package/src/adapters/opencode/commands/cf-sync.md +21 -6
- package/src/adapters/opencode/commands/cf-task/align.md +25 -8
- package/src/adapters/opencode/commands/cf-task/archive.md +36 -42
- package/src/adapters/opencode/commands/cf-task/block.md +16 -2
- package/src/adapters/opencode/commands/cf-task/graph.md +14 -1
- package/src/adapters/opencode/commands/cf-task/note.md +14 -1
- package/src/adapters/opencode/commands/cf-task/plan.md +137 -23
- package/src/adapters/opencode/commands/cf-task/prd.md +13 -2
- package/src/adapters/opencode/commands/cf-task/start.md +53 -45
- package/src/adapters/opencode/commands/cf-task/status.md +13 -0
- package/src/adapters/opencode/commands/cf-task/verify-e2e.md +68 -35
- package/src/adapters/opencode/commands/cf-validate.md +35 -9
- package/src/adapters/opencode/plugins/code-flow/index.js +128 -179
- package/src/adapters/platform-bindings.json +370 -0
- package/src/cli.js +369 -316
- package/src/core/code-flow/config.yml +4 -1
- package/src/core/code-flow/runtime-commands.json +259 -0
- package/src/core/code-flow/scripts/cf_acceptance_manifest.py +5 -289
- package/src/core/code-flow/scripts/cf_acceptance_runner.py +5 -187
- package/src/core/code-flow/scripts/cf_audit.py +10 -0
- package/src/core/code-flow/scripts/cf_codex_hook.py +10 -0
- package/src/core/code-flow/scripts/cf_design_lint.py +10 -0
- package/src/core/code-flow/scripts/cf_evidence_import.py +10 -0
- package/src/core/code-flow/scripts/cf_feedback.py +5 -51
- package/src/core/code-flow/scripts/cf_opencode_event.py +10 -0
- package/src/core/code-flow/scripts/cf_post_hook.py +6 -191
- package/src/core/code-flow/scripts/cf_pre_tool_hook.py +6 -99
- package/src/core/code-flow/scripts/cf_scenarios.py +10 -0
- package/src/core/code-flow/scripts/cf_spec_context.py +5 -1741
- package/src/core/code-flow/scripts/cf_spec_gate.py +5 -259
- package/src/core/code-flow/scripts/cf_spec_session.py +5 -114
- package/src/core/code-flow/scripts/cf_stats.py +5 -501
- package/src/core/code-flow/scripts/cf_stop_hook.py +6 -376
- package/src/core/code-flow/scripts/cf_sync.py +5 -135
- package/src/core/code-flow/scripts/cf_task_index.py +5 -143
- package/src/core/code-flow/scripts/cf_task_parallel.py +5 -389
- package/src/core/code-flow/scripts/cf_task_workflow.py +6 -201
- package/src/core/code-flow/scripts/cf_user_prompt_hook.py +6 -173
- package/src/core/code-flow/scripts/cf_validation.py +6 -77
- package/src/core/code-flow/scripts/codeflow/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_evidence.py +347 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_manifest_core.py +660 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_plan.py +202 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_registration.py +60 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_risk.py +73 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_schema.py +282 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_selection.py +266 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_check_inputs.py +172 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_check_registration.py +326 -0
- package/src/core/code-flow/scripts/{cf_checks.py → codeflow/acceptance/cf_checks.py} +8 -11
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_plan_quality.py +197 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_scenario_quality.py +174 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_scenario_source.py +300 -0
- package/src/core/code-flow/scripts/codeflow/acceptance/cf_traceability.py +502 -0
- package/src/core/code-flow/scripts/codeflow/cli/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_acceptance_manifest.py +347 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_acceptance_runner.py +1490 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_audit.py +533 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_design_lint.py +172 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_evidence_import.py +607 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_feedback.py +53 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_scenarios.py +58 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_spec_context.py +1175 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_spec_gate.py +60 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_spec_session.py +46 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_stats.py +540 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_sync.py +144 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_task_index.py +177 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_task_parallel.py +1142 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_task_workflow.py +742 -0
- package/src/core/code-flow/scripts/codeflow/cli/cf_validation.py +31 -0
- package/src/core/code-flow/scripts/codeflow/core/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_context_cache.py +67 -0
- package/src/core/code-flow/scripts/{cf_core.py → codeflow/core/cf_core.py} +119 -297
- package/src/core/code-flow/scripts/codeflow/core/cf_exec_base.py +325 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_fs.py +57 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_git.py +92 -0
- package/src/core/code-flow/scripts/{cf_log.py → codeflow/core/cf_log.py} +13 -7
- package/src/core/code-flow/scripts/codeflow/core/cf_markdown.py +77 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_native_paths.py +26 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_patch.py +33 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_runtime_install.py +32 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_stop_guard.py +53 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_task_state.py +9 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_validators.py +408 -0
- package/src/core/code-flow/scripts/codeflow/core/cf_worktree_env.py +253 -0
- package/src/core/code-flow/scripts/codeflow/evidence/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/evidence/cf_evidence_store.py +527 -0
- package/src/core/code-flow/scripts/codeflow/hooks/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/hooks/cf_codex_hook.py +95 -0
- package/src/core/code-flow/scripts/codeflow/hooks/cf_opencode_event.py +98 -0
- package/src/core/code-flow/scripts/codeflow/hooks/cf_post_hook.py +30 -0
- package/src/core/code-flow/scripts/codeflow/hooks/cf_pre_tool_hook.py +41 -0
- package/src/core/code-flow/scripts/codeflow/hooks/cf_stop_hook.py +292 -0
- package/src/core/code-flow/scripts/codeflow/hooks/cf_user_prompt_hook.py +161 -0
- package/src/core/code-flow/scripts/codeflow/spec/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_active_task.py +412 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_context_refresh.py +301 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_edit_service.py +149 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_injection.py +111 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_rule_readiness.py +114 -0
- package/src/core/code-flow/scripts/{cf_scan.py → codeflow/spec/cf_scan.py} +93 -21
- package/src/core/code-flow/scripts/codeflow/spec/cf_spec_budget.py +55 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_spec_context_model.py +477 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_spec_impact.py +119 -0
- package/src/core/code-flow/scripts/{cf_spec_metadata.py → codeflow/spec/cf_spec_metadata.py} +5 -2
- package/src/core/code-flow/scripts/{cf_spec_migrate.py → codeflow/spec/cf_spec_migrate.py} +27 -10
- package/src/core/code-flow/scripts/codeflow/spec/cf_spec_optimize.py +193 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_spec_optimize_apply.py +187 -0
- package/src/core/code-flow/scripts/{cf_spec_resolver.py → codeflow/spec/cf_spec_resolver.py} +39 -17
- package/src/core/code-flow/scripts/{cf_spec_router.py → codeflow/spec/cf_spec_router.py} +96 -36
- package/src/core/code-flow/scripts/{cf_spec_verify.py → codeflow/spec/cf_spec_verify.py} +163 -35
- package/src/core/code-flow/scripts/codeflow/spec/cf_stage_gate.py +330 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_task_projection.py +83 -0
- package/src/core/code-flow/scripts/codeflow/spec/cf_workflow_service.py +755 -0
- package/src/core/code-flow/scripts/{cf_workflow_transaction.py → codeflow/spec/cf_workflow_transaction.py} +29 -15
- package/src/core/code-flow/scripts/codeflow/task/__init__.py +0 -0
- package/src/core/code-flow/scripts/codeflow/task/cf_requirement_verification.py +867 -0
- package/src/core/code-flow/scripts/codeflow/task/cf_task_doc.py +178 -0
- package/src/core/code-flow/scripts/codeflow/task/cf_task_merge.py +635 -0
- package/src/core/code-flow/scripts/codeflow/task/cf_task_plan.py +251 -0
- package/src/core/code-flow/scripts/codeflow/task/cf_task_runtime.py +570 -0
- package/src/core/code-flow/scripts/codeflow/task/cf_validation_service.py +73 -0
- package/src/core/code-flow/scripts/runtime-resources.json +29 -0
- package/src/core/code-flow/specs/shared/design/design-frontend.md +18 -15
- package/src/core/code-flow/specs/shared/design/design-full.md +33 -23
- package/src/core/code-flow/specs/shared/design/design-lite.md +23 -13
- package/src/core/code-flow/specs/shared/scenarios-template.md +55 -0
- package/src/core/code-flow/validation.yml +9 -3
- package/src/migrate/runtime.js +213 -0
- package/src/migrate/spec-workflow.js +16 -1
- package/src/runtime.js +219 -0
- package/src/core/code-flow/scripts/cf_acceptance_evidence.py +0 -100
- package/src/core/code-flow/scripts/cf_acceptance_schema.py +0 -83
- package/src/core/code-flow/scripts/cf_exec_base.py +0 -112
- package/src/core/code-flow/scripts/cf_task_runtime.py +0 -304
- package/src/core/code-flow/scripts/cf_task_state.py +0 -3
- package/src/core/code-flow/scripts/cf_workflow_service.py +0 -366
- /package/src/core/code-flow/scripts/{cf_session_state.py → codeflow/core/cf_session_state.py} +0 -0
|
@@ -2,7 +2,7 @@ version: 1
|
|
|
2
2
|
|
|
3
3
|
spec_workflow:
|
|
4
4
|
schema_version: 1
|
|
5
|
-
enforcement: required #
|
|
5
|
+
enforcement: required # 推荐全门禁;历史 warn/inject 仅兼容部分验证降级,路由完整性错误始终阻断
|
|
6
6
|
drift_check: true
|
|
7
7
|
scope_expansion_check: true
|
|
8
8
|
metrics: true
|
|
@@ -92,6 +92,9 @@ path_mapping:
|
|
|
92
92
|
- path: "shared/_map.md"
|
|
93
93
|
tags: ["*"]
|
|
94
94
|
tier: 0
|
|
95
|
+
- path: "shared/scenarios-template.md"
|
|
96
|
+
tags: [] # 永不自动注入,仅 align 显式 Read
|
|
97
|
+
tier: 1
|
|
95
98
|
- path: "shared/prd-template.md"
|
|
96
99
|
tags: [] # 永不自动注入,仅 cf-task:prd 命令显式 Read
|
|
97
100
|
tier: 1
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"commands": {
|
|
4
|
+
"spec budget": {
|
|
5
|
+
"module": "cf_spec_context",
|
|
6
|
+
"prefix": ["budget"],
|
|
7
|
+
"example_args": "--help"
|
|
8
|
+
},
|
|
9
|
+
"spec impact": {
|
|
10
|
+
"module": "cf_spec_context",
|
|
11
|
+
"prefix": ["impact"],
|
|
12
|
+
"example_args": "--help"
|
|
13
|
+
},
|
|
14
|
+
"spec optimize": {
|
|
15
|
+
"module": "cf_spec_context",
|
|
16
|
+
"prefix": ["optimize"],
|
|
17
|
+
"example_args": "--help"
|
|
18
|
+
},
|
|
19
|
+
"spec catalog": {
|
|
20
|
+
"module": "cf_spec_context",
|
|
21
|
+
"prefix": [
|
|
22
|
+
"catalog"
|
|
23
|
+
],
|
|
24
|
+
"example_args": "--help"
|
|
25
|
+
},
|
|
26
|
+
"spec decision": {
|
|
27
|
+
"module": "cf_spec_context",
|
|
28
|
+
"prefix": [
|
|
29
|
+
"decision"
|
|
30
|
+
],
|
|
31
|
+
"example_args": "--help"
|
|
32
|
+
},
|
|
33
|
+
"spec bind": {
|
|
34
|
+
"module": "cf_spec_context",
|
|
35
|
+
"prefix": [
|
|
36
|
+
"bind"
|
|
37
|
+
],
|
|
38
|
+
"example_args": "--help"
|
|
39
|
+
},
|
|
40
|
+
"spec validate": {
|
|
41
|
+
"module": "cf_spec_context",
|
|
42
|
+
"prefix": [
|
|
43
|
+
"validate"
|
|
44
|
+
],
|
|
45
|
+
"example_args": "--task-dir \"<需求目录>\" --json"
|
|
46
|
+
},
|
|
47
|
+
"spec status": {
|
|
48
|
+
"module": "cf_spec_context",
|
|
49
|
+
"prefix": [
|
|
50
|
+
"status"
|
|
51
|
+
],
|
|
52
|
+
"example_args": "--task-dir \"<需求目录>\" --root \"$PWD\" --json"
|
|
53
|
+
},
|
|
54
|
+
"spec doctor": {
|
|
55
|
+
"module": "cf_spec_context",
|
|
56
|
+
"prefix": [
|
|
57
|
+
"doctor"
|
|
58
|
+
],
|
|
59
|
+
"example_args": "--task-dir \"<需求目录>\" --root \"$PWD\" --json"
|
|
60
|
+
},
|
|
61
|
+
"spec refresh": {
|
|
62
|
+
"module": "cf_spec_context",
|
|
63
|
+
"prefix": [
|
|
64
|
+
"refresh"
|
|
65
|
+
],
|
|
66
|
+
"example_args": "--task-dir \"<需求目录>\" --root \"$PWD\" --json"
|
|
67
|
+
},
|
|
68
|
+
"spec refresh-missing": {
|
|
69
|
+
"module": "cf_spec_context",
|
|
70
|
+
"prefix": [
|
|
71
|
+
"refresh-missing"
|
|
72
|
+
],
|
|
73
|
+
"example_args": "--help"
|
|
74
|
+
},
|
|
75
|
+
"spec active": {
|
|
76
|
+
"module": "cf_spec_context",
|
|
77
|
+
"prefix": [
|
|
78
|
+
"active"
|
|
79
|
+
],
|
|
80
|
+
"example_args": "--help"
|
|
81
|
+
},
|
|
82
|
+
"task start": {
|
|
83
|
+
"module": "cf_spec_context",
|
|
84
|
+
"prefix": [
|
|
85
|
+
"start"
|
|
86
|
+
],
|
|
87
|
+
"example_args": "--task-dir \"<需求目录>\" --task-file \"<任务文件>\" --task TASK-001 --root \"$PWD\" --json"
|
|
88
|
+
},
|
|
89
|
+
"task finish": {
|
|
90
|
+
"module": "cf_task_workflow",
|
|
91
|
+
"prefix": [
|
|
92
|
+
"finish"
|
|
93
|
+
],
|
|
94
|
+
"example_args": "--task-dir \"<需求目录>\" --task TASK-001 --root \"$PWD\" --json"
|
|
95
|
+
},
|
|
96
|
+
"task block": {
|
|
97
|
+
"module": "cf_task_workflow",
|
|
98
|
+
"prefix": [
|
|
99
|
+
"block"
|
|
100
|
+
],
|
|
101
|
+
"example_args": "--help"
|
|
102
|
+
},
|
|
103
|
+
"task resume": {
|
|
104
|
+
"module": "cf_task_workflow",
|
|
105
|
+
"prefix": [
|
|
106
|
+
"resume"
|
|
107
|
+
],
|
|
108
|
+
"example_args": "--help"
|
|
109
|
+
},
|
|
110
|
+
"task verify": {
|
|
111
|
+
"module": "cf_task_workflow",
|
|
112
|
+
"prefix": [
|
|
113
|
+
"verify"
|
|
114
|
+
],
|
|
115
|
+
"example_args": "--task-dir \"<需求目录>\" --tasks TASK-001,TASK-002 --layers validators,e2e --profile smoke --root \"$PWD\" --json"
|
|
116
|
+
},
|
|
117
|
+
"task verify-e2e": {
|
|
118
|
+
"module": "cf_task_workflow",
|
|
119
|
+
"prefix": [
|
|
120
|
+
"verify-e2e"
|
|
121
|
+
],
|
|
122
|
+
"example_args": "--task-dir \"<需求目录>\" --root \"$PWD\" --json"
|
|
123
|
+
},
|
|
124
|
+
"task confirm-manual": {
|
|
125
|
+
"module": "cf_task_workflow",
|
|
126
|
+
"prefix": [
|
|
127
|
+
"confirm-manual"
|
|
128
|
+
],
|
|
129
|
+
"example_args": "--help"
|
|
130
|
+
},
|
|
131
|
+
"task cleanup-session": {
|
|
132
|
+
"module": "cf_task_workflow",
|
|
133
|
+
"prefix": [
|
|
134
|
+
"cleanup-session"
|
|
135
|
+
],
|
|
136
|
+
"example_args": "--help"
|
|
137
|
+
},
|
|
138
|
+
"audit": {
|
|
139
|
+
"module": "cf_audit",
|
|
140
|
+
"prefix": [],
|
|
141
|
+
"example_args": "--task-dir \"<需求目录>\" --root \"$PWD\" --json"
|
|
142
|
+
},
|
|
143
|
+
"spec gate": {
|
|
144
|
+
"module": "cf_spec_gate",
|
|
145
|
+
"prefix": [],
|
|
146
|
+
"example_args": "--help"
|
|
147
|
+
},
|
|
148
|
+
"spec session": {
|
|
149
|
+
"module": "cf_spec_session",
|
|
150
|
+
"prefix": [],
|
|
151
|
+
"example_args": "--help"
|
|
152
|
+
},
|
|
153
|
+
"task index": {
|
|
154
|
+
"module": "cf_task_index",
|
|
155
|
+
"prefix": [],
|
|
156
|
+
"example_args": "--help"
|
|
157
|
+
},
|
|
158
|
+
"task parallel": {
|
|
159
|
+
"module": "cf_task_parallel",
|
|
160
|
+
"prefix": [],
|
|
161
|
+
"example_args": "--help"
|
|
162
|
+
},
|
|
163
|
+
"acceptance manifest": {
|
|
164
|
+
"module": "cf_acceptance_manifest",
|
|
165
|
+
"prefix": [],
|
|
166
|
+
"example_args": "--task-file \"<任务文件>\" --output \"<需求目录>/.acceptance-manifest.json\""
|
|
167
|
+
},
|
|
168
|
+
"acceptance project": {
|
|
169
|
+
"module": "cf_acceptance_manifest",
|
|
170
|
+
"prefix": [
|
|
171
|
+
"--project"
|
|
172
|
+
],
|
|
173
|
+
"example_args": "--task-file \"<任务文件>\" --output \"<需求目录>/.acceptance-manifest.json\""
|
|
174
|
+
},
|
|
175
|
+
"acceptance risk": {
|
|
176
|
+
"module": "cf_acceptance_manifest",
|
|
177
|
+
"prefix": [
|
|
178
|
+
"--accept-risk"
|
|
179
|
+
],
|
|
180
|
+
"example_args": "--task-file \"<任务文件>\" --output \"<需求目录>/.acceptance-manifest.json\" --check-id CHK-... --risk-owner \"<owner>\" --risk-reason \"<reason>\" --expires-at 2026-12-31 --confirmed-by \"<user>\" --source \"<user reply>\""
|
|
181
|
+
},
|
|
182
|
+
"acceptance run": {
|
|
183
|
+
"module": "cf_acceptance_runner",
|
|
184
|
+
"prefix": [],
|
|
185
|
+
"example_args": "--manifest \"<需求目录>/.acceptance-manifest.json\" --root \"$PWD\" --write-evidence"
|
|
186
|
+
},
|
|
187
|
+
"evidence import": {
|
|
188
|
+
"module": "cf_evidence_import",
|
|
189
|
+
"prefix": [
|
|
190
|
+
"import"
|
|
191
|
+
],
|
|
192
|
+
"example_args": "--format junit --file \"<report.xml>\" --commit \"$(git rev-parse HEAD)\" --run-url \"<ci-run-url>\" --json"
|
|
193
|
+
},
|
|
194
|
+
"validate": {
|
|
195
|
+
"module": "cf_validation",
|
|
196
|
+
"prefix": [],
|
|
197
|
+
"example_args": "--root \"$PWD\" --json"
|
|
198
|
+
},
|
|
199
|
+
"stats": {
|
|
200
|
+
"module": "cf_stats",
|
|
201
|
+
"prefix": [],
|
|
202
|
+
"example_args": "--help"
|
|
203
|
+
},
|
|
204
|
+
"feedback ignore": {
|
|
205
|
+
"module": "cf_feedback",
|
|
206
|
+
"prefix": [
|
|
207
|
+
"ignore"
|
|
208
|
+
],
|
|
209
|
+
"example_args": "--help"
|
|
210
|
+
},
|
|
211
|
+
"sync check": {
|
|
212
|
+
"module": "cf_sync",
|
|
213
|
+
"prefix": [
|
|
214
|
+
"check"
|
|
215
|
+
],
|
|
216
|
+
"example_args": "--help"
|
|
217
|
+
},
|
|
218
|
+
"sync apply": {
|
|
219
|
+
"module": "cf_sync",
|
|
220
|
+
"prefix": [
|
|
221
|
+
"sync"
|
|
222
|
+
],
|
|
223
|
+
"example_args": "--help"
|
|
224
|
+
},
|
|
225
|
+
"task archive": {
|
|
226
|
+
"module": "cf_task_workflow",
|
|
227
|
+
"prefix": [
|
|
228
|
+
"archive"
|
|
229
|
+
],
|
|
230
|
+
"example_args": "--task-dir \"<需求目录>\" --root \"$PWD\" --json"
|
|
231
|
+
},
|
|
232
|
+
"scenarios parse": {
|
|
233
|
+
"module": "cf_scenarios",
|
|
234
|
+
"prefix": [
|
|
235
|
+
"parse"
|
|
236
|
+
],
|
|
237
|
+
"example_args": "--file \"<需求>.scenarios.md\" --quality --json"
|
|
238
|
+
},
|
|
239
|
+
"scenarios merge": {
|
|
240
|
+
"module": "cf_scenarios",
|
|
241
|
+
"prefix": [
|
|
242
|
+
"merge"
|
|
243
|
+
],
|
|
244
|
+
"example_args": "--file a.scenarios.md --file b.scenarios.md --json"
|
|
245
|
+
},
|
|
246
|
+
"scenarios migrate": {
|
|
247
|
+
"module": "cf_scenarios",
|
|
248
|
+
"prefix": [
|
|
249
|
+
"migrate"
|
|
250
|
+
],
|
|
251
|
+
"example_args": "--task-file \"<任务文件>\" --output \"<需求>.scenarios.md\" --json"
|
|
252
|
+
},
|
|
253
|
+
"design lint": {
|
|
254
|
+
"module": "cf_design_lint",
|
|
255
|
+
"prefix": [],
|
|
256
|
+
"example_args": "--file \"<需求>.design.md\" --scenarios-file \"<需求>.scenarios.md\" --json"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -1,294 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import argparse
|
|
7
|
-
import hashlib
|
|
8
|
-
import json
|
|
9
|
-
import shlex
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
import re
|
|
2
|
+
"""Path-frozen entry shim: implementation lives in codeflow.cli.cf_acceptance_manifest."""
|
|
12
3
|
import sys
|
|
13
|
-
from typing import IO, Callable, Mapping, Optional, Sequence
|
|
14
|
-
|
|
15
|
-
from cf_acceptance_schema import load_manifest, validate_scenarios
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
_ROW_RE = re.compile(r"^\|\s*([SEB]-\d+)\s*\|(.+?)\|$")
|
|
19
|
-
|
|
20
|
-
_MANIFEST_SCHEMA = 2
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _kind(level: str) -> str:
|
|
24
|
-
"""Map test level to execution kind.
|
|
25
|
-
|
|
26
|
-
- functional: auto-executed in Done Gate (unit, integration)
|
|
27
|
-
- e2e: deferred until explicit --verify-e2e (needs external dependencies)
|
|
28
|
-
- manual: user confirmation required, never auto-executed
|
|
29
|
-
|
|
30
|
-
层级单元格允许带注解(如 `E2E(Playwright)`、`e2e/chrome`、`manual(chrome)`),
|
|
31
|
-
取分隔符前的词元判定;避免带注解的 E2E 被误判为 functional 而每任务执行。
|
|
32
|
-
"""
|
|
33
|
-
head = level.strip().lower()
|
|
34
|
-
for separator in ("(", "(", "[", "/", ",", ",", " ", "—", "-"):
|
|
35
|
-
head = head.split(separator, 1)[0]
|
|
36
|
-
head = head.strip()
|
|
37
|
-
if head in ("manual", "手工", "手动"):
|
|
38
|
-
return "manual"
|
|
39
|
-
if head in ("e2e", "端到端", "end"):
|
|
40
|
-
return "e2e"
|
|
41
|
-
return "functional"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def _parse_command(cell: str, scenario_id: str) -> Optional[list[str]]:
|
|
45
|
-
"""Parse the optional 命令 column: argv JSON array preferred, else shell words.
|
|
46
|
-
|
|
47
|
-
Empty / `-` / `planned` → None (not yet registered, not executable).
|
|
48
|
-
"""
|
|
49
|
-
text = (cell or "").strip()
|
|
50
|
-
if not text or text in {"-", "planned", "pending", "TBD", "tbd"}:
|
|
51
|
-
return None
|
|
52
|
-
if text.startswith("["):
|
|
53
|
-
try:
|
|
54
|
-
value = json.loads(text)
|
|
55
|
-
except json.JSONDecodeError as exc:
|
|
56
|
-
raise ValueError(f"{scenario_id} 命令列不是合法 argv JSON: {exc}") from exc
|
|
57
|
-
if not isinstance(value, list) or not value or not all(isinstance(item, str) for item in value):
|
|
58
|
-
raise ValueError(f"{scenario_id} 命令列必须是 argv 字符串数组")
|
|
59
|
-
return list(value)
|
|
60
|
-
return shlex.split(text)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _parse_float(cell: str, default: float) -> float:
|
|
64
|
-
try:
|
|
65
|
-
return float((cell or "").strip() or default)
|
|
66
|
-
except (TypeError, ValueError):
|
|
67
|
-
return default
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def _manifest_hash(rows: list[dict[str, object]]) -> str:
|
|
71
|
-
immutable = [
|
|
72
|
-
{key: item[key] for key in ("id", "source", "level", "kind", "boundary", "owner", "command", "cwd", "timeout", "depends_on")}
|
|
73
|
-
for item in rows
|
|
74
|
-
]
|
|
75
|
-
payload = json.dumps(immutable, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
76
|
-
return hashlib.sha256(payload).hexdigest()
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def _legacy_kind(level: str) -> str:
|
|
80
|
-
"""v1 精确匹配:仅用于识别旧版锁定的 manifest,不做新语义判定。"""
|
|
81
|
-
normalized = level.strip().lower()
|
|
82
|
-
if normalized == "manual":
|
|
83
|
-
return "manual"
|
|
84
|
-
if normalized == "e2e":
|
|
85
|
-
return "e2e"
|
|
86
|
-
return "functional"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _extract_rows(task_file: str, kind_fn: Callable[[str], str]) -> dict[str, object]:
|
|
90
|
-
path = Path(task_file)
|
|
91
|
-
text = path.read_text(encoding="utf-8")
|
|
92
|
-
rows: list[dict[str, object]] = []
|
|
93
|
-
in_table = False
|
|
94
|
-
seen: set[str] = set()
|
|
95
|
-
for line in text.splitlines():
|
|
96
|
-
if line.startswith("## Acceptance Coverage"):
|
|
97
|
-
in_table = True
|
|
98
|
-
continue
|
|
99
|
-
if in_table and line.startswith("## "):
|
|
100
|
-
break
|
|
101
|
-
if not in_table:
|
|
102
|
-
continue
|
|
103
|
-
match = _ROW_RE.match(line)
|
|
104
|
-
if not match or set(match.group(2).replace("|", "").strip()) <= {"-", " "}:
|
|
105
|
-
continue
|
|
106
|
-
fields = [item.strip() for item in match.group(2).split("|")]
|
|
107
|
-
if match.group(1) in seen:
|
|
108
|
-
raise ValueError(f"duplicate scenario id: {match.group(1)}")
|
|
109
|
-
if len(fields) < 5:
|
|
110
|
-
raise ValueError(f"Acceptance Coverage missing columns: {match.group(1)}")
|
|
111
|
-
seen.add(match.group(1))
|
|
112
|
-
depends = [item.strip() for item in (fields[8] if len(fields) > 8 else "").split(",") if item.strip()]
|
|
113
|
-
rows.append({
|
|
114
|
-
"id": match.group(1),
|
|
115
|
-
"source": fields[0],
|
|
116
|
-
"level": fields[1],
|
|
117
|
-
"kind": kind_fn(fields[1]),
|
|
118
|
-
"boundary": fields[2],
|
|
119
|
-
"owner": fields[3],
|
|
120
|
-
"status": fields[4],
|
|
121
|
-
"command": _parse_command(fields[5], match.group(1)) if len(fields) > 5 else None,
|
|
122
|
-
"cwd": (fields[6] if len(fields) > 6 else "").strip() or ".",
|
|
123
|
-
"timeout": _parse_float(fields[7], 60.0) if len(fields) > 7 else 60.0,
|
|
124
|
-
"depends_on": depends,
|
|
125
|
-
})
|
|
126
|
-
if not rows:
|
|
127
|
-
raise ValueError("Acceptance Coverage 缺失或为空")
|
|
128
|
-
validate_scenarios(rows)
|
|
129
|
-
return {"schema": _MANIFEST_SCHEMA, "task_file": str(path), "task_sha256": _manifest_hash(rows), "scenarios": rows}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def extract_manifest(task_file: str, kind_fn: Callable[[str], str] = _kind) -> dict[str, object]:
|
|
133
|
-
return _extract_rows(task_file, kind_fn)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def write_manifest(task_file: str, output: str) -> dict[str, object]:
|
|
137
|
-
manifest = extract_manifest(task_file)
|
|
138
|
-
target = Path(output)
|
|
139
|
-
try:
|
|
140
|
-
manifest["task_file"] = str(Path(task_file).resolve().relative_to(target.parent.resolve()))
|
|
141
|
-
except ValueError:
|
|
142
|
-
manifest["task_file"] = str(Path(task_file).resolve())
|
|
143
|
-
target.parent.mkdir(parents=True, exist_ok=True)
|
|
144
|
-
target.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
145
|
-
return manifest
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
_IMMUTABLE_SCENARIO_FIELDS = (
|
|
149
|
-
"id", "source", "level", "kind", "boundary", "owner", "command", "cwd", "timeout", "depends_on",
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def _heal_legacy_kinds(manifest_file: Path, manifest: dict[str, object], expected: dict[str, object]) -> None:
|
|
154
|
-
"""旧版 `_kind` 精确匹配把带注解的 E2E 锁成 functional;任务内容未变时
|
|
155
|
-
原地升级 kind 并重锁哈希,保留 execution evidence 等运行态字段。"""
|
|
156
|
-
by_id: dict[str, dict[str, object]] = {}
|
|
157
|
-
scenarios = expected.get("scenarios")
|
|
158
|
-
if isinstance(scenarios, list):
|
|
159
|
-
for item in scenarios:
|
|
160
|
-
if isinstance(item, dict) and isinstance(item.get("id"), str):
|
|
161
|
-
by_id[item["id"]] = item
|
|
162
|
-
current = manifest.get("scenarios")
|
|
163
|
-
if isinstance(current, list):
|
|
164
|
-
for item in current:
|
|
165
|
-
if not isinstance(item, dict):
|
|
166
|
-
continue
|
|
167
|
-
target = by_id.get(item.get("id")) if isinstance(item.get("id"), str) else None
|
|
168
|
-
if target is not None:
|
|
169
|
-
item["kind"] = target.get("kind")
|
|
170
|
-
manifest["task_sha256"] = expected["task_sha256"]
|
|
171
|
-
manifest_file.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def validate_manifest(task_file: str, manifest_file: str) -> tuple[bool, str]:
|
|
175
|
-
try:
|
|
176
|
-
manifest = load_manifest(Path(manifest_file))
|
|
177
|
-
expected = extract_manifest(task_file)
|
|
178
|
-
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
|
179
|
-
return False, f"acceptance_manifest_invalid: {exc}"
|
|
180
|
-
if manifest.get("schema") != _MANIFEST_SCHEMA:
|
|
181
|
-
return False, "acceptance_manifest_drift"
|
|
182
|
-
if manifest.get("task_sha256") != expected["task_sha256"]:
|
|
183
|
-
legacy = extract_manifest(task_file, _legacy_kind)
|
|
184
|
-
if manifest.get("task_sha256") != legacy["task_sha256"]:
|
|
185
|
-
return False, "acceptance_manifest_drift"
|
|
186
|
-
_heal_legacy_kinds(Path(manifest_file), manifest, expected)
|
|
187
|
-
stored_task = manifest.get("task_file", "")
|
|
188
|
-
if not stored_task or (Path(manifest_file).parent / stored_task).resolve() != Path(task_file).resolve():
|
|
189
|
-
return False, "acceptance_manifest_task_mismatch"
|
|
190
|
-
if not isinstance(manifest.get("scenarios"), list):
|
|
191
|
-
return False, "acceptance_manifest_scenarios_changed"
|
|
192
|
-
stored = {
|
|
193
|
-
item.get("id"): item
|
|
194
|
-
for item in manifest["scenarios"]
|
|
195
|
-
if isinstance(item, Mapping) and isinstance(item.get("id"), str)
|
|
196
|
-
}
|
|
197
|
-
required = {item["id"] for item in expected["scenarios"]}
|
|
198
|
-
if set(stored) != required:
|
|
199
|
-
return False, "acceptance_manifest_scenarios_changed"
|
|
200
|
-
for item in expected["scenarios"]:
|
|
201
|
-
current = stored[item["id"]]
|
|
202
|
-
for field in _IMMUTABLE_SCENARIO_FIELDS:
|
|
203
|
-
if current.get(field) != item.get(field):
|
|
204
|
-
return False, f"acceptance_manifest_field_changed:{item['id']}:{field}"
|
|
205
|
-
text = Path(task_file).read_text(encoding="utf-8")
|
|
206
|
-
for scenario in expected["scenarios"]:
|
|
207
|
-
owner = scenario["owner"]
|
|
208
|
-
section = re.search(rf"(?ms)^##\s+{re.escape(owner)}:.*?(?=^##\s+TASK-|\Z)", text)
|
|
209
|
-
refs = re.search(r"(?m)^- \*\*Acceptance-Refs\*\*:[ \t]*([^\n]*)", section.group(0)) if section else None
|
|
210
|
-
if not refs or scenario["id"] not in re.findall(r"\b[SEB]-\d+\b", refs.group(1)):
|
|
211
|
-
return False, "acceptance_manifest_owner_missing"
|
|
212
|
-
return True, ""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def record_manual_evidence(manifest_file: str, scenario_id: str, confirmed_by: str, evidence: str) -> None:
|
|
216
|
-
if not confirmed_by or confirmed_by.lower().split(":", 1)[0] in {"agent", "assistant", "codex", "claude", "opencode", "costrict"}:
|
|
217
|
-
raise ValueError("manual evidence must be confirmed by a user")
|
|
218
|
-
path = Path(manifest_file)
|
|
219
|
-
data = load_manifest(path)
|
|
220
|
-
scenarios = data.get("scenarios")
|
|
221
|
-
if not isinstance(scenarios, list):
|
|
222
|
-
raise ValueError("invalid manifest scenarios")
|
|
223
|
-
found = False
|
|
224
|
-
owner = ""
|
|
225
|
-
task_file = data.get("task_file")
|
|
226
|
-
for item in scenarios:
|
|
227
|
-
if isinstance(item, dict) and item.get("id") == scenario_id:
|
|
228
|
-
if item.get("kind") != "manual":
|
|
229
|
-
raise ValueError("scenario is not manual")
|
|
230
|
-
item["status"] = "verified"
|
|
231
|
-
item["evidence"] = {"confirmed_by": confirmed_by, "evidence": evidence}
|
|
232
|
-
found = True
|
|
233
|
-
owner = item.get("owner") if isinstance(item.get("owner"), str) else ""
|
|
234
|
-
if not found:
|
|
235
|
-
raise ValueError("scenario not found")
|
|
236
|
-
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
237
|
-
if isinstance(task_file, str) and task_file:
|
|
238
|
-
task_path = Path(task_file)
|
|
239
|
-
if not task_path.is_absolute():
|
|
240
|
-
task_path = path.parent / task_path
|
|
241
|
-
_sync_task_evidence(task_path, scenario_id, confirmed_by, evidence, owner)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def _sync_task_evidence(task_file: Path, scenario_id: str, confirmed_by: str, evidence: str, owner: str = "") -> None:
|
|
245
|
-
from cf_acceptance_evidence import sync_task_evidence
|
|
246
|
-
sync_task_evidence(task_file, scenario_id, confirmed_by, evidence, owner)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def main(argv: Optional[Sequence[str]] = None, stdout: IO[str] = sys.stdout) -> int:
|
|
250
|
-
parser = argparse.ArgumentParser()
|
|
251
|
-
parser.add_argument("--task-file", required=True)
|
|
252
|
-
parser.add_argument("--output", default="")
|
|
253
|
-
parser.add_argument("--verify-plan", action="store_true")
|
|
254
|
-
parser.add_argument("--task-dir", default="")
|
|
255
|
-
parser.add_argument("--record-manual", action="store_true")
|
|
256
|
-
parser.add_argument("--scenario-id", default="")
|
|
257
|
-
parser.add_argument("--confirmed-by", default="")
|
|
258
|
-
parser.add_argument("--evidence", default="")
|
|
259
|
-
args = parser.parse_args(argv)
|
|
260
|
-
try:
|
|
261
|
-
if args.record_manual:
|
|
262
|
-
if not args.output or not args.scenario_id or not args.evidence:
|
|
263
|
-
raise ValueError("--record-manual requires --output, --scenario-id and --evidence")
|
|
264
|
-
record_manual_evidence(args.output, args.scenario_id, args.confirmed_by, args.evidence)
|
|
265
|
-
stdout.write(json.dumps({"ok": True, "scenario": args.scenario_id}, ensure_ascii=False))
|
|
266
|
-
return 0
|
|
267
|
-
if args.verify_plan:
|
|
268
|
-
if not args.task_dir:
|
|
269
|
-
raise ValueError("--verify-plan requires --task-dir")
|
|
270
|
-
from cf_spec_context import load_context
|
|
271
|
-
from cf_spec_gate import result_to_data, validate_plan_coverage, validate_stage
|
|
272
|
-
|
|
273
|
-
context = load_context(str(Path(args.task_dir) / "spec-context.yml"))
|
|
274
|
-
gate = validate_stage(context, "plan")
|
|
275
|
-
coverage = validate_plan_coverage(context, args.task_file)
|
|
276
|
-
result = {
|
|
277
|
-
"decision": "block" if gate.decision == "block" or coverage.decision == "block" else "pass",
|
|
278
|
-
"gate": result_to_data(gate),
|
|
279
|
-
"coverage": result_to_data(coverage),
|
|
280
|
-
}
|
|
281
|
-
stdout.write(json.dumps(result, ensure_ascii=False))
|
|
282
|
-
return 0 if result["decision"] == "pass" else 3
|
|
283
|
-
if not args.output:
|
|
284
|
-
raise ValueError("--output is required unless --verify-plan is used")
|
|
285
|
-
manifest = write_manifest(args.task_file, args.output)
|
|
286
|
-
stdout.write(json.dumps({"ok": True, "scenarios": len(manifest["scenarios"])}, ensure_ascii=False))
|
|
287
|
-
return 0
|
|
288
|
-
except (OSError, ValueError) as exc:
|
|
289
|
-
stdout.write(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False))
|
|
290
|
-
return 2
|
|
291
4
|
|
|
5
|
+
from codeflow.cli import cf_acceptance_manifest as _impl
|
|
292
6
|
|
|
293
7
|
if __name__ == "__main__":
|
|
294
|
-
raise SystemExit(main())
|
|
8
|
+
raise SystemExit(_impl.main())
|
|
9
|
+
else:
|
|
10
|
+
sys.modules[__name__] = _impl
|