pi-ultracode 0.10.3 → 0.10.4

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 CHANGED
@@ -57,18 +57,18 @@ Pi decides whether a workflow is useful. Small tasks can still use the normal si
57
57
 
58
58
  Useful commands:
59
59
 
60
- | Command | Action |
61
- | --- | --- |
62
- | `/ultracode` | Enable `auto` from off; disable any active mode |
63
- | `/ultracode auto` | Let the parent choose depth using the task and context |
64
- | `/ultracode focused` | Use fixed depth for bounded work with direct local verification |
65
- | `/ultracode standard` | Use fixed depth for independent dimensions or competing hypotheses, with targeted verification |
66
- | `/ultracode deep` | Use fixed depth for interacting constraints or critical invariants, with independent evidence and counterexample checks |
67
- | `/ultracode off` | Disable it without changing the parent effort |
68
- | `/ultracode status` | Show the configured mode and its applicability / verification requirements |
69
- | `/workflows` or `F6` | Open the workflow browser |
70
- | `/workflows <runId>` | Open a specific run |
71
- | `/workflows abort` | Abort active runs |
60
+ | Command | Action |
61
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
62
+ | `/ultracode` | Enable `auto` from off; disable any active mode |
63
+ | `/ultracode auto` | Let the parent choose depth using the task and context |
64
+ | `/ultracode focused` | Use fixed depth for bounded work with direct local verification |
65
+ | `/ultracode standard` | Use fixed depth for independent dimensions or competing hypotheses, with targeted verification |
66
+ | `/ultracode deep` | Use fixed depth for interacting constraints or critical invariants, with independent evidence and counterexample checks |
67
+ | `/ultracode off` | Disable it without changing the parent effort |
68
+ | `/ultracode status` | Show the configured mode and its applicability / verification requirements |
69
+ | `/workflows` or `F6` | Open the workflow browser |
70
+ | `/workflows <runId>` | Open a specific run |
71
+ | `/workflows abort` | Abort active runs |
72
72
 
73
73
  Press `Esc` to cancel a running workflow. In Pi's fullscreen TUI, use `Ctrl+PageUp`, `Ctrl+PageDown`, and `Ctrl+End` inside workflow details.
74
74
 
@@ -76,12 +76,12 @@ Press `Esc` to cancel a running workflow. In Pi's fullscreen TUI, use `Ctrl+Page
76
76
 
77
77
  Depth defines **what must be investigated and verified**, not model effort or a quota of agents and rounds:
78
78
 
79
- | Depth | Applicable conditions | Required verification |
80
- |---|---|---|
81
- | **Focused** | Bounded work with a known approach and locally verifiable results; e.g. a specified validation rule | Directly check the changed behavior or key claim, usually in the parent loop. No broad investigation or adversarial panel by default; verification is not skipped. |
82
- | **Standard** | Multiple independent dimensions or unresolved hypotheses can materially change the answer; e.g. a fault with several plausible causes | Investigate those dimensions or compare causes, then target disputed or weakly evidenced claims. Known multi-step work alone is not enough to require standard. |
83
- | **Deep** | Tightly interacting hard constraints or critical invariants need independent evidence and counterexamples; e.g. cancellation racing a durable commit | Trace interactions, test boundary/failure cases, and challenge key claims with a distinct test, reproduction, proof, or source. A second agent is neither necessary nor sufficient. |
84
- | **Auto** | Choose the smallest depth sufficient for the current task | Apply the selected depth's evidence requirements; escalate only when material new evidence or unresolved conflicts justify it. |
79
+ | Depth | Applicable conditions | Required verification |
80
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
81
+ | **Focused** | Bounded work with a known approach and locally verifiable results; e.g. a specified validation rule | Directly check the changed behavior or key claim, usually in the parent loop. No broad investigation or adversarial panel by default; verification is not skipped. |
82
+ | **Standard** | Multiple independent dimensions or unresolved hypotheses can materially change the answer; e.g. a fault with several plausible causes | Investigate those dimensions or compare causes, then target disputed or weakly evidenced claims. Known multi-step work alone is not enough to require standard. |
83
+ | **Deep** | Tightly interacting hard constraints or critical invariants need independent evidence and counterexamples; e.g. cancellation racing a durable commit | Trace interactions, test boundary/failure cases, and challenge key claims with a distinct test, reproduction, proof, or source. A second agent is neither necessary nor sufficient. |
84
+ | **Auto** | Choose the smallest depth sufficient for the current task | Apply the selected depth's evidence requirements; escalate only when material new evidence or unresolved conflicts justify it. |
85
85
 
86
86
  Risk determines **which claims need verification**. Security, concurrency, data loss, file count, or a request for review alone never mandates `deep`; missing information does not prove hidden complexity. Deep does not automatically mean a workflow, more agents, or repeated rounds. Explicit `focused` / `standard` / `deep` modes remain fixed: if insufficient, report the uncertainty and recommend a deeper mode rather than silently exceeding the boundary.
