dic-workflow-kit 1.0.0 → 1.1.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dic-workflow-kit",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Design-implementation consistency workflow with OpenSpec obligations, BDD knowledge, Skills, and Subagents.",
5
5
  "author": {
6
6
  "name": "TonyClaw"
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Design-Implementation Consistency Workflow Kit
2
2
 
3
- Version: `1.0.0`
3
+ Version: `1.1.0`
4
4
 
5
5
  Design-Implementation Consistency Workflow Kit, or DIC Workflow Kit, is a portable agentic workflow package for checking and repairing whether implementation matches design intent.
6
6
 
@@ -10,31 +10,76 @@ The kit is not tied to one contest, one repository, one language stack, or one a
10
10
 
11
11
  > The differentiator is not the number of agents. DIC Workflow Kit uses design intent, typed handoffs, project profiles, validation evidence, and residual-risk reporting to prove that implementation remains aligned with its design contract.
12
12
 
13
- ## Install in Codex with npm
13
+ ## Install with npm
14
14
 
15
- The npm package contains all seven Skills and all nine Subagents. Install it into the default personal Codex plugin marketplace with:
15
+ The npm package contains all seven Skills and all nine Subagents and installs them with host-native directory layouts for Codex, Claude Code, OpenCode, and MiMo Code.
16
+
17
+ Codex remains the default, so the original command is backward compatible:
16
18
 
17
19
  ```text
18
- npx dic-workflow-kit install
20
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install
19
21
  ```
20
22
 
21
- Or install the CLI globally:
23
+ Select another host explicitly:
22
24
 
23
25
  ```text
24
- npm install --global dic-workflow-kit
25
- dic-workflow-kit install
26
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host claude-code
27
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host opencode
28
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host mimo-code
29
+ ```
30
+
31
+ User scope is the default. To install into the current repository instead, use `--scope project`:
32
+
33
+ ```text
34
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host claude-code --scope project
35
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host opencode --scope project
36
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host mimo-code --scope project
37
+ ```
38
+
39
+ The host mappings are:
40
+
41
+ | Host | User scope | Project scope |
42
+ | --- | --- | --- |
43
+ | Codex | `~/.agents/plugins/plugins/dic-workflow-kit` plus marketplace registration | Not applicable to this plugin installer |
44
+ | Claude Code | `~/.claude/{skills,agents}` | `.claude/{skills,agents}` |
45
+ | OpenCode | `~/.config/opencode/{skills,agents}` | `.opencode/{skills,agents}` |
46
+ | MiMo Code | platform config root under `mimocode/{skills,agents}` | `.mimocode/{skills,agents}` |
47
+
48
+ On Windows, MiMo Code's user root resolves to `%LOCALAPPDATA%\mimocode`; `MIMOCODE_HOME` and `XDG_CONFIG_HOME` are honored where applicable. The installer adapts Subagent frontmatter per host while keeping `agents/` in the package as the single source of truth.
49
+
50
+ You can also install the CLI globally:
51
+
52
+ ```text
53
+ npm install --global @tonyclaw/dic-workflow-kit@1.1.0
54
+ dic-workflow-kit install --host opencode
26
55
  ```
27
56
 
28
- Before the package is published to the npm registry, install the same CLI directly from the repository:
57
+ The same CLI can also be installed directly from the repository:
29
58
 
30
59
  ```text
31
60
  npm install --global git+ssh://git@gitcode.com/TonyClaw/DICWorkflowKit.git
32
61
  dic-workflow-kit install
33
62
  ```
34
63
 
35
- The installer writes the plugin to `~/.agents/plugins/plugins/dic-workflow-kit` and registers it in `~/.agents/plugins/marketplace.json`. It preserves unrelated entries and replaces only the entry with the same plugin name.
64
+ Use `--dry-run` to preview the resolved destination, `--install-root <path>` for an isolated host root, or the backward-compatible Codex-only `--marketplace-root <path>`. The Codex installer preserves unrelated marketplace entries and replaces only the entry with the same plugin name. Restart or reload the target coding-agent session after its first Skills/agents directory is created.
65
+
66
+ ### Dual npm release
67
+
68
+ Every release publishes the same version and payload under both npm names:
69
+
70
+ - `@tonyclaw/dic-workflow-kit`
71
+ - `dic-workflow-kit`
72
+
73
+ `package.json` is the canonical version source. Set a new version once; the helper propagates it to the Codex plugin manifest, workflow runtime, all Skills, all Subagents, and both READMEs:
74
+
75
+ ```text
76
+ npm run release:set-version -- 1.1.0
77
+ npm run release:plan
78
+ npm run release:dual:dry-run
79
+ npm run release:dual
80
+ ```
36
81
 
37
- Use `npx dic-workflow-kit install --dry-run` to preview the destination, or `--marketplace-root /path/to/.agents/plugins` for an isolated environment. Start a new Codex task after installation so its Skills and Subagents are discovered.
82
+ npm cannot provide an atomic transaction across two package names. The coordinator therefore uses a convergent release: it completes all local checks first, publishes both packages from temporary staging directories, skips a package when the exact version already exists, and can be safely rerun to publish only the missing side after a partial failure.
38
83
 
39
84
  For a defense-oriented walkthrough of the project claims and their code evidence, see [答辩引导:项目特点与代码证据](docs/答辩引导-项目特点与代码证据.md).
40
85
 
@@ -157,7 +202,7 @@ For OpenSpec projects, the kit treats these as first-class design sources:
157
202
 
158
203
  The kit checks whether proposals, specs, scenarios, and tasks were actually reflected in implementation and tests.
159
204
 
160
- To scope intake to one OpenSpec Change, pass its identifier explicitly. Active changes are preferred; if no active match exists, the archived location is resolved and recorded in the profile. Keep evidence outside the target repository with `--output-root`:
205
+ The source repository also contains a development helper for generating these profiles directly. It is intentionally excluded from the npm runtime package. From a source checkout, scope intake to one OpenSpec Change with:
161
206
 
162
207
  ```text
163
208
  python scripts/dic_workflow.py \
package/README.zh-CN.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 设计—实现一致性 Workflow Kit
2
2
 
3
- 版本:`1.0.0`
3
+ 版本:`1.1.0`
4
4
 
5
5
  Design-Implementation Consistency Workflow Kit,简称 DIC Workflow Kit,是一套可移植的智能体工作流包,用于检查和修复“代码实现是否符合设计意图”。
6
6
 
@@ -8,29 +8,74 @@ Design-Implementation Consistency Workflow Kit,简称 DIC Workflow Kit,是
8
8
 
9
9
  > 项目的差异不在 Agent 数量。DIC Workflow Kit 通过设计意图、类型化交接、项目画像、验证证据和残余风险报告,证明实现仍然符合设计契约。
10
10
 
11
- ## 通过 npm 安装到 Codex
11
+ ## 通过 npm 安装
12
12
 
13
- npm 包完整包含 7 个 Skills 和 9 个 Subagents
13
+ npm 包完整包含 7 个 Skills 和 9 个 Subagents,并支持 Codex、Claude Code、OpenCode 和 MiMo Code 的原生目录结构。
14
+
15
+ Codex 仍是默认宿主,原命令保持兼容:
16
+
17
+ ```text
18
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install
19
+ ```
20
+
21
+ 安装到其他宿主时显式指定 `--host`:
14
22
 
15
23
  ```text
16
- npx dic-workflow-kit install
24
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host claude-code
25
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host opencode
26
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host mimo-code
17
27
  ```
18
28
 
29
+ 默认是用户级安装;若只希望当前仓库使用,则增加 `--scope project`:
30
+
31
+ ```text
32
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host claude-code --scope project
33
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host opencode --scope project
34
+ npx @tonyclaw/dic-workflow-kit@1.1.0 install --host mimo-code --scope project
35
+ ```
36
+
37
+ | 宿主 | 用户级目录 | 项目级目录 |
38
+ | --- | --- | --- |
39
+ | Codex | `~/.agents/plugins/plugins/dic-workflow-kit`,并注册 marketplace | 当前插件安装器不提供项目级模式 |
40
+ | Claude Code | `~/.claude/{skills,agents}` | `.claude/{skills,agents}` |
41
+ | OpenCode | `~/.config/opencode/{skills,agents}` | `.opencode/{skills,agents}` |
42
+ | MiMo Code | 平台配置根目录下的 `mimocode/{skills,agents}` | `.mimocode/{skills,agents}` |
43
+
44
+ Windows 上 MiMo Code 的用户级根目录为 `%LOCALAPPDATA%\mimocode`;适用时也会读取 `MIMOCODE_HOME` 和 `XDG_CONFIG_HOME`。安装器以仓库中的 `agents/` 为唯一来源,并按宿主转换 Subagent frontmatter,避免维护四套重复内容。
45
+
19
46
  也可以先全局安装 CLI:
20
47
 
21
48
  ```text
22
- npm install --global dic-workflow-kit
23
- dic-workflow-kit install
49
+ npm install --global @tonyclaw/dic-workflow-kit@1.1.0
50
+ dic-workflow-kit install --host opencode
24
51
  ```
25
52
 
26
- npm registry 正式发布前,可以直接从仓库安装同一个 CLI:
53
+ 也可以直接从仓库安装同一个 CLI:
27
54
 
28
55
  ```text
29
56
  npm install --global git+ssh://git@gitcode.com/TonyClaw/DICWorkflowKit.git
30
57
  dic-workflow-kit install
31
58
  ```
32
59
 
33
- 默认安装到 `~/.agents/plugins/plugins/dic-workflow-kit`,并在 `~/.agents/plugins/marketplace.json` 中注册。安装器会保留其他 marketplace 条目,只更新同名插件。使用 `--dry-run` 预演,或使用 `--marketplace-root` 指定隔离目录。安装后请新建 Codex 任务,以重新发现 Skills Subagents。
60
+ 使用 `--dry-run` 预演目标目录,使用 `--install-root <path>` 指定隔离的宿主根目录;Codex 仍兼容 `--marketplace-root <path>`。首次创建 Skills agents 目录后,请重启或重新加载对应 Coding Agent 会话。
61
+
62
+ ### 双 npm 包发布
63
+
64
+ 每次发布必须以相同版本、相同内容发布两个包名:
65
+
66
+ - `@tonyclaw/dic-workflow-kit`
67
+ - `dic-workflow-kit`
68
+
69
+ `package.json` 是唯一版本源。版本脚本会同步 Codex 插件清单、工作流脚本、全部 Skills、全部 Subagents 和中英文 README:
70
+
71
+ ```text
72
+ npm run release:set-version -- 1.1.0
73
+ npm run release:plan
74
+ npm run release:dual:dry-run
75
+ npm run release:dual
76
+ ```
77
+
78
+ npm 不支持两个包名之间的原子事务,因此发布协调器采用可收敛策略:先完成全部本地检查,再从临时 staging 分别发布两个包;某一侧已经存在相同版本时自动跳过,部分失败后重复运行只会补发缺失的一侧。
34
79
 
35
80
  答辩时如何讲解项目特点并定位代码证据,请参考[《答辩引导:项目特点与代码证据》](docs/答辩引导-项目特点与代码证据.md)。
36
81
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: code-repairer
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Implement approved repair slices while preserving contracts, minimizing blast radius, and avoiding unrelated refactors.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: contract-oracle
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Convert source requirements, OpenSpec scenarios, and feedback hypotheses into source-confirmed implementation obligations.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: final-reviewer
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Perform the final consistency gate across source coverage, protected paths, validation evidence, residual risks, changed files, and output artifacts.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: flow-auditor
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Audit business and use-case flow completeness, including lifecycle, branches, state transitions, side effects, integration paths, and failure handling.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: impl-inspector
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Inspect implementation against source-confirmed obligations without editing files.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: profile-builder
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Build a normalized project profile from repository evidence so downstream agents stop guessing paths, tools, and source hierarchy.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: repair-planner
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Create small, source-backed repair slices with target files, validation commands, stop conditions, and fallback plans.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spec-reader
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Read authoritative design, OpenSpec, API, schema, README, and configuration sources and produce a source-backed requirement brief.
5
5
  mode: subagent
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: validation-runner
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Discover, run, and summarize project validation commands using evaluator-readable evidence.
5
5
  mode: subagent
6
6
  ---
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import{cp as A,mkdir as w,readFile as y,readdir as g,writeFile as E}from"node:fs/promises";import{homedir as f}from"node:os";import{dirname as x,join as n,resolve as m}from"node:path";import{fileURLToPath as N}from"node:url";var h="dic-workflow-kit",p=m(x(N(import.meta.url)),".."),j=new Map([["codex","codex"],["claude","claude-code"],["claude-code","claude-code"],["opencode","opencode"],["mimo","mimo-code"],["mimocode","mimo-code"],["mimo-code","mimo-code"]]),L=[".codex-plugin","adapters","agents","schemas","skills","INSTRUCTION.md"];function u(e,o,t){let r=e[o+1];if(!r)throw new Error(`${t} requires a value`);return r}function C(e){let o=e[0]??"help",t="codex",r="user",c,a,s=process.cwd(),d=!1;for(let i=1;i<e.length;i+=1){let l=e[i];if(l==="--host"){let k=u(e,i,l).toLowerCase();if(t=j.get(k),!t)throw new Error(`unsupported host: ${k}; expected codex, claude-code, opencode, or mimo-code`);i+=1}else if(l==="--scope"){if(r=u(e,i,l).toLowerCase(),!["user","project"].includes(r))throw new Error("--scope must be user or project");i+=1}else if(l==="--install-root")c=m(u(e,i,l)),i+=1;else if(l==="--project-root")s=m(u(e,i,l)),i+=1;else if(l==="--marketplace-root")a=m(u(e,i,l)),i+=1;else if(l==="--dry-run")d=!0;else throw new Error(`unknown argument: ${l}`)}if(t!=="codex"&&a)throw new Error("--marketplace-root is only valid for --host codex");if(t==="codex"&&r==="project")throw new Error("Codex plugin installation supports user scope only");return{command:o,host:t,scope:r,installRoot:c,marketplaceRoot:a,projectRoot:s,dryRun:d}}function O({host:e,scope:o,projectRoot:t}){if(o==="project"){let r={"claude-code":".claude",opencode:".opencode","mimo-code":".mimocode"}[e];return n(t,r)}return e==="claude-code"?n(f(),".claude"):e==="opencode"?n(process.env.XDG_CONFIG_HOME||n(f(),".config"),"opencode"):e==="mimo-code"?process.env.MIMOCODE_HOME?m(process.env.MIMOCODE_HOME):process.platform==="win32"&&process.env.LOCALAPPDATA?n(process.env.LOCALAPPDATA,"mimocode"):n(process.env.XDG_CONFIG_HOME||n(f(),".config"),"mimocode"):n(f(),".agents","plugins")}async function v(e){try{let o=JSON.parse(await y(e,"utf8"));if(!o||typeof o!="object"||Array.isArray(o))throw new Error("marketplace.json must contain an object");if(!Array.isArray(o.plugins))throw new Error("marketplace.json must contain a plugins array");return o}catch(o){if(o?.code!=="ENOENT")throw o;return{name:"personal",interface:{displayName:"Personal"},plugins:[]}}}function S(){return{name:h,source:{source:"local",path:`./plugins/${h}`},policy:{installation:"AVAILABLE",authentication:"ON_INSTALL"},category:"Developer Tools"}}async function T(e){let o=e.marketplaceRoot||e.installRoot||O(e),t=n(o,"marketplace.json"),r=n(o,"plugins",h),c=await v(t),a=S(),s=c.plugins.findIndex(d=>d?.name===h);if(s>=0?c.plugins[s]=a:c.plugins.push(a),!e.dryRun){await w(r,{recursive:!0});for(let d of L)await A(n(p,d),n(r,d),{recursive:!0,force:!0});await w(o,{recursive:!0}),await E(t,`${JSON.stringify(c,null,2)}
3
+ `,"utf8")}R({...e,root:r,skillsRoot:n(r,"skills"),agentsRoot:n(r,"agents"),extra:`MARKETPLACE: ${t}`})}function I(e,o){let t=e.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);if(!t)return e;let r=o==="claude-code"?new Set(["name","description","tools","disallowedTools","model","permissionMode","skills","memory","background","isolation"]):new Set(["name","description","mode","model","temperature","top_p","tools","permission","steps","hidden","color"]);return`---
4
+ ${t[1].split(/\r?\n/).filter(a=>{let s=a.match(/^([A-Za-z][A-Za-z0-9_-]*):/)?.[1];return!s||r.has(s)}).join(`
5
+ `)}
6
+ ---
7
+ ${e.slice(t[0].length)}`}async function $(e){let o=e.installRoot||O(e),t=n(o,"skills"),r=n(o,"agents"),c=await g(n(p,"skills")),a=(await g(n(p,"agents"))).filter(s=>s.endsWith(".md"));if(!e.dryRun){await w(t,{recursive:!0});for(let s of c)await A(n(p,"skills",s),n(t,s),{recursive:!0,force:!0});await w(r,{recursive:!0});for(let s of a){let d=await y(n(p,"agents",s),"utf8");await E(n(r,s),I(d,e.host),"utf8")}}R({...e,root:o,skillsRoot:t,agentsRoot:r})}function R({host:e,scope:o,root:t,skillsRoot:r,agentsRoot:c,dryRun:a,extra:s}){process.stdout.write([a?"DRY_RUN: true":"INSTALLED: true",`HOST: ${e}`,`SCOPE: ${o}`,`ROOT: ${t}`,`SKILLS: ${r}`,`SUBAGENTS: ${c}`,s,""].filter(d=>d!==void 0).join(`
8
+ `))}async function P(e){e.host==="codex"?await T(e):await $(e)}function _(){process.stdout.write(["DIC Workflow Kit multi-host installer","","Usage:"," dic-workflow-kit install [--host <host>] [--scope user|project]"," [--project-root <path>] [--install-root <path>]"," [--marketplace-root <path>] [--dry-run]","","Hosts:"," codex (default), claude-code, opencode, mimo-code","","Examples:"," dic-workflow-kit install"," dic-workflow-kit install --host claude-code"," dic-workflow-kit install --host opencode --scope project"," dic-workflow-kit install --host mimo-code --scope project","","--marketplace-root is a backward-compatible Codex-only option.","--install-root overrides the resolved host configuration directory.",""].join(`
9
+ `))}try{let e=C(process.argv.slice(2));if(e.command==="install")await P(e);else if(e.command==="help"||e.command==="--help")_();else throw new Error(`unknown command: ${e.command}`)}catch(e){process.stderr.write(`ERROR: ${e.message}
10
+ `),process.exitCode=1}
package/package.json CHANGED
@@ -1,31 +1,26 @@
1
1
  {
2
2
  "name": "dic-workflow-kit",
3
- "version": "1.0.0",
4
- "description": "Install DIC Workflow Kit Skills and Subagents as a Codex plugin.",
3
+ "version": "1.1.0",
4
+ "description": "Install DIC Workflow Kit Skills and Subagents for Codex, Claude Code, OpenCode, and MiMo Code.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "dic-workflow-kit": "bin/dic-workflow-kit.mjs"
8
- },
9
- "scripts": {
10
- "test": "python -m unittest discover -s tests -v && node --test tests/test_npm_installer.mjs",
11
- "pack:check": "npm pack --dry-run"
7
+ "dic-workflow-kit": "dist/dic-workflow-kit.mjs"
12
8
  },
13
9
  "files": [
14
10
  ".codex-plugin/",
15
11
  "adapters/",
16
12
  "agents/",
17
- "bin/",
13
+ "dist/",
18
14
  "schemas/",
19
- "scripts/",
20
- "!scripts/__pycache__/",
21
- "!**/*.pyc",
22
15
  "skills/",
23
16
  "INSTRUCTION.md",
24
- "README.md",
25
- "README.zh-CN.md"
17
+ "README.md"
26
18
  ],
27
19
  "keywords": [
28
20
  "codex",
21
+ "claude-code",
22
+ "opencode",
23
+ "mimo-code",
29
24
  "plugin",
30
25
  "skills",
31
26
  "subagents",
@@ -39,6 +34,10 @@
39
34
  "url": "git+ssh://git@gitcode.com/TonyClaw/DICWorkflowKit.git"
40
35
  },
41
36
  "homepage": "https://gitcode.com/TonyClaw/DICWorkflowKit",
37
+ "publishConfig": {
38
+ "access": "public",
39
+ "registry": "https://registry.npmjs.org/"
40
+ },
42
41
  "engines": {
43
42
  "node": ">=20"
44
43
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: knowledge-bdd
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Derive Given-When-Then acceptance scenarios from confirmed design rules and flow contracts.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: knowledge-openspec
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: OpenSpec knowledge pack for proposals, specs, scenarios, tasks, changes, archived capabilities, validation, and design-to-implementation mapping.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: knowledge-repair-distill
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Generic repair convergence guidance, gap taxonomy, model steering hints, and validation gates for design-implementation consistency work.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: workflow-core
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Core Design-Implementation Consistency workflow contract, evidence model, handoff rules, and final output expectations.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: workflow-intake
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Discover authoritative design sources, implementation roots, test roots, protected paths, validation commands, and adapter hints.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: workflow-profile
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Build a normalized project profile for design-implementation consistency work without hardcoding a repository or technology stack.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: workflow-repair
3
- version: 1.0.0
3
+ version: 1.1.0
4
4
  description: Guide source-backed implementation inspection, repair planning, code repair, validation loops, and final consistency evidence.
5
5
  ---
6
6
 
@@ -1,150 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
4
- import { homedir } from "node:os";
5
- import { dirname, join, resolve } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
-
8
- const PLUGIN_NAME = "dic-workflow-kit";
9
- const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
10
- const PAYLOAD = [
11
- ".codex-plugin",
12
- "adapters",
13
- "agents",
14
- "schemas",
15
- "scripts",
16
- "skills",
17
- "INSTRUCTION.md",
18
- "README.md",
19
- "README.zh-CN.md",
20
- ];
21
-
22
- function parseArgs(argv) {
23
- const command = argv[0] ?? "help";
24
- let marketplaceRoot = join(homedir(), ".agents", "plugins");
25
- let dryRun = false;
26
- for (let index = 1; index < argv.length; index += 1) {
27
- const value = argv[index];
28
- if (value === "--marketplace-root") {
29
- const next = argv[index + 1];
30
- if (!next) {
31
- throw new Error("--marketplace-root requires a directory");
32
- }
33
- marketplaceRoot = resolve(next);
34
- index += 1;
35
- } else if (value === "--dry-run") {
36
- dryRun = true;
37
- } else {
38
- throw new Error(`unknown argument: ${value}`);
39
- }
40
- }
41
- return { command, marketplaceRoot: resolve(marketplaceRoot), dryRun };
42
- }
43
-
44
- async function loadMarketplace(path) {
45
- try {
46
- const value = JSON.parse(await readFile(path, "utf8"));
47
- if (!value || typeof value !== "object" || Array.isArray(value)) {
48
- throw new Error("marketplace.json must contain an object");
49
- }
50
- if (!Array.isArray(value.plugins)) {
51
- throw new Error("marketplace.json must contain a plugins array");
52
- }
53
- return value;
54
- } catch (error) {
55
- if (error?.code !== "ENOENT") {
56
- throw error;
57
- }
58
- return {
59
- name: "personal",
60
- interface: { displayName: "Personal" },
61
- plugins: [],
62
- };
63
- }
64
- }
65
-
66
- function pluginEntry() {
67
- return {
68
- name: PLUGIN_NAME,
69
- source: {
70
- source: "local",
71
- path: `./plugins/${PLUGIN_NAME}`,
72
- },
73
- policy: {
74
- installation: "AVAILABLE",
75
- authentication: "ON_INSTALL",
76
- },
77
- category: "Developer Tools",
78
- };
79
- }
80
-
81
- async function install({ marketplaceRoot, dryRun }) {
82
- const marketplacePath = join(marketplaceRoot, "marketplace.json");
83
- const pluginRoot = join(marketplaceRoot, "plugins", PLUGIN_NAME);
84
- const marketplace = await loadMarketplace(marketplacePath);
85
- const entry = pluginEntry();
86
- const existingIndex = marketplace.plugins.findIndex(
87
- (candidate) => candidate?.name === PLUGIN_NAME,
88
- );
89
- if (existingIndex >= 0) {
90
- marketplace.plugins[existingIndex] = entry;
91
- } else {
92
- marketplace.plugins.push(entry);
93
- }
94
-
95
- if (!dryRun) {
96
- await mkdir(pluginRoot, { recursive: true });
97
- for (const relativePath of PAYLOAD) {
98
- await cp(join(PACKAGE_ROOT, relativePath), join(pluginRoot, relativePath), {
99
- recursive: true,
100
- force: true,
101
- });
102
- }
103
- await mkdir(marketplaceRoot, { recursive: true });
104
- await writeFile(
105
- marketplacePath,
106
- `${JSON.stringify(marketplace, null, 2)}\n`,
107
- "utf8",
108
- );
109
- }
110
-
111
- process.stdout.write(
112
- [
113
- dryRun ? "DRY_RUN: true" : "INSTALLED: true",
114
- `PLUGIN_ROOT: ${pluginRoot}`,
115
- `MARKETPLACE: ${marketplacePath}`,
116
- "SKILLS: skills/",
117
- "SUBAGENTS: agents/",
118
- "",
119
- ].join("\n"),
120
- );
121
- }
122
-
123
- function printHelp() {
124
- process.stdout.write(
125
- [
126
- "DIC Workflow Kit Codex installer",
127
- "",
128
- "Usage:",
129
- " dic-workflow-kit install [--marketplace-root <path>] [--dry-run]",
130
- "",
131
- "Default destination:",
132
- " ~/.agents/plugins/plugins/dic-workflow-kit",
133
- "",
134
- ].join("\n"),
135
- );
136
- }
137
-
138
- try {
139
- const options = parseArgs(process.argv.slice(2));
140
- if (options.command === "install") {
141
- await install(options);
142
- } else if (options.command === "help" || options.command === "--help") {
143
- printHelp();
144
- } else {
145
- throw new Error(`unknown command: ${options.command}`);
146
- }
147
- } catch (error) {
148
- process.stderr.write(`ERROR: ${error.message}\n`);
149
- process.exitCode = 1;
150
- }
@@ -1,507 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Portable helper skeleton for DIC Workflow Kit.
3
-
4
- This script intentionally uses only the Python standard library.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- import argparse
10
- import hashlib
11
- import json
12
- import os
13
- import re
14
- import shlex
15
- from pathlib import Path
16
- from typing import Any
17
-
18
-
19
- VERSION = "1.0.0"
20
-
21
-
22
- def write_text(path: Path, text: str) -> None:
23
- path.parent.mkdir(parents=True, exist_ok=True)
24
- path.write_text(text, encoding="utf-8", newline="\n")
25
-
26
-
27
- def find_workflow_root(start: Path) -> Path:
28
- current = start.resolve()
29
- if current.is_file():
30
- current = current.parent
31
- for candidate in [current, *current.parents]:
32
- if (candidate / "INSTRUCTION.md").exists():
33
- return candidate
34
- return current
35
-
36
-
37
- def rel(path: Path, root: Path) -> str:
38
- try:
39
- return path.resolve().relative_to(root.resolve()).as_posix()
40
- except ValueError:
41
- return path.as_posix()
42
-
43
-
44
- def extract_validation_commands(text: str, source: str) -> list[dict[str, Any]]:
45
- commands = []
46
- seen = set()
47
- for value in re.findall(r"`([^`\r\n]+)`", text):
48
- if not re.match(
49
- r"^(?:npm\s+(?:run|test|exec)\b|npx\s+|pnpm\s+|yarn\s+|"
50
- r"openspec\s+validate\b|pytest(?:\s|$)|python\s+|mvn\s+)",
51
- value,
52
- ):
53
- continue
54
- try:
55
- command = shlex.split(value)
56
- except ValueError:
57
- continue
58
- key = tuple(command)
59
- if key in seen:
60
- continue
61
- seen.add(key)
62
- commands.append(
63
- {
64
- "name": value,
65
- "command": command,
66
- "cwd": ".",
67
- "source": source,
68
- "kind": "suggested",
69
- }
70
- )
71
- return commands
72
-
73
-
74
- def obligation_id(capability_id: str, kind: str, title: str) -> str:
75
- digest = hashlib.sha256(
76
- f"{capability_id}\0{kind}\0{title}".encode("utf-8")
77
- ).hexdigest()[:12]
78
- return f"obl-{digest}"
79
-
80
-
81
- def extract_openspec_obligations(
82
- spec_path: Path,
83
- project_root: Path,
84
- capability_id: str,
85
- authority: str,
86
- ) -> list[dict[str, Any]]:
87
- lines = spec_path.read_text(encoding="utf-8").splitlines()
88
- obligations = []
89
- current_requirement = None
90
- index = 0
91
- while index < len(lines):
92
- line = lines[index]
93
- requirement_match = re.match(r"^###\s+Requirement:\s*(.+?)\s*$", line)
94
- scenario_match = re.match(r"^####\s+Scenario:\s*(.+?)\s*$", line)
95
- if requirement_match:
96
- title = requirement_match.group(1)
97
- statement_lines = []
98
- cursor = index + 1
99
- while cursor < len(lines) and not re.match(
100
- r"^#{3,4}\s+(?:Requirement|Scenario):", lines[cursor]
101
- ):
102
- value = lines[cursor].strip()
103
- if value and not value.startswith("#"):
104
- statement_lines.append(value)
105
- cursor += 1
106
- current_requirement = obligation_id(capability_id, "requirement", title)
107
- obligations.append(
108
- {
109
- "id": current_requirement,
110
- "capabilityId": capability_id,
111
- "kind": "requirement",
112
- "title": title,
113
- "statement": " ".join(statement_lines),
114
- "source": rel(spec_path, project_root),
115
- "line": index + 1,
116
- "authority": authority,
117
- "parentId": None,
118
- "acceptanceClauses": [],
119
- }
120
- )
121
- elif scenario_match:
122
- title = scenario_match.group(1)
123
- clauses = []
124
- cursor = index + 1
125
- while cursor < len(lines) and not re.match(
126
- r"^#{3,4}\s+(?:Requirement|Scenario):", lines[cursor]
127
- ):
128
- clause_match = re.match(
129
- r"^\s*-\s+\*\*(GIVEN|WHEN|THEN|AND|BUT)\*\*\s*(.+?)\s*$",
130
- lines[cursor],
131
- flags=re.IGNORECASE,
132
- )
133
- if clause_match:
134
- clauses.append(
135
- {
136
- "keyword": clause_match.group(1).upper(),
137
- "text": clause_match.group(2),
138
- "line": cursor + 1,
139
- }
140
- )
141
- cursor += 1
142
- obligations.append(
143
- {
144
- "id": obligation_id(capability_id, "scenario", title),
145
- "capabilityId": capability_id,
146
- "kind": "scenario",
147
- "title": title,
148
- "statement": " ".join(
149
- f"{item['keyword']} {item['text']}" for item in clauses
150
- ),
151
- "source": rel(spec_path, project_root),
152
- "line": index + 1,
153
- "authority": authority,
154
- "parentId": current_requirement,
155
- "acceptanceClauses": clauses,
156
- }
157
- )
158
- index += 1
159
- return obligations
160
-
161
-
162
- def build_obligation_catalog(
163
- project_root: Path, design_entry: dict[str, Any]
164
- ) -> dict[str, Any]:
165
- use_baseline = design_entry["lifecycle"] == "archived" and design_entry["baselineSpecs"]
166
- selected_specs = (
167
- design_entry["baselineSpecs"] if use_baseline else design_entry["deltaSpecs"]
168
- )
169
- authority = "current-baseline" if use_baseline else "active-change"
170
- obligations = []
171
- for spec in selected_specs:
172
- spec_path = project_root / spec
173
- capability_id = spec_path.parent.name
174
- obligations.extend(
175
- extract_openspec_obligations(
176
- spec_path, project_root, capability_id, authority
177
- )
178
- )
179
- return {
180
- "schema": "dic.contract-obligations.v1",
181
- "version": VERSION,
182
- "changeId": design_entry["changeId"],
183
- "changeLifecycle": design_entry["lifecycle"],
184
- "authorityPolicy": (
185
- "Archived changes use promoted current baseline specs."
186
- if use_baseline
187
- else "Active changes use their delta specs."
188
- ),
189
- "sources": selected_specs,
190
- "counts": {
191
- "requirements": sum(item["kind"] == "requirement" for item in obligations),
192
- "scenarios": sum(item["kind"] == "scenario" for item in obligations),
193
- "total": len(obligations),
194
- },
195
- "obligations": obligations,
196
- }
197
-
198
-
199
- def render_obligation_catalog(catalog: dict[str, Any]) -> str:
200
- lines = [
201
- "# Contract Obligations",
202
- "",
203
- f"- Change: `{catalog['changeId']}`",
204
- f"- Lifecycle: {catalog['changeLifecycle']}",
205
- f"- Authority: {catalog['authorityPolicy']}",
206
- f"- Requirements: {catalog['counts']['requirements']}",
207
- f"- Scenarios: {catalog['counts']['scenarios']}",
208
- "",
209
- ]
210
- for item in catalog["obligations"]:
211
- lines.extend(
212
- [
213
- f"## {item['id']} — {item['title']}",
214
- "",
215
- f"- Kind: {item['kind']}",
216
- f"- Source: `{item['source']}:{item['line']}`",
217
- f"- Authority: {item['authority']}",
218
- ]
219
- )
220
- if item["parentId"]:
221
- lines.append(f"- Parent: `{item['parentId']}`")
222
- lines.extend(["", item["statement"] or "_No normative body extracted._", ""])
223
- return "\n".join(lines)
224
-
225
-
226
- def resolve_design_map(project_root: Path, capability_ids: list[str]) -> dict[str, Any] | None:
227
- map_path = project_root / "openspec" / "designs" / "spec-to-design-map.md"
228
- if not map_path.exists():
229
- return None
230
- text = map_path.read_text(encoding="utf-8")
231
- for line in text.splitlines():
232
- if not line.lstrip().startswith("|"):
233
- continue
234
- cells = [cell.strip() for cell in line.strip().strip("|").split("|")]
235
- if len(cells) < 5:
236
- continue
237
- capability = cells[0].strip("` ")
238
- if capability not in capability_ids:
239
- continue
240
- linked_paths = []
241
- for cell in cells[1:4]:
242
- linked_paths.extend(re.findall(r"`(openspec/[^`]+\.md)`", cell))
243
- linked_paths = list(dict.fromkeys(linked_paths))
244
- return {
245
- "source": rel(map_path, project_root),
246
- "capabilityId": capability,
247
- "architectureDesigns": [
248
- path for path in linked_paths if "/designs/architecture/" in path
249
- ],
250
- "moduleDesigns": [
251
- path for path in linked_paths if "/designs/modules/" in path
252
- ],
253
- "adrs": [path for path in linked_paths if "/designs/adr/" in path],
254
- "validationSummary": cells[4],
255
- "validationCommands": extract_validation_commands(cells[4], rel(map_path, project_root)),
256
- }
257
- return None
258
-
259
-
260
- def resolve_openspec_change(project_root: Path, change_id: str) -> dict[str, Any]:
261
- candidates = [
262
- ("active", project_root / "openspec" / "changes" / change_id),
263
- ("archived", project_root / "openspec" / "changes" / "archive" / change_id),
264
- ]
265
- for lifecycle, change_root in candidates:
266
- if not change_root.is_dir():
267
- continue
268
- delta_specs = sorted(change_root.glob("specs/**/spec.md"))
269
- capability_ids = [
270
- path.relative_to(change_root / "specs").parts[0] for path in delta_specs
271
- ]
272
- baseline_specs = []
273
- for delta_spec in delta_specs:
274
- relative_spec = delta_spec.relative_to(change_root / "specs")
275
- baseline = project_root / "openspec" / "specs" / relative_spec
276
- if baseline.exists():
277
- baseline_specs.append(baseline)
278
- documents = [
279
- path
280
- for path in [
281
- change_root / "proposal.md",
282
- change_root / "design.md",
283
- change_root / "tasks.md",
284
- ]
285
- if path.exists()
286
- ]
287
- result = {
288
- "type": "openspec-change",
289
- "changeId": change_id,
290
- "lifecycle": lifecycle,
291
- "changeRoot": rel(change_root, project_root),
292
- "documents": [rel(path, project_root) for path in documents],
293
- "deltaSpecs": [rel(path, project_root) for path in delta_specs],
294
- "baselineSpecs": [rel(path, project_root) for path in baseline_specs],
295
- }
296
- design_map = resolve_design_map(project_root, capability_ids)
297
- if design_map:
298
- result["designMap"] = design_map
299
- return result
300
- raise SystemExit(
301
- f"OpenSpec change not found: {change_id} "
302
- f"(checked active and archived changes under {project_root})"
303
- )
304
-
305
-
306
- def detect_project(root: Path, change_id: str = "") -> dict[str, Any]:
307
- project_root = root
308
- openspec = list(project_root.glob("**/openspec/specs/**/spec.md")) + list(
309
- project_root.glob("**/openspec/changes/**/proposal.md")
310
- )
311
- design_entry = resolve_openspec_change(project_root, change_id) if change_id else None
312
- if design_entry:
313
- selected = (
314
- design_entry["documents"]
315
- + design_entry["deltaSpecs"]
316
- + design_entry["baselineSpecs"]
317
- )
318
- design_map = design_entry.get("designMap")
319
- if design_map:
320
- selected += [
321
- design_map["source"],
322
- *design_map["architectureDesigns"],
323
- *design_map["moduleDesigns"],
324
- *design_map["adrs"],
325
- ]
326
- design_sources = [project_root / path for path in selected]
327
- else:
328
- design_sources = []
329
- for pattern in ["README*", "docs/**/*.md", "design/**/*.md", "spec/**/*.md", "api/**/*.md", "schemas/**/*"]:
330
- design_sources.extend(project_root.glob(pattern))
331
- for path in openspec:
332
- if path not in design_sources:
333
- design_sources.append(path)
334
-
335
- impl_roots = [p for p in [project_root / "src", project_root / "app", project_root / "code"] if p.exists()]
336
- test_roots = [p for p in [project_root / "test", project_root / "tests", project_root / "test-cases"] if p.exists()]
337
- project_types = []
338
- adapters = ["generic"]
339
- if openspec:
340
- project_types.append("openspec")
341
- adapters.append("openspec")
342
- if (project_root / "pom.xml").exists() or list(project_root.glob("**/pom.xml")):
343
- project_types.append("maven")
344
- if (project_root / "package.json").exists() or list(project_root.glob("**/package.json")):
345
- project_types.append("node")
346
-
347
- validation_commands = []
348
- if design_entry:
349
- for document in design_entry["documents"]:
350
- path = project_root / document
351
- validation_commands.extend(
352
- extract_validation_commands(path.read_text(encoding="utf-8"), document)
353
- )
354
- if design_entry.get("designMap"):
355
- validation_commands.extend(design_entry["designMap"]["validationCommands"])
356
- if design_entry["lifecycle"] == "archived":
357
- validation_commands = [
358
- item
359
- for item in validation_commands
360
- if not (
361
- item["command"][:2] == ["openspec", "validate"]
362
- and "--all" not in item["command"]
363
- )
364
- ]
365
- unique_commands = {}
366
- for item in validation_commands:
367
- unique_commands.setdefault(tuple(item["command"]), item)
368
- validation_commands = list(unique_commands.values())
369
-
370
- profile = {
371
- "schema": "dic.project-profile.v1",
372
- "version": VERSION,
373
- "projectRoot": project_root.as_posix(),
374
- "projectTypes": sorted(set(project_types)) or ["generic"],
375
- "adapters": adapters,
376
- "designSources": [rel(p, project_root) for p in sorted(set(design_sources))],
377
- "implementationRoots": [rel(p, project_root) for p in impl_roots],
378
- "testRoots": [rel(p, project_root) for p in test_roots],
379
- "protectedPaths": [rel(p, project_root) for p in sorted(set(design_sources))],
380
- "validationCommands": validation_commands,
381
- "riskFamilies": ["requirements", "api-contract", "flow-completeness", "validation"],
382
- "outputContract": {
383
- "finalResultJson": "reports/FINAL_RESULT.json",
384
- "finalOutputMd": "result/output.md",
385
- "contractObligationsJson": "reports/contract-obligations.json",
386
- "contractObligationsMd": "reports/contract-obligations.md",
387
- "traceDir": "logs/trace"
388
- }
389
- }
390
- if design_entry:
391
- profile["designEntry"] = design_entry
392
- profile["deltaSpecs"] = design_entry["deltaSpecs"]
393
- profile["baselineSpecs"] = design_entry["baselineSpecs"]
394
- return profile
395
-
396
-
397
- def command_init(args: argparse.Namespace) -> int:
398
- root = Path(args.root or os.getcwd()).resolve()
399
- output_root = Path(args.output_root).resolve() if args.output_root else root
400
- for directory in ["reports", "result", "logs/trace"]:
401
- (output_root / directory).mkdir(parents=True, exist_ok=True)
402
- profile = detect_project(root, args.change)
403
- profile_path = output_root / "reports" / "project-profile.json"
404
- write_text(profile_path, json.dumps(profile, indent=2, ensure_ascii=False))
405
- if profile.get("designEntry"):
406
- catalog = build_obligation_catalog(root, profile["designEntry"])
407
- write_text(
408
- output_root / "reports" / "contract-obligations.json",
409
- json.dumps(catalog, indent=2, ensure_ascii=False),
410
- )
411
- write_text(
412
- output_root / "reports" / "contract-obligations.md",
413
- render_obligation_catalog(catalog),
414
- )
415
- output = [
416
- "# DIC Workflow Init",
417
- "",
418
- f"- Version: {VERSION}",
419
- f"- Project root: `{root.as_posix()}`",
420
- f"- Output root: `{output_root.as_posix()}`",
421
- f"- Project profile: `{profile_path.as_posix()}`",
422
- f"- Adapters: {', '.join(profile['adapters'])}",
423
- ]
424
- if profile.get("designEntry"):
425
- entry = profile["designEntry"]
426
- output.extend(
427
- [
428
- f"- Design entry: OpenSpec Change `{entry['changeId']}`",
429
- f"- Change lifecycle: {entry['lifecycle']}",
430
- ]
431
- )
432
- output.append("")
433
- write_text(output_root / "logs" / "trace" / "init.md", "\n".join(output))
434
- print(f"DIC_WORKFLOW_VERSION: {VERSION}")
435
- print(f"PROJECT_ROOT: {root}")
436
- print(f"OUTPUT_ROOT: {output_root}")
437
- print(f"PROJECT_PROFILE: {profile_path}")
438
- return 0
439
-
440
-
441
- def command_finalize(args: argparse.Namespace) -> int:
442
- root = find_workflow_root(Path(args.root or os.getcwd()))
443
- status = args.status.upper()
444
- if status not in {"PASS", "PARTIAL", "BLOCKED", "FAIL"}:
445
- raise SystemExit(f"invalid status: {args.status}")
446
- result = {
447
- "schema": "dic.final-result.v1",
448
- "status": status,
449
- "final": True,
450
- "summary": args.summary,
451
- "artifacts": [
452
- "reports/project-profile.json",
453
- "reports/contract-obligations.json",
454
- "reports/contract-obligations.md",
455
- "reports/final-consistency-report.md",
456
- "reports/FINAL_RESULT.json",
457
- "result/output.md",
458
- "logs/trace/"
459
- ],
460
- "validation": [],
461
- "changedFiles": [],
462
- "residualRisks": []
463
- }
464
- write_text(root / "reports" / "FINAL_RESULT.json", json.dumps(result, indent=2, ensure_ascii=False))
465
- lines = [
466
- "# DIC Final Result",
467
- "",
468
- f"FINAL_EXECUTION_RESULT: {status}",
469
- "FINAL_RESULT_FINAL: true",
470
- "",
471
- args.summary,
472
- "",
473
- "## Artifacts",
474
- "",
475
- "- `reports/FINAL_RESULT.json`",
476
- "- `reports/contract-obligations.json`",
477
- "- `reports/contract-obligations.md`",
478
- "- `result/output.md`",
479
- "- `logs/trace/`",
480
- "",
481
- ]
482
- write_text(root / "result" / "output.md", "\n".join(lines))
483
- print(f"FINAL_EXECUTION_RESULT: {status}")
484
- return 0
485
-
486
-
487
- def main() -> int:
488
- parser = argparse.ArgumentParser(prog="dic_workflow")
489
- parser.add_argument("--root", default="")
490
- parser.add_argument("--output-root", default="")
491
- sub = parser.add_subparsers(dest="command", required=True)
492
-
493
- p = sub.add_parser("init")
494
- p.add_argument("--change", default="")
495
- p.set_defaults(func=command_init)
496
-
497
- p = sub.add_parser("finalize")
498
- p.add_argument("--status", default="PARTIAL")
499
- p.add_argument("--summary", default="DIC workflow finished with recorded evidence.")
500
- p.set_defaults(func=command_finalize)
501
-
502
- args = parser.parse_args()
503
- return int(args.func(args))
504
-
505
-
506
- if __name__ == "__main__":
507
- raise SystemExit(main())