opencode-plugin-flow 3.3.8 → 3.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +6 -5
- package/dist/cli.js +12 -12
- package/dist/index.js +228 -69
- package/dist/index.js.map +20 -16
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [3.3.10] - 2026-06-14
|
|
6
|
+
|
|
7
|
+
Teach Flow to fan out discovery without making execution parallel
|
|
8
|
+
|
|
9
|
+
Flow's planning and review skills already ask for strong context, but broad audits and research-heavy goals still pushed one agent through every module, risk lens, or API stream serially. That made good context expensive, and it left no Flow-native guidance for borrowing host subagents safely when the work naturally splits into independent read-only slices.
|
|
10
|
+
|
|
11
|
+
This release adds a parallel discovery reference to the planning skill. It teaches the manager agent to profile the repository first, split only independent read-only discovery slices, require structured worker handoffs, and synthesize the evidence into existing Flow fields such as `planning.research`, `planning.reviewFindings`, requirements, architecture decisions, feature targets, review scopes, and notes. The review skill can use the same pattern for very large reviews while keeping severity, deduplication, refutation, and `flow_review_record` in the main reviewer. The run skill now states the boundary explicitly: parallel discovery may inform a feature, but Flow execution remains one active feature at a time.
|
|
12
|
+
|
|
13
|
+
The release intentionally keeps the runtime small. No commands, tools, agents, state paths, persisted schemas, runtime transitions, completion gates, review policy defaults, validation strictness, package exports, public payloads, or Flow-owned subagent roles changed. The only code change is distribution sync registration so the new reference file ships and updates like the existing skill references.
|
|
14
|
+
|
|
15
|
+
Constraint: Allow broad planning and review discovery to use host-native parallel workers without making Flow's state machine parallel
|
|
16
|
+
Constraint: Preserve existing context-pack fields instead of adding worker result state or a new runtime payload
|
|
17
|
+
Rejected: Add a dedicated `/flow-discover` command or new tool | the pattern is judgment-heavy guidance, and the existing skills already own planning and review orchestration
|
|
18
|
+
Rejected: Let workers call state-changing Flow tools | the manager must remain the single owner of plan, review, execution, and session mutations
|
|
19
|
+
Confidence: high
|
|
20
|
+
Scope-risk: low
|
|
21
|
+
Reversibility: clean - the reference file, skill wording, sync registration, docs, and release metadata can be reverted without migrating sessions
|
|
22
|
+
Directive: Use parallel discovery only for independent read-only planning or review slices; synthesize worker handoffs before saving Flow state, then execute one active feature at a time
|
|
23
|
+
Tested: `bun run check` (typecheck, lint, build, full test suite, architecture seam enforcement, npm-tarball install smoke asserting the 3.3.10 README pin, bundle sanity); `bun run smoke:release` (packed candidate tarball retained under release-smoke evidence, install smoke run against that tarball, manual live OpenCode checklist generated with the retained tarball install spec); focused skill sync/config tests; `bun run lint`; `.agents/skills/flow-contribution-check/scripts/preflight.sh commit`; `.agents/skills/flow-contribution-check/scripts/preflight.sh push`
|
|
24
|
+
Not-tested: Live OpenCode UI restart against the release candidate; this release changes skill guidance, synced reference content, maintainer docs, and release metadata only.
|
|
25
|
+
|
|
26
|
+
## [3.3.9] - 2026-06-14
|
|
27
|
+
|
|
28
|
+
Make context handoff inspectable without bloating session docs
|
|
29
|
+
|
|
30
|
+
Flow's context pack had become useful enough that operators needed a direct read-only inspection surface, but the first implementation risked pushing too much product judgment and project-map detail into the always-rendered session docs. That made the feature feel heavier than the workflow backend should be.
|
|
31
|
+
|
|
32
|
+
This release adds `flow_context`, an eighth canonical runtime tool that reads the active session without mutating `.flow/**`. It exposes the derived context pack in summary, feature, or full views, including workflow profile, context quality, readiness, traceability, diagnostics, and an optional lightweight project structure map for handoff and review. The existing `flow_status` surface keeps the compact operator answer, while `flow_context` owns deeper inspection.
|
|
33
|
+
|
|
34
|
+
The release deliberately keeps the simplification boundary tight. Context quality is advisory instead of a readiness blocker, project structure mapping is on-demand through `flow_context` instead of embedded in every rendered `.flow/**/docs/context.md`, and shared path matching now drives both traceability and project-map role classification. Broad planned targets such as `.`, `./`, `*`, and `**/*` warn for specificity without falsely reporting in-scope changed artifacts as scope drift. Sensitive, ignored, absolute, and parent-relative focus paths are redacted from the project map focus summary.
|
|
35
|
+
|
|
36
|
+
No commands, state paths, persisted session schema version, runtime transition semantics, completion gates, review policy defaults, package exports, synced file ownership rules, or public mutation payloads changed. The public tool surface expands from seven to eight tools by adding one read-only projection tool, and `ignore` is added as a small runtime dependency for `.gitignore`-aware project-map filtering.
|
|
37
|
+
|
|
38
|
+
Constraint: Add deeper context inspection without turning context quality into a hard workflow gate
|
|
39
|
+
Constraint: Keep persisted session docs lightweight and source-of-truth state unchanged
|
|
40
|
+
Rejected: Revert the context work wholesale | the useful inspection and traceability improvements are worth keeping after simplification
|
|
41
|
+
Rejected: Embed the project structure map in every `docs/context.md` render | on-demand inspection avoids routine filesystem walks, snapshot churn, and unnecessary privacy surface
|
|
42
|
+
Confidence: high
|
|
43
|
+
Scope-risk: medium
|
|
44
|
+
Reversibility: clean - the read-only tool, derived projections, docs, tests, and dependency can be reverted without migrating sessions
|
|
45
|
+
Directive: Use `flow_context` for deep handoff inspection; keep `flow_status` as the compact operator readiness answer
|
|
46
|
+
Tested: `bun run check` (typecheck, lint, build, 440-test suite, architecture seam enforcement, npm-tarball install smoke asserting the 3.3.9 README pin, bundle sanity); `bun run smoke:release` (packed candidate tarball retained under release-smoke evidence, install smoke run against that tarball, manual live OpenCode checklist generated with the retained tarball install spec); `bun run report:architecture-metrics`; focused context-pack/render tests; `.agents/skills/flow-contribution-check/scripts/preflight.sh commit`; `.agents/skills/flow-contribution-check/scripts/preflight.sh push`
|
|
47
|
+
Not-tested: Live OpenCode UI restart against the release candidate; this release changes a read-only tool surface, derived projections, rendered docs, skill guidance, docs, tests, dependency metadata, and release metadata only.
|
|
48
|
+
|
|
5
49
|
## [3.3.8] - 2026-06-14
|
|
6
50
|
|
|
7
51
|
Make workflow readiness answer for scope drift
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Add Flow to the `plugin` array in your `opencode.json` (global `~/.config/openco
|
|
|
31
31
|
|
|
32
32
|
```json
|
|
33
33
|
{
|
|
34
|
-
"plugin": ["opencode-plugin-flow@3.3.
|
|
34
|
+
"plugin": ["opencode-plugin-flow@3.3.10"]
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -120,11 +120,12 @@ These five are the whole command surface since v3.1. The v2/v3.0 convenience com
|
|
|
120
120
|
|
|
121
121
|
### Tools
|
|
122
122
|
|
|
123
|
-
The plugin registers a small tool surface (
|
|
123
|
+
The plugin registers a small tool surface (8 tools). All `.flow/**` mutations go through the state-changing tools; `flow_context` is read-only:
|
|
124
124
|
|
|
125
125
|
| Tool | Purpose |
|
|
126
126
|
| --- | --- |
|
|
127
127
|
| `flow_status` | Session state, doctor-style readiness, and a computed suggested next step. |
|
|
128
|
+
| `flow_context` | Read-only context pack, quality score, traceability, and project structure map for planning/review handoff. |
|
|
128
129
|
| `flow_plan_save` | Create or update the draft plan (planning context plus features). |
|
|
129
130
|
| `flow_plan_approve` | Approve the plan, optionally restricted to a feature subset. |
|
|
130
131
|
| `flow_run_start` | Start the next runnable feature. |
|
|
@@ -132,7 +133,7 @@ The plugin registers a small tool surface (7 tools) that owns all `.flow/**` mut
|
|
|
132
133
|
| `flow_review_record` | Record a reviewer decision (`scope: feature` or `final`). |
|
|
133
134
|
| `flow_session` | Activate or close a session, list history, or show a stored session. |
|
|
134
135
|
|
|
135
|
-
These
|
|
136
|
+
These eight tools are the whole registered surface — the v2 tool-name redirect stubs that shipped in 3.0 were removed in v3.1 as scheduled. Existing v2 sessions still migrate seamlessly (the session schema is unchanged).
|
|
136
137
|
|
|
137
138
|
### Agents
|
|
138
139
|
|
|
@@ -152,7 +153,7 @@ Plus: atomic, locked, path-safe writes under `.flow/**`; schema validation of al
|
|
|
152
153
|
|
|
153
154
|
Everything judgment-shaped — how to decompose a plan, how deep to review, what counts as good evidence, when to stop and ask — lives in the skills, where you can read and override it.
|
|
154
155
|
|
|
155
|
-
`/flow-status` also reports advisory `workflowReadiness`, `contextTraceability`, and `contextDiagnostics`. These fields show whether the session is ready for planning, execution, feature review, final review, or release; which planned targets changed; which validation and review evidence exists; and where planned context is weak. They do not replace review judgment, but they make context gaps visible before they become unverifiable completion claims.
|
|
156
|
+
`/flow-status` also reports advisory `workflowReadiness`, `contextQuality`, `contextTraceability`, and `contextDiagnostics`. These fields show whether the session is ready for planning, execution, feature review, final review, or release; how strong the planned context is; which planned targets changed; which validation and review evidence exists; and where planned context is weak. `flow_context` exposes the same derived handoff in summary, feature, or full views, optionally with a lightweight project structure map. They do not replace review judgment, but they make context gaps visible before they become unverifiable completion claims.
|
|
156
157
|
|
|
157
158
|
## What Flow writes
|
|
158
159
|
|
|
@@ -161,7 +162,7 @@ Flow stores workflow state in the project/worktree where OpenCode is running:
|
|
|
161
162
|
```text
|
|
162
163
|
.flow/active/<session-id>/session.json # active session (source of truth)
|
|
163
164
|
.flow/active/<session-id>/docs/** # readable derived views
|
|
164
|
-
.flow/active/<session-id>/docs/context.md # derived context pack, readiness, traceability, and diagnostics
|
|
165
|
+
.flow/active/<session-id>/docs/context.md # derived context pack, readiness, quality, traceability, and diagnostics
|
|
165
166
|
.flow/stored/<session-id>/session.json # parked resumable sessions
|
|
166
167
|
.flow/completed/<session-id>-<timestamp>/** # closed session history
|
|
167
168
|
.flow/locks/
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{homedir as he}from"node:os";import{readdir as
|
|
3
|
-
`,E="flow-opencode-generated-skill"
|
|
4
|
-
`),
|
|
5
|
-
`);if(p(l)!==d)return{kind:"invalid_generated",reason:"hash_mismatch"};return{kind:"valid_generated",marker:{name:n,version:r,hash:d}}}var
|
|
6
|
-
`)){if(!
|
|
7
|
-
`)){let r=n.indexOf("=");if(r===-1)continue;
|
|
8
|
-
`)){let n=o.indexOf("=");if(n===-1)continue;t.set(o.slice(0,n),o.slice(n+1))}let
|
|
2
|
+
import{homedir as he}from"node:os";import{readdir as ae,readFile as ie,rm as h,rmdir as se}from"node:fs/promises";import{dirname as oe,join as c,normalize as ne,sep as re}from"node:path";var J={fast:"low",balanced:"medium",deep:"high"},j={"flow-reviewer":{mode:"all",description:"Review Flow work read-only and record a reviewer decision.",prompt:"You are the Flow reviewer. Load the `flow-review` skill, review the requested work read-only, then record your decision with flow_review_record.",reasoningEffort:J.deep,permission:{edit:"deny",bash:"deny",task:{"*":"deny"},"flow_*":"deny",flow_status:"allow",flow_review_record:"allow"}}},W={"flow-plan":{description:"Create, update, or approve a Flow plan",template:"Load the `flow-plan` skill and plan: $ARGUMENTS"},"flow-run":{description:"Run one approved Flow feature",template:"Load the `flow-run` skill and execute the next approved Flow feature. $ARGUMENTS"},"flow-auto":{description:"Drive the Flow loop autonomously until completion or a real blocker",template:"Load the `flow` skill and drive the Flow loop (status, plan, run, review) until completion or a real blocker: $ARGUMENTS"},"flow-status":{description:"Inspect the active Flow session and workspace readiness",template:"Call flow_status (detailed) and report session state, readiness checks, and the suggested next step."},"flow-review":{description:"Run a read-only Flow review with a fresh context",agent:"flow-reviewer",subtask:!0,template:"Load the `flow-review` skill and review: $ARGUMENTS"}},k=["flow-doctor","flow-history","flow-reset","flow-session"];import{createHash as P}from"node:crypto";import{join as v}from"node:path";var x=v(".config","opencode","skills"),g=v(".config","opencode","commands"),_=v(".config","opencode","agents"),w=".flow-skill-version",N="SKILL.md.backup",R=v(".config","opencode","plugins","flow.js"),A=`// Managed by flow-opencode install/uninstall
|
|
3
|
+
`,E="flow-opencode-generated-skill",K=`<!-- ${E} `,Z=new RegExp(`^<!-- ${E} name=([a-z0-9]+(?:-[a-z0-9]+)*) version=([0-9]+) hash=sha256:([a-f0-9]{64}) -->$`,"u");function p(e){return P("sha256").update(e,"utf8").digest("hex")}function m(e){let t=e.split(`
|
|
4
|
+
`),i=t.flatMap((u,H)=>u.startsWith(K)?[H]:[]);if(i.length===0)return{kind:"not_generated"};if(i.length>1)return{kind:"invalid_generated",reason:"duplicate_marker"};let a=i[0];if(a===void 0)return{kind:"not_generated"};let s=t[a];if(s===void 0)return{kind:"invalid_generated",reason:"malformed_marker"};let o=s.match(Z);if(!o)return{kind:"invalid_generated",reason:"malformed_marker"};let[,n,r,d]=o;if(n===void 0||r===void 0||d===void 0)return{kind:"invalid_generated",reason:"malformed_marker"};let l=[...t.slice(0,a),...t.slice(a+1)].join(`
|
|
5
|
+
`);if(p(l)!==d)return{kind:"invalid_generated",reason:"hash_mismatch"};return{kind:"valid_generated",marker:{name:n,version:r,hash:d}}}var z="opencode-plugin-flow",O="file=",B="=sha256:";function y(e){let t=new Map;for(let i of e.split(`
|
|
6
|
+
`)){if(!i.startsWith(O))continue;let a=i.slice(O.length),s=a.lastIndexOf(B);if(s===-1)continue;let o=a.slice(0,s),n=a.slice(s+B.length);if(o.length>0&&/^[a-f0-9]{64}$/.test(n))t.set(o,n)}return t}function b(e,t,i){let a=new Map;for(let n of e.split(`
|
|
7
|
+
`)){let r=n.indexOf("=");if(r===-1)continue;a.set(n.slice(0,r),n.slice(r+1))}let s=a.get("version"),o=a.get("hash");if(a.get("plugin")!==z||a.get("kind")!==t||a.get("name")!==i||!s||!o?.startsWith("sha256:"))return null;return{kind:t,name:i,version:s,hash:o.slice(7)}}function T(e){let t=new Map;for(let o of e.split(`
|
|
8
|
+
`)){let n=o.indexOf("=");if(n===-1)continue;t.set(o.slice(0,n),o.slice(n+1))}let i=t.get("plugin"),a=t.get("version");if(i!==z||!a)return null;let s=t.get("hash");return{plugin:i,version:a,hash:s?.startsWith("sha256:")?s.slice(7):null}}import{mkdir as He,readFile as $,rm as F,writeFile as Je}from"node:fs/promises";import{dirname as Ke,join as U,sep as Ze}from"node:path";function D(e){return`${["---",`description: ${JSON.stringify(e.description)}`,...e.agent?[`agent: ${JSON.stringify(e.agent)}`]:[],...e.subtask===void 0?[]:[`subtask: ${e.subtask}`],"---"].join(`
|
|
9
9
|
`)}
|
|
10
10
|
|
|
11
11
|
${e.template}
|
|
@@ -13,7 +13,7 @@ ${e.template}
|
|
|
13
13
|
`)}
|
|
14
14
|
|
|
15
15
|
${e.prompt}
|
|
16
|
-
`}function
|
|
16
|
+
`}function L(){return new Map(Object.entries(W).map(([e,t])=>[e,D(t)]))}function G(){return new Map(Object.entries(j).map(([e,t])=>[e,ee(t)]))}async function C(e){let t=[],i=[];for(let a of e.names){let s=U(e.root,`${a}.md`),o=U(e.root,`.${a}.flow-version`),n=await V(o),r=n===null?null:b(n,e.kind,a);if(r===null)continue;let d=await V(s);if(d!==null&&p(d)!==r.hash){i.push(s);continue}if(!e.dryRun)await F(s,{force:!0}),await F(o,{force:!0}),await F(`${s}.backup`,{force:!0});t.push(s)}return{removed:t,keptUserEdited:i}}async function V(e){try{return await $(e,"utf8")}catch(t){if(t.code==="ENOENT")return null;throw t}}function te(e){if(!e)return[];let t=["permission:"];for(let[i,a]of Object.entries(e)){if(typeof a==="string"){t.push(` ${JSON.stringify(i)}: ${JSON.stringify(a)}`);continue}if(a&&typeof a==="object"){t.push(` ${JSON.stringify(i)}:`);for(let[s,o]of Object.entries(a))t.push(` ${JSON.stringify(s)}: ${JSON.stringify(o)}`)}}return t}async function M({homeDir:e,dryRun:t=!1,logger:i}){let a={removedSkills:[],keptUserEditedSkills:[],removedCommands:[],keptUserEditedCommands:[],removedAgents:[],keptUserEditedAgents:[],removedPreNpmPlugin:null,keptForeignPreNpmPlugin:null},s=c(e,x);for(let d of await le(s)){if(d!=="flow"&&!d.startsWith("flow-"))continue;let l=c(s,d),u=await de(l);if(u==="foreign")continue;if(u==="user_edited"){a.keptUserEditedSkills.push(l),i?.(`Kept user-edited Flow skill at ${l}; remove it manually if it is no longer needed.`);continue}if(!t)await ce(l);a.removedSkills.push(l),i?.(`${t?"Would remove":"Removed"} Flow skill at ${l}.`)}let o=await C({kind:"command",root:c(e,g),names:k,dryRun:t});for(let d of o.removed)a.removedCommands.push(d),i?.(`${t?"Would remove":"Removed"} retired Flow command at ${d}.`);for(let d of o.keptUserEdited)a.keptUserEditedCommands.push(d),i?.(`Kept user-edited Flow command at ${d}; remove it manually if it is no longer needed.`);await Y({homeDir:e,dryRun:t,logger:i,kind:"command",root:c(e,g),files:L(),removed:a.removedCommands,keptUserEdited:a.keptUserEditedCommands}),await Y({homeDir:e,dryRun:t,logger:i,kind:"agent",root:c(e,_),files:G(),removed:a.removedAgents,keptUserEdited:a.keptUserEditedAgents});let n=c(e,R),r=await f(n);if(r!==null)if(r.startsWith(A)){if(!t)await h(n,{force:!0});a.removedPreNpmPlugin=n,i?.(`${t?"Would remove":"Removed"} pre-npm Flow plugin copy at ${n}.`)}else a.keptForeignPreNpmPlugin=n,i?.(`Kept ${n}: it is not managed by Flow. Remove it manually if it is a stale Flow copy.`);return i?.('Finally, remove "opencode-plugin-flow" from the plugin array in opencode.json and restart OpenCode.'),a}async function de(e){let t=c(e,"SKILL.md"),i=await f(t),a=await f(c(e,w)),s=a===null?null:T(a);if(s!==null&&a!==null){for(let[r,d]of y(a)){if(r==="SKILL.md")continue;let l=Q(e,r);if(l===null)continue;let u=await f(l);if(u!==null&&p(u)!==d)return"user_edited"}if(i===null)return"pristine";if(s.hash!==null&&p(i)===s.hash)return"pristine";return m(i).kind==="valid_generated"?"pristine":"user_edited"}if(i===null)return"foreign";let o=m(i);if(o.kind==="valid_generated")return"pristine";if(o.kind==="invalid_generated")return"user_edited";return"foreign"}function Q(e,t){let i=ne(c(e,...t.split("/")));if(i!==e&&i.startsWith(`${e}${re}`))return i;return null}async function ce(e){let t=await f(c(e,w)),i=new Set;if(t!==null)for(let a of y(t).keys()){let s=Q(e,a);if(s===null)continue;await h(s,{force:!0}),await h(`${s}.backup`,{force:!0});let o=oe(s);if(o!==e)i.add(o)}await h(c(e,"SKILL.md"),{force:!0}),await h(c(e,w),{force:!0}),await h(c(e,N),{force:!0});for(let a of[...i].sort((s,o)=>o.length-s.length))await I(a);await I(e)}async function I(e){try{await se(e)}catch(t){let i=t.code;if(i!=="ENOENT"&&i!=="ENOTEMPTY")throw t}}async function le(e){try{return(await ae(e,{withFileTypes:!0})).filter((i)=>i.isDirectory()).map((i)=>i.name).sort()}catch(t){if(t.code==="ENOENT")return[];throw t}}async function f(e){try{return await ie(e,"utf8")}catch(t){if(t.code==="ENOENT")return null;throw t}}async function Y(e){for(let[t,i]of e.files){let a=c(e.root,`${t}.md`),s=c(e.root,`.${t}.flow-version`),o=await f(a),n=await f(s),r=n===null?null:b(n,e.kind,t);if(o===null&&r===null)continue;if(!(r!==null||o===i))continue;if(o!==null&&r!==null&&p(o)!==r.hash){e.keptUserEdited.push(a),e.logger?.(`Kept user-edited Flow ${e.kind} at ${a}; remove it manually if it is no longer needed.`);continue}if(!e.dryRun)await h(a,{force:!0}),await h(s,{force:!0}),await h(`${a}.backup`,{force:!0});e.removed.push(a),e.logger?.(`${e.dryRun?"Would remove":"Removed"} Flow ${e.kind} at ${a}.`)}if(!e.dryRun)await I(e.root)}var q=`opencode-plugin-flow — Flow plugin lifecycle commands
|
|
17
17
|
|
|
18
18
|
Usage:
|
|
19
19
|
bunx opencode-plugin-flow uninstall [--dry-run]
|
|
@@ -26,10 +26,10 @@ Commands:
|
|
|
26
26
|
|
|
27
27
|
Options:
|
|
28
28
|
--dry-run Show what would be removed without deleting anything
|
|
29
|
-
--help Show this message`;function
|
|
29
|
+
--help Show this message`;function X(e){process.stdout.write(`${e}
|
|
30
30
|
`)}function S(e){process.stderr.write(`${e}
|
|
31
|
-
`)}async function ue(e){let t=[...e];if(t.length===0||t.includes("--help")||t.includes("-h"))return
|
|
31
|
+
`)}async function ue(e){let t=[...e];if(t.length===0||t.includes("--help")||t.includes("-h"))return X(q),0;let i=t.shift();if(i!=="uninstall")return S(`Unknown command: ${i}
|
|
32
32
|
|
|
33
|
-
${
|
|
33
|
+
${q}`),1;let a=!1;for(let s of t){if(s==="--dry-run"){a=!0;continue}return S(`Unknown argument: ${s}
|
|
34
34
|
|
|
35
|
-
${
|
|
35
|
+
${q}`),1}return await M({homeDir:process.env.HOME??he(),dryRun:a,logger:X}),0}try{process.exitCode=await ue(process.argv.slice(2))}catch(e){S(e instanceof Error?e.message:String(e)),process.exitCode=1}
|