87
87
 
@@ -105,15 +105,15 @@ Before each parent run, the extension supplies a local Pi capability snapshot: t
105
105
 
106
106
  The snapshot describes local registered capabilities, not a live provider probe, and may become stale during a run. Runtime clamping remains a safety net for explicit requests or stale metadata. Standalone SDK use of `createWorkflowTool()` without the extension does not inject this section; the host must supply capability context to enable informed automatic suffix selection. Neither the snapshot nor its absence rewrites explicit user effort choices or no-suffix defaults.
107
107
 
108
- | Suffix | Criteria and exclusions | Example |
109
- |---|---|---|
110
- | `:off` | Fully specified mechanical work; no locating changes, interpreting meaning, or hidden-impact reasoning | Fix a typo at an exact location using a supplied replacement |
111
- | `:minimal` | One clear local judgment; no cross-file tracing or multi-step implementation | Explain a short self-contained expression |
112
- | `:low` | Small implementation or question with a known approach and straightforward checks; not an unknown-root-cause fault | Add a specified validation rule and its test |
113
- | `:medium` | Bounded multi-step work coordinating known constraints; not interacting unresolved hypotheses | Wire a configuration option through loading, usage, and tests |
114
- | `:high` | Test competing hypotheses, trace cross-module effects, or reason about critical correctness constraints; security/architecture keywords alone do not qualify | Diagnose cross-module state desynchronization by comparing plausible causes |
115
- | `:xhigh` | Interacting hard problems requiring comparison of approaches and validation of cross-module invariants; file count or vague complexity claims do not qualify | Investigate an intermittent lifecycle, cache, and cancellation interaction |
116
- | `:max` | Exceptionally difficult synthesis requiring rigorous argument and adversarial validation, or concrete evidence of insufficient lower-effort reasoning; risk keywords alone do not qualify | Prove and repair system-wide concurrency, memory-safety, and low-level invariants |
108
+ | Suffix | Criteria and exclusions | Example |
109
+ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
110
+ | `:off` | Fully specified mechanical work; no locating changes, interpreting meaning, or hidden-impact reasoning | Fix a typo at an exact location using a supplied replacement |
111
+ | `:minimal` | One clear local judgment; no cross-file tracing or multi-step implementation | Explain a short self-contained expression |
112
+ | `:low` | Small implementation or question with a known approach and straightforward checks; not an unknown-root-cause fault | Add a specified validation rule and its test |
113
+ | `:medium` | Bounded multi-step work coordinating known constraints; not interacting unresolved hypotheses | Wire a configuration option through loading, usage, and tests |
114
+ | `:high` | Test competing hypotheses, trace cross-module effects, or reason about critical correctness constraints; security/architecture keywords alone do not qualify | Diagnose cross-module state desynchronization by comparing plausible causes |
115
+ | `:xhigh` | Interacting hard problems requiring comparison of approaches and validation of cross-module invariants; file count or vague complexity claims do not qualify | Investigate an intermittent lifecycle, cache, and cancellation interaction |
116
+ | `:max` | Exceptionally difficult synthesis requiring rigorous argument and adversarial validation, or concrete evidence of insufficient lower-effort reasoning; risk keywords alone do not qualify | Prove and repair system-wide concurrency, memory-safety, and low-level invariants |
117
117
 
118
118
  Implementation, review, input length, file count, risk keywords, and depth mode are not sufficient reasons for high effort. Missing information does not automatically imply `medium/high`; do not invent complexity. For `high/xhigh/max`, briefly identify the concrete reasoning difficulty. The highest supported effort requires neither a failed lower-level attempt nor an available intermediate level. Risk determines what needs verification, not a uniform high effort for every child.
119
119
 
@@ -145,14 +145,14 @@ export const meta = {
145
145
  };
146
146
 
147
147
  const findings = await parallel([
148
- () => agent(
149
- "Review the current branch for correctness and lifecycle bugs.",
150
- { label: "correctness review" },
151
- ),
152
- () => agent(
153
- "Review the current branch for missing or weak regression tests.",
154
- { label: "test review" },
155
- ),
148
+ () =>
149
+ agent("Review the current branch for correctness and lifecycle bugs.", {
150
+ label: "correctness review",
151
+ }),
152
+ () =>
153
+ agent("Review the current branch for missing or weak regression tests.", {
154
+ label: "test review",
155
+ }),
156
156
  ]);
157
157
 
158
158
  return findings.filter(Boolean);
@@ -210,13 +210,63 @@ Token and cost data are reported for observability, not enforced as a budget. Wo
210
210
 
211
211
  ## Development
212
212
 
213
+ Development uses Node.js 24.21.0 (`.node-version`) and Vite+ 1.0.0. The
214
+ `packageManager` field pins npm 11.19.0 and selects the installer used by
215
+ `vp install`, `vp add`, and `vp remove`.
216
+
217
+ With the global Vite+ CLI installed:
218
+
213
219
  ```bash
214
- npm ci
215
- npm run typecheck
216
- npm test
217
- npm pack --dry-run
220
+ vp install --frozen-lockfile --ignore-scripts
221
+ vp run check
222
+ vp run typecheck
223
+ vp run test
224
+ vp pm pack -- --dry-run
218
225
  ```
219
226
 
227
+ Without the global CLI, run `npm ci --ignore-scripts`, then `npm run check`,
228
+ `npm run typecheck`, and `npm test`. These scripts use the pinned local `vp`;
229
+ release CI follows this path and preserves npm trusted publishing and version gates.
230
+
231
+ `vp test` invokes the built-in test runner once; use `vp test watch` for watch mode.
232
+ `vp run test` executes this package's `test` script, which uses `vp test run` for a
233
+ single run. `vp dev` starts Vite's development server; this source-distributed
234
+ extension has no web development server or `dev` task.
235
+
236
+ ### Static checks
237
+
238
+ `vp check` and `vp run check` run the same complete formatting, lint, and type
239
+ checks. The `check` script calls `vp check` without bypass flags or path filters,
240
+ and release CI uses this same script. Lint warnings remain visible for review.
241
+ Use `vp fmt <files>` to format changed files.
242
+
243
+ `vp run typecheck` runs only TypeScript (`vp exec tsc --noEmit`) against
244
+ `tsconfig.json`. It is a focused development command; the complete `check` task
245
+ remains the CI gate.
246
+
247
+ Saved JavaScript workflows in `examples/` run in the workflow sandbox. The example
248
+ uses a JSDoc type-only import from `pi-ultracode/workflow` to load the ambient
249
+ workflow declarations in both the checkout and an installed package, without
250
+ adding a runtime import or a checkout-only alias.
251
+
252
+ Tests retain isolated fork workers, automatic mock restoration, and the previous
253
+ runner's unlimited default test timeout.
254
+
255
+ ### Package validation
256
+
257
+ The published package intentionally contains `extensions/`, `src/`, `types/`,
258
+ examples, and documentation. Pi loads the TypeScript entry directly, and
259
+ `src/workflow/script-executor.ts` locates its sibling `script-worker.mjs`.
260
+ `npm pack --dry-run` checks the actual publication contents; an extracted tarball
261
+ should also resolve `/// <reference types="pi-ultracode/workflow" />` from a
262
+ consumer TypeScript project.
263
+
264
+ `vp pack` is a tsdown bundle builder, not a replacement for `npm pack` here. An
265
+ optional smoke check such as
266
+ `vp pack --no-config src/depth.ts --out-dir /tmp/pi-ultracode-pack-smoke --no-dts`
267
+ only tests the bundler. It does not validate the extension's release artifact.
268
+ There is intentionally no generated `dist/`, build step, or rewritten export map.
269
+
220
270
  ## License
221
271
 
222
272
  MIT
package/README.zh-CN.md CHANGED
@@ -57,21 +57,21 @@ Pi 会自行判断工作流是否有帮助。小任务仍可继续使用普通
57
57
 
58
58
  常用命令:
59
59
 
60
- | 命令 | 作用 |
61
- | --- | --- |
62
- | `/ultracode` | 关闭时开启 `auto`;任一模式已开启时关闭 |
63
- | `/ultracode auto` | 始终由父代理结合任务和上下文选择深度 |
64
- | `/ultracode focused` | 固定处理方法已知、范围明确且能局部直接验证的工作 |
65
- | `/ultracode standard` | 固定调查独立维度或竞争假设,并做针对性验证 |
66
- | `/ultracode deep` | 固定检查相互作用的约束或关键不变量,要求独立证据和反例检验 |
67
- | `/ultracode off` | 关闭且不改变父代理 effort |
68
- | `/ultracode status` | 查看配置模式及其适用条件、验证要求 |
69
- | `/ultracode default on` | 全局默认开启,并立即开启当前会话;已开启时保留当前深度 |
70
- | `/ultracode default off` | 取消默认开启 |
71
- | `/ultracode default` | 查看全局启动默认值 |
72
- | `/workflows` 或 `F6` | 打开工作流浏览器 |
73
- | `/workflows <runId>` | 打开指定运行 |
74
- | `/workflows abort` | 中止活动运行 |
60
+ | 命令 | 作用 |
61
+ | ------------------------ | ---------------------------------------------------------- |
62
+ | `/ultracode` | 关闭时开启 `auto`;任一模式已开启时关闭 |
63
+ | `/ultracode auto` | 始终由父代理结合任务和上下文选择深度 |
64
+ | `/ultracode focused` | 固定处理方法已知、范围明确且能局部直接验证的工作 |
65
+ | `/ultracode standard` | 固定调查独立维度或竞争假设,并做针对性验证 |
66
+ | `/ultracode deep` | 固定检查相互作用的约束或关键不变量,要求独立证据和反例检验 |
67
+ | `/ultracode off` | 关闭且不改变父代理 effort |
68
+ | `/ultracode status` | 查看配置模式及其适用条件、验证要求 |
69
+ | `/ultracode default on` | 全局默认开启,并立即开启当前会话;已开启时保留当前深度 |
70
+ | `/ultracode default off` | 取消默认开启 |
71
+ | `/ultracode default` | 查看全局启动默认值 |
72
+ | `/workflows` 或 `F6` | 打开工作流浏览器 |
73
+ | `/workflows <runId>` | 打开指定运行 |
74
+ | `/workflows abort` | 中止活动运行 |
75
75
 
76
76
  `/ultracode default on` 保存全局启动默认值,并立即将关闭的当前会话切换为 `auto`;当前已开启时保留所选深度。`/ultracode default off` 只取消默认开启,不关闭当前会话;关闭当前会话请用 `/ultracode off`。这些操作均不改变 effort。设置保存在 `~/.pi/agent/ultracode.json`(遵循 `PI_CODING_AGENT_DIR`),跨项目和重启生效;未设置时默认关闭。启动时,只有当前分支没有保存 Ultracode 模式才应用默认值,已有的 `off` 或深度选择在 reload、resume、fork 后继续保留。显式 `pi --ultracode` 仍可启用 `auto`。
77
77
 
@@ -81,12 +81,12 @@ Pi 会自行判断工作流是否有帮助。小任务仍可继续使用普通
81
81
 
82
82
  深度决定**要调查和验证什么**,不是模型 effort,也不是代理数量或调查轮数配额:
83
83
 
84
- | 深度 | 适用条件 | 验证要求 |
85
- |---|---|---|
86
- | **Focused** | 范围明确、方法已知、结果可局部验证;例如添加指定校验规则 | 直接检查修改行为或关键结论,通常使用父代理循环。默认不做广泛调查或对抗评审,但不能省略验证。 |
87
- | **Standard** | 多个独立维度或未解决的竞争假设会实质影响答案;例如故障存在多个合理根因 | 调查相关维度或比较根因,再针对冲突、证据薄弱的结论验证。仅仅步骤多且方法已知,不足以要求 standard。 |
88
- | **Deep** | 紧密相互作用的硬约束或关键不变量,需要独立证据与反例检验;例如取消与持久提交发生竞争 | 追踪相互影响,检查边界及失败路径,用不同测试、复现、证明或来源挑战关键结论。第二个代理既非必要条件,也不保证证据独立。 |
89
- | **Auto** | 选择足以处理当前任务的最小深度 | 遵守所选深度的证据要求;仅在实质新证据或未解决冲突要求时升级。 |
84
+ | 深度 | 适用条件 | 验证要求 |
85
+ | ------------ | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
86
+ | **Focused** | 范围明确、方法已知、结果可局部验证;例如添加指定校验规则 | 直接检查修改行为或关键结论,通常使用父代理循环。默认不做广泛调查或对抗评审,但不能省略验证。 |
87
+ | **Standard** | 多个独立维度或未解决的竞争假设会实质影响答案;例如故障存在多个合理根因 | 调查相关维度或比较根因,再针对冲突、证据薄弱的结论验证。仅仅步骤多且方法已知,不足以要求 standard。 |
88
+ | **Deep** | 紧密相互作用的硬约束或关键不变量,需要独立证据与反例检验;例如取消与持久提交发生竞争 | 追踪相互影响,检查边界及失败路径,用不同测试、复现、证明或来源挑战关键结论。第二个代理既非必要条件,也不保证证据独立。 |
89
+ | **Auto** | 选择足以处理当前任务的最小深度 | 遵守所选深度的证据要求;仅在实质新证据或未解决冲突要求时升级。 |
90
90
 
91
91
  风险决定**哪些结论需要验证**。安全、并发、数据丢失等关键词、文件数量或“做一次 review”本身不能自动触发 `deep`;信息不足也不证明存在隐藏复杂度。Deep 不自动意味着必须使用工作流、增加代理或反复调查。显式 `focused` / `standard` / `deep` 保持固定:若不足以确认结论,应说明不确定性并建议更深模式,不能悄悄越界。
92
92
 
@@ -108,15 +108,15 @@ Ultracode 不会修改父会话的 effort;以下父代理选择作为默认和
108
108
 
109
109
  快照来自本地注册信息,不会在线探测提供商,且运行期间可能过时;执行端仍用 clamp 保护显式请求或过时信息。SDK 单独使用 `createWorkflowTool()` 而不加载扩展时,不会注入此区块,宿主需自行提供能力上下文才能让父代理据此自动选择后缀。快照存在与否,都不会改写用户显式选择的 effort 或无后缀默认行为。
110
110
 
111
- | 后缀 | 适用条件与排除条件 | 例子 |
112
- |---|---|---|
113
- | `:off` | 完全指定的机械操作;不涉及寻找修改位置、判断含义或隐藏影响 | 在指定位置按给定内容修正错字 |
114
- | `:minimal` | 单一范围内的一次明确局部判断;不涉及跨文件追踪或多步骤实现 | 解释一个自包含短表达式 |
115
- | `:low` | 目标清楚、方法已知、验证直接的小实现或问题;不包含根因未知的故障 | 给已有函数添加指定校验规则及测试 |
116
- | `:medium` | 协调多个已知约束的有界多步骤实现或调查;不包含相互影响的未解决假设 | 在既有架构中贯通配置加载、使用和测试 |
117
- | `:high` | 检验多个合理假设、追踪跨模块影响,或推理关键正确性约束;不能只凭安全或架构关键词升级 | 比较竞争根因,诊断跨模块状态不同步 |
118
- | `:xhigh` | 多个困难问题相互作用,需要比较方案、验证跨模块不变量;文件多或笼统声称复杂不够 | 排查生命周期、缓存、取消相互影响的偶发故障 |
119
- | `:max` | 极难的综合推理,需要严密论证及反例检验,或已有低档推理不足的具体证据;风险关键词本身不够 | 证明并修复涉及并发、内存安全和底层语义的全系统不变量 |
111
+ | 后缀 | 适用条件与排除条件 | 例子 |
112
+ | ---------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------- |
113
+ | `:off` | 完全指定的机械操作;不涉及寻找修改位置、判断含义或隐藏影响 | 在指定位置按给定内容修正错字 |
114
+ | `:minimal` | 单一范围内的一次明确局部判断;不涉及跨文件追踪或多步骤实现 | 解释一个自包含短表达式 |
115
+ | `:low` | 目标清楚、方法已知、验证直接的小实现或问题;不包含根因未知的故障 | 给已有函数添加指定校验规则及测试 |
116
+ | `:medium` | 协调多个已知约束的有界多步骤实现或调查;不包含相互影响的未解决假设 | 在既有架构中贯通配置加载、使用和测试 |
117
+ | `:high` | 检验多个合理假设、追踪跨模块影响,或推理关键正确性约束;不能只凭安全或架构关键词升级 | 比较竞争根因,诊断跨模块状态不同步 |
118
+ | `:xhigh` | 多个困难问题相互作用,需要比较方案、验证跨模块不变量;文件多或笼统声称复杂不够 | 排查生命周期、缓存、取消相互影响的偶发故障 |
119
+ | `:max` | 极难的综合推理,需要严密论证及反例检验,或已有低档推理不足的具体证据;风险关键词本身不够 | 证明并修复涉及并发、内存安全和底层语义的全系统不变量 |
120
120
 
121
121
  写代码、审查、文本长度、文件数量、风险关键词及 depth 模式都不能单独决定高档;信息不足不自动意味着 `medium/high`,也不能臆造复杂度。选择 `high/xhigh/max` 时须简述具体推理难点。最高档不要求先经历低档失败,也不要求模型支持中间档。风险决定需要验证什么,不等于所有参与代理都要高 effort。
122
122
 
@@ -148,14 +148,14 @@ export const meta = {
148
148
  };
149
149
 
150
150
  const findings = await parallel([
151
- () => agent(
152
- "Review the current branch for correctness and lifecycle bugs.",
153
- { label: "correctness review" },
154
- ),
155
- () => agent(
156
- "Review the current branch for missing or weak regression tests.",
157
- { label: "test review" },
158
- ),
151
+ () =>
152
+ agent("Review the current branch for correctness and lifecycle bugs.", {
153
+ label: "correctness review",
154
+ }),
155
+ () =>
156
+ agent("Review the current branch for missing or weak regression tests.", {
157
+ label: "test review",
158
+ }),
159
159
  ]);
160
160
 
161
161
  return findings.filter(Boolean);
@@ -213,13 +213,56 @@ Token 和成本只用于可观测性,不作为执行预算。Worker 与 VM 限
213
213
 
214
214
  ## 开发
215
215
 
216
+ 开发使用 Node.js 24.21.0(`.node-version`)和 Vite+ 1.0.0。
217
+ `packageManager` 固定 npm 11.19.0,并决定 `vp install`、`vp add`、`vp remove`
218
+ 使用的包管理器。
219
+
220
+ 已安装全局 Vite+ CLI 时:
221
+
216
222
  ```bash
217
- npm ci
218
- npm run typecheck
219
- npm test
220
- npm pack --dry-run
223
+ vp install --frozen-lockfile --ignore-scripts
224
+ vp run check
225
+ vp run typecheck
226
+ vp run test
227
+ vp pm pack -- --dry-run
221
228
  ```
222
229
 
230
+ 也可以先运行 `npm ci --ignore-scripts`,再运行 `npm run check`、
231
+ `npm run typecheck` 和 `npm test`。这些脚本使用固定版本的本地 `vp`;
232
+ 发布 CI 使用此方式,并保留 npm 可信发布与版本校验。
233
+
234
+ `vp test` 调用内置测试运行器并执行一次;使用 `vp test watch` 监听文件变化。
235
+ `vp run test` 执行本项目的 `test` 脚本,通过 `vp test run` 单次运行测试。
236
+ `vp dev` 启动 Vite 开发服务器;本项目按源码发布扩展,未配置网页开发服务器或 `dev` 任务。
237
+
238
+ ### 静态检查
239
+
240
+ `vp check` 和 `vp run check` 执行相同的完整格式、lint 和类型检查。
241
+ `check` 脚本直接调用 `vp check`,不添加跳过参数或路径过滤;发布 CI 使用同一脚本。
242
+ Lint 警告保持可见,供后续审查。使用 `vp fmt <files>` 格式化修改的文件。
243
+
244
+ `vp run typecheck` 仅通过 `vp exec tsc --noEmit` 检查 `tsconfig.json` 中的 TypeScript。
245
+ 此命令用于开发时的定向检查,CI 仍执行完整 `check` 任务。
246
+
247
+ `examples/` 中的 JavaScript 工作流在工作流沙箱中运行。示例使用 JSDoc 类型导入引用
248
+ `pi-ultracode/workflow`,在源码目录和已安装包中加载全局工作流声明,保持运行时零导入,
249
+ 无需添加源码目录专用别名。
250
+
251
+ 测试保留隔离的 fork worker、自动恢复 mock,以及原测试运行器默认无超时限制的行为。
252
+
253
+ ### 发布包验证
254
+
255
+ 发布包保留 `extensions/`、`src/`、`types/`、示例和文档。Pi 直接加载 TypeScript
256
+ 入口,`src/workflow/script-executor.ts` 按相邻路径定位 `script-worker.mjs`。
257
+ `npm pack --dry-run` 检查实际发布内容;还应解压 tarball,在消费方 TypeScript
258
+ 项目中验证 `/// <reference types="pi-ultracode/workflow" />` 的类型解析。
259
+
260
+ `vp pack` 使用 tsdown 生成 bundle,与本项目的 `npm pack` 发布流程用途不同。
261
+ 可选冒烟命令
262
+ `vp pack --no-config src/depth.ts --out-dir /tmp/pi-ultracode-pack-smoke --no-dts`
263
+ 仅验证打包器,扩展发布产物仍需上述检查。项目保持源码布局,无需生成 `dist/`、
264
+ 增加构建步骤或改写导出映射。
265
+
223
266
  ## 许可证
224
267
 
225
268
  MIT
@@ -1,4 +1,4 @@
1
- /// <reference types="pi-ultracode/workflow" />
1
+ /** @import {} from "pi-ultracode/workflow" */
2
2
  //
3
3
  // Example saved workflow. Copy to `.pi/ultracode/workflows/` and run it with
4
4
  // the `workflow` tool's `name` parameter, or call `workflow('loop-until-dry-bugs')`
@@ -9,76 +9,100 @@
9
9
  // confirms each fresh bug with three distinct lenses.
10
10
 
11
11
  export const meta = {
12
- name: 'loop-until-dry-bugs',
13
- description: 'Find bugs until the well runs dry, verifying each with three lenses',
14
- phases: [{ title: 'Find' }, { title: 'Verify' }],
15
- }
12
+ name: "loop-until-dry-bugs",
13
+ description: "Find bugs until the well runs dry, verifying each with three lenses",
14
+ phases: [{ title: "Find" }, { title: "Verify" }],
15
+ };
16
16
 
17
17
  const FINDERS = [
18
- 'Find correctness bugs by reading the changed files closely.',
19
- 'Find bugs by tracing data flow across module boundaries.',
20
- 'Find bugs by hunting error-handling and edge-case gaps.',
21
- ]
18
+ "Find correctness bugs by reading the changed files closely.",
19
+ "Find bugs by tracing data flow across module boundaries.",
20
+ "Find bugs by hunting error-handling and edge-case gaps.",
21
+ ];
22
22
 
23
23
  const BUGS = {
24
- type: 'object',
24
+ type: "object",
25
25
  properties: {
26
26
  bugs: {
27
- type: 'array',
27
+ type: "array",
28
28
  items: {
29
- type: 'object',
30
- properties: { file: { type: 'string' }, line: { type: 'number' }, desc: { type: 'string' } },
31
- required: ['file', 'desc'],
29
+ type: "object",
30
+ properties: {
31
+ file: { type: "string" },
32
+ line: { type: "number" },
33
+ desc: { type: "string" },
34
+ },
35
+ required: ["file", "desc"],
32
36
  },
33
37
  },
34
38
  },
35
- required: ['bugs'],
36
- }
39
+ required: ["bugs"],
40
+ };
37
41
 
38
42
  const VERDICT = {
39
- type: 'object',
40
- properties: { real: { type: 'boolean' }, why: { type: 'string' } },
41
- required: ['real'],
42
- }
43
+ type: "object",
44
+ properties: { real: { type: "boolean" }, why: { type: "string" } },
45
+ required: ["real"],
46
+ };
43
47
 
44
- const seen = new Set()
45
- const confirmed = []
46
- let dry = 0
47
- let roundsRun = 0
48
- const MAX_ROUNDS = 2
48
+ const seen = new Set();
49
+ const confirmed = [];
50
+ let dry = 0;
51
+ let roundsRun = 0;
52
+ const MAX_ROUNDS = 2;
49
53
 
50
54
  while (dry < 2 && roundsRun < MAX_ROUNDS) {
51
- roundsRun++
55
+ roundsRun++;
52
56
  const rounds = await parallel(
53
- FINDERS.map((prompt, i) => () => agent(prompt, { label: 'find ' + i, phase: 'Find', schema: BUGS })),
54
- )
55
- const found = rounds.filter(Boolean).flatMap((r) => r.bugs ?? [])
56
- const fresh = found.filter((b) => !seen.has(b.file + ':' + b.desc))
57
+ FINDERS.map(
58
+ (prompt, i) => () => agent(prompt, { label: "find " + i, phase: "Find", schema: BUGS }),
59
+ ),
60
+ );
61
+ const found = rounds.filter(Boolean).flatMap((r) => r.bugs ?? []);
62
+ const fresh = found.filter((b) => !seen.has(b.file + ":" + b.desc));
57
63
  if (fresh.length === 0) {
58
- dry++
59
- continue
64
+ dry++;
65
+ continue;
60
66
  }
61
- dry = 0
62
- for (const b of fresh) seen.add(b.file + ':' + b.desc)
67
+ dry = 0;
68
+ for (const b of fresh) seen.add(b.file + ":" + b.desc);
63
69
 
64
70
  const judged = await parallel(
65
71
  fresh.map((b) => async () => {
66
72
  const votes = await parallel(
67
- ['correctness', 'security', 'does-it-reproduce'].map((lens) => () =>
68
- agent('Judge via the ' + lens + ' lens — is this real? "' + b.desc + '" (' + b.file + '). Default to real:false if unsure.',
69
- { label: 'verify ' + lens, phase: 'Verify', agentType: 'code-reviewer', schema: VERDICT }),
73
+ ["correctness", "security", "does-it-reproduce"].map(
74
+ (lens) => () =>
75
+ agent(
76
+ "Judge via the " +
77
+ lens +
78
+ ' lens — is this real? "' +
79
+ b.desc +
80
+ '" (' +
81
+ b.file +
82
+ "). Default to real:false if unsure.",
83
+ {
84
+ label: "verify " + lens,
85
+ phase: "Verify",
86
+ agentType: "code-reviewer",
87
+ schema: VERDICT,
88
+ },
89
+ ),
70
90
  ),
71
- )
72
- return { bug: b, real: votes.filter(Boolean).filter((v) => v.real).length >= 2 }
91
+ );
92
+ return { bug: b, real: votes.filter(Boolean).filter((v) => v.real).length >= 2 };
73
93
  }),
74
- )
75
- confirmed.push(...judged.filter((j) => j.real).map((j) => j.bug))
94
+ );
95
+ confirmed.push(...judged.filter((j) => j.real).map((j) => j.bug));
76
96
  }
77
97
 
78
- const complete = dry >= 2
79
- const truncated = !complete
98
+ const complete = dry >= 2;
99
+ const truncated = !complete;
80
100
  if (truncated) {
81
- log('Stopped after MAX_ROUNDS=' + MAX_ROUNDS + ' before reaching two consecutive dry rounds; returning truncated results.')
101
+ log(
102
+ "Stopped after MAX_ROUNDS=" +
103
+ MAX_ROUNDS +
104
+ " before reaching two consecutive dry rounds; returning truncated results.",
105
+ );
82
106
  }
83
107
 
84
- return { confirmed, totalSeen: seen.size, roundsRun, complete, truncated }
108
+ return { confirmed, totalSeen: seen.size, roundsRun, complete, truncated };
@@ -5,7 +5,8 @@
5
5
  * workflow orchestration, and the `/ultracode` / `/workflows` commands.
6
6
  */
7
7
 
8
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
8
+ import { Effect } from "effect";
9
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
9
10
  import { createWorkflowTool, type WorkflowToolDeps } from "../src/workflow/tool.ts";
10
11
  import { UltracodeMode, type ThinkingPreferenceStore } from "../src/mode.ts";
11
12
  import { registerCommands } from "../src/commands.ts";
@@ -62,54 +63,73 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
62
63
  if (event.toolName === workflowTool.name && !mode.isEnforcing()) {
63
64
  return {
64
65
  block: true,
65
- reason: "The workflow tool is disabled. Run /ultracode or select an Ultracode depth before using it.",
66
+ reason:
67
+ "The workflow tool is disabled. Run /ultracode or select an Ultracode depth before using it.",
66
68
  };
67
69
  }
68
70
  });
69
71
 
70
- pi.on("session_start", async (_event, ctx) => {
71
- // Restore persisted mode state across reload / resume / fork.
72
- let hasSavedMode = true;
73
- try {
74
- // Mode entries are branch-local; discarded future branches must not win.
75
- hasSavedMode = mode.restore(pi, ctx.sessionManager.getBranch() as any);
76
- } catch {
77
- // Do not apply a startup default when branch restoration failed.
78
- }
79
- if (!mode.isEnabled() && pi.getFlag?.("ultracode") === true) {
80
- mode.enable(pi, "auto");
81
- } else if (!hasSavedMode && !mode.isEnabled()) {
82
- try {
83
- if (preferences.getDefaultEnabled()) mode.enable(pi, "auto");
84
- } catch (error) {
85
- ctx.ui.notify(`Failed to read Ultracode default: ${String(error)}`, "warning");
72
+ const attempt = <A>(thunk: () => A) => Effect.try({ try: thunk, catch: (error) => error });
73
+ const updateStatus = (ctx: ExtensionContext) =>
74
+ attempt(() => {
75
+ mode.syncWorkflowTool(pi);
76
+ if (ctx.hasUI) {
77
+ ctx.ui.setStatus(
78
+ "ultracode",
79
+ mode.isEnabled()
80
+ ? mode.statusLine((text, color) => ctx.ui.theme.fg(color, text))
81
+ : undefined,
82
+ );
86
83
  }
87
- }
88
- // Registration makes tools discoverable; activation follows the chosen mode.
89
- mode.syncWorkflowTool(pi);
90
- if (ctx.hasUI) {
91
- ctx.ui.setStatus(
92
- "ultracode",
93
- mode.isEnabled() ? mode.statusLine((label) => ctx.ui.theme.fg("accent", label)) : undefined,
94
- );
95
- }
96
- });
84
+ });
97
85
 
98
- pi.on("session_tree", async (_event, ctx) => {
99
- if (mode.isSuspended()) return;
100
- mode.restore(pi, ctx.sessionManager.getBranch() as any);
101
- if (ctx.hasUI) {
102
- ctx.ui.setStatus(
103
- "ultracode",
104
- mode.isEnabled() ? mode.statusLine((label) => ctx.ui.theme.fg("accent", label)) : undefined,
105
- );
106
- }
107
- });
86
+ pi.on("session_start", (_event, ctx) =>
87
+ Effect.runPromise(
88
+ // Mode entries are branch-local; restoration failure suppresses startup defaults.
89
+ attempt(() => mode.restore(pi, ctx.sessionManager.getBranch() as any)).pipe(
90
+ Effect.catch(() => Effect.succeed(true)),
91
+ Effect.flatMap((hasSavedMode) =>
92
+ attempt(() => pi.getFlag?.("ultracode") === true).pipe(
93
+ Effect.flatMap((flagEnabled) => {
94
+ if (!mode.isEnabled() && flagEnabled) return attempt(() => mode.enable(pi, "auto"));
95
+ if (hasSavedMode || mode.isEnabled()) return Effect.void;
96
+ return attempt(() => {
97
+ if (preferences.getDefaultEnabled()) mode.enable(pi, "auto");
98
+ }).pipe(
99
+ Effect.catch((error) =>
100
+ attempt(() => {
101
+ ctx.ui.notify(`Failed to read Ultracode default: ${String(error)}`, "warning");
102
+ }),
103
+ ),
104
+ );
105
+ }),
106
+ ),
107
+ ),
108
+ Effect.andThen(updateStatus(ctx)),
109
+ ),
110
+ ),
111
+ );
108
112
 
109
- pi.on("session_shutdown", async () => {
110
- // The persisted configured mode remains active for reload/resume/fork replacements.
111
- mode.suspend(pi);
112
- });
113
+ pi.on("session_tree", (_event, ctx) =>
114
+ Effect.runPromise(
115
+ Effect.suspend(() =>
116
+ mode.isSuspended()
117
+ ? Effect.void
118
+ : attempt(() => mode.restore(pi, ctx.sessionManager.getBranch() as any)).pipe(
119
+ Effect.andThen(updateStatus(ctx)),
120
+ ),
121
+ ),
122
+ ),
123
+ );
124
+
125
+ pi.on("session_shutdown", () =>
126
+ Effect.runPromise(
127
+ attempt(() => {
128
+ // Persisted configuration survives reload/resume/fork replacements.
129
+ mode.suspend(pi);
130
+ }),
131
+ ),
132
+ );
113
133
 
114
134
  pi.on("before_agent_start", (event, ctx) => {
115
135
  // Reconcile tool availability and update our prompt section on every turn,