@selesai/code 0.5.17 → 0.5.18

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interactive-mode.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-mode.test.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.test.ts"],"names":[],"mappings":"","sourcesContent":["import { describe, expect, it, vi } from \"vitest\";\nimport { InteractiveMode } from \"./interactive-mode.js\";\n\ndescribe(\"InteractiveMode handleEvent tool_execution_update\", () => {\n\tit(\"refreshes displayed args from execution update\", () => {\n\t\tconst component = {\n\t\t\tupdateArgs: vi.fn(),\n\t\t\tupdateResult: vi.fn(),\n\t\t};\n\t\tconst requestRender = vi.fn();\n\n\t\tconst mode = Object.create(InteractiveMode.prototype) as InteractiveMode;\n\t\tmode.isInitialized = true;\n\t\tmode.footer = { invalidate: vi.fn() } as any;\n\t\tmode.pendingTools = new Map([[\"tc-1\", component as any]]);\n\t\tmode.ui = { requestRender } as any as InteractiveMode[\"ui\"];\n\n\t\tmode.handleEvent({\n\t\t\ttype: \"tool_execution_update\",\n\t\t\ttoolCallId: \"tc-1\",\n\t\t\ttoolName: \"bash\",\n\t\t\targs: { command: \"rtk ls -la\" },\n\t\t\tpartialResult: { content: [], details: undefined },\n\t\t} as any);\n\n\t\texpect(component.updateArgs).toHaveBeenCalledTimes(1);\n\t\texpect(component.updateArgs).toHaveBeenCalledWith({ command: \"rtk ls -la\" });\n\t\texpect(component.updateResult).toHaveBeenCalledTimes(1);\n\t\texpect(component.updateResult).toHaveBeenCalledWith(\n\t\t\t{ content: [], details: undefined, isError: false },\n\t\t\ttrue,\n\t\t);\n\t\texpect(requestRender).toHaveBeenCalledTimes(1);\n\t});\n\n\tit(\"is a no-op when update references an unknown tool\", () => {\n\t\tconst requestRender = vi.fn();\n\t\tconst mode = Object.create(InteractiveMode.prototype) as InteractiveMode;\n\t\tmode.isInitialized = true;\n\t\tmode.footer = { invalidate: vi.fn() } as any;\n\t\tmode.pendingTools = new Map();\n\t\tmode.ui = { requestRender } as any as InteractiveMode[\"ui\"];\n\n\t\tmode.handleEvent({\n\t\t\ttype: \"tool_execution_update\",\n\t\t\ttoolCallId: \"missing\",\n\t\t\ttoolName: \"bash\",\n\t\t\targs: { command: \"rtk ls -la\" },\n\t\t\tpartialResult: { content: [], details: undefined },\n\t\t} as any);\n\n\t\texpect(requestRender).not.toHaveBeenCalled();\n\t});\n});\n"]}
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { InteractiveMode } from "./interactive-mode.js";
3
+ describe("InteractiveMode handleEvent tool_execution_update", () => {
4
+ it("refreshes displayed args from execution update", () => {
5
+ const component = {
6
+ updateArgs: vi.fn(),
7
+ updateResult: vi.fn(),
8
+ };
9
+ const requestRender = vi.fn();
10
+ const mode = Object.create(InteractiveMode.prototype);
11
+ mode.isInitialized = true;
12
+ mode.footer = { invalidate: vi.fn() };
13
+ mode.pendingTools = new Map([["tc-1", component]]);
14
+ mode.ui = { requestRender };
15
+ mode.handleEvent({
16
+ type: "tool_execution_update",
17
+ toolCallId: "tc-1",
18
+ toolName: "bash",
19
+ args: { command: "rtk ls -la" },
20
+ partialResult: { content: [], details: undefined },
21
+ });
22
+ expect(component.updateArgs).toHaveBeenCalledTimes(1);
23
+ expect(component.updateArgs).toHaveBeenCalledWith({ command: "rtk ls -la" });
24
+ expect(component.updateResult).toHaveBeenCalledTimes(1);
25
+ expect(component.updateResult).toHaveBeenCalledWith({ content: [], details: undefined, isError: false }, true);
26
+ expect(requestRender).toHaveBeenCalledTimes(1);
27
+ });
28
+ it("is a no-op when update references an unknown tool", () => {
29
+ const requestRender = vi.fn();
30
+ const mode = Object.create(InteractiveMode.prototype);
31
+ mode.isInitialized = true;
32
+ mode.footer = { invalidate: vi.fn() };
33
+ mode.pendingTools = new Map();
34
+ mode.ui = { requestRender };
35
+ mode.handleEvent({
36
+ type: "tool_execution_update",
37
+ toolCallId: "missing",
38
+ toolName: "bash",
39
+ args: { command: "rtk ls -la" },
40
+ partialResult: { content: [], details: undefined },
41
+ });
42
+ expect(requestRender).not.toHaveBeenCalled();
43
+ });
44
+ });
45
+ //# sourceMappingURL=interactive-mode.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-mode.test.js","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,QAAQ,CAAC,mDAAmD,EAAE,GAAG,EAAE,CAAC;IACnE,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE,CAAC;QAC1D,MAAM,SAAS,GAAG;YACjB,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;YACnB,YAAY,EAAE,EAAE,CAAC,EAAE,EAAE;SACrB,CAAC;QACF,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAE9B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,SAAS,CAAoB,CAAC;QACzE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,EAAS,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,SAAgB,CAAC,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,EAAE,GAAG,EAAE,aAAa,EAAkC,CAAC;QAE5D,IAAI,CAAC,WAAW,CAAC;YAChB,IAAI,EAAE,uBAAuB;YAC7B,UAAU,EAAE,MAAM;YAClB,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE;YAC/B,aAAa,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;SAC3C,CAAC,CAAC;QAEV,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;QAC7E,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAClD,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,EACnD,IAAI,CACJ,CAAC;QACF,MAAM,CAAC,aAAa,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAAA,CAC/C,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE,CAAC;QAC7D,MAAM,aAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,SAAS,CAAoB,CAAC;QACzE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,EAAS,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC,EAAE,GAAG,EAAE,aAAa,EAAkC,CAAC;QAE5D,IAAI,CAAC,WAAW,CAAC;YAChB,IAAI,EAAE,uBAAuB;YAC7B,UAAU,EAAE,SAAS;YACrB,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE;YAC/B,aAAa,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;SAC3C,CAAC,CAAC;QAEV,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAAA,CAC7C,CAAC,CAAC;AAAA,CACH,CAAC,CAAC","sourcesContent":["import { describe, expect, it, vi } from \"vitest\";\nimport { InteractiveMode } from \"./interactive-mode.js\";\n\ndescribe(\"InteractiveMode handleEvent tool_execution_update\", () => {\n\tit(\"refreshes displayed args from execution update\", () => {\n\t\tconst component = {\n\t\t\tupdateArgs: vi.fn(),\n\t\t\tupdateResult: vi.fn(),\n\t\t};\n\t\tconst requestRender = vi.fn();\n\n\t\tconst mode = Object.create(InteractiveMode.prototype) as InteractiveMode;\n\t\tmode.isInitialized = true;\n\t\tmode.footer = { invalidate: vi.fn() } as any;\n\t\tmode.pendingTools = new Map([[\"tc-1\", component as any]]);\n\t\tmode.ui = { requestRender } as any as InteractiveMode[\"ui\"];\n\n\t\tmode.handleEvent({\n\t\t\ttype: \"tool_execution_update\",\n\t\t\ttoolCallId: \"tc-1\",\n\t\t\ttoolName: \"bash\",\n\t\t\targs: { command: \"rtk ls -la\" },\n\t\t\tpartialResult: { content: [], details: undefined },\n\t\t} as any);\n\n\t\texpect(component.updateArgs).toHaveBeenCalledTimes(1);\n\t\texpect(component.updateArgs).toHaveBeenCalledWith({ command: \"rtk ls -la\" });\n\t\texpect(component.updateResult).toHaveBeenCalledTimes(1);\n\t\texpect(component.updateResult).toHaveBeenCalledWith(\n\t\t\t{ content: [], details: undefined, isError: false },\n\t\t\ttrue,\n\t\t);\n\t\texpect(requestRender).toHaveBeenCalledTimes(1);\n\t});\n\n\tit(\"is a no-op when update references an unknown tool\", () => {\n\t\tconst requestRender = vi.fn();\n\t\tconst mode = Object.create(InteractiveMode.prototype) as InteractiveMode;\n\t\tmode.isInitialized = true;\n\t\tmode.footer = { invalidate: vi.fn() } as any;\n\t\tmode.pendingTools = new Map();\n\t\tmode.ui = { requestRender } as any as InteractiveMode[\"ui\"];\n\n\t\tmode.handleEvent({\n\t\t\ttype: \"tool_execution_update\",\n\t\t\ttoolCallId: \"missing\",\n\t\t\ttoolName: \"bash\",\n\t\t\targs: { command: \"rtk ls -la\" },\n\t\t\tpartialResult: { content: [], details: undefined },\n\t\t} as any);\n\n\t\texpect(requestRender).not.toHaveBeenCalled();\n\t});\n});\n"]}
package/docs/workflows.md CHANGED
@@ -18,7 +18,7 @@ src/extensions/workflow/
18
18
  ```
19
19
 
20
20
  - **`state-machine.ts`** is the deep module. It owns the phase graph, artifact gating, skip rules, the terminal close gate, and the reentrancy guard. It imports nothing external — no `node:fs`, no pi API, no `pi-tui`, no `typebox`. Every method returns a `WorkflowEffect` (a discriminated union in domain vocabulary) that the adapter pattern-matches on.
21
- - **`adapter.ts`** is the thin glue. It owns Pi/fs wiring, durable state, explicit resume, loop review persistence, and the git-based `reuse` skip predicate. Parent-written artifacts advance durable phase state. `prototype` and `quick` stop at user-controlled boundaries; `task` queues its build loop as soon as its plan is ready.
21
+ - **`adapter.ts`** is the thin glue. It owns Pi/fs wiring, durable state, explicit resume, loop review persistence, and the git-based `reuse` skip predicate. Parent-written artifacts advance durable phase state and queue hidden engine continuations; every built-in mode flows automatically.
22
22
  - **`workflow.json`** in each artifact directory is the canonical, versioned run record. It is atomically replaced after state changes; session custom entries are only pointers for UI/history and never reconstruct an active run.
23
23
  - **`extension.ts`** imports each mode's registration object and calls `createWorkflowExtension(config, options)(pi)` for each. One extension load registers one shared writer plus `start_workflow`, `resume_workflow`, and `end_workflow`; each lifecycle call selects a mode.
24
24
  - **A mode file** is pure data: the phase list, per-phase artifact filenames, prompt generators, terminal close artifacts, and command/status/entry identities. Prompts receive `{ artifactDir, userPrompt }`. Each mode exports a `WorkflowModeRegistration` object (e.g. `prototypeMode`, `quickMode`); it does not call `createWorkflowExtension` itself.
@@ -119,7 +119,7 @@ Lifecycle: `plan → reuse → handoff → loop (build ↔ review) → terminal-
119
119
  - `/workflow-task <goal>` — start a new run
120
120
  - `/workflow-task resume` — list and resume active runs
121
121
  - `/workflow-task help` — show the lifecycle
122
- - Valid `plan.md`, `reuse.md`, and `handoff.md` each automatically queue the next phase prompt (the workflow does not pause at those boundaries)
122
+ - Valid phase artifacts automatically queue the next phase prompt (the workflow does not pause at artifact boundaries)
123
123
  - No grilling, research, or audit phases
124
124
  - `reuse.md` is optional; it is skipped automatically when the project has no git history
125
125
 
@@ -136,7 +136,6 @@ Lifecycle: `plan → reuse → handoff → loop (build ↔ review) → terminal-
136
136
  | `statusKey` | `string` | Footer status key. |
137
137
  | `entryType` | `string` | Session-history custom-type. It stores a pointer only; `workflow.json` is canonical. |
138
138
  | `footerLabel` | `string` | Label shown in the footer (`● label · step/total phase`). |
139
- | `continueAfterArtifact?` | `boolean` | Queue the next phase prompt after the parent writes a valid artifact. `task` enables this at every parent-owned artifact boundary (plan, reuse, handoff) so the workflow flows automatically into the loop. |
140
139
 
141
140
  ### Adapter options
142
141
 
@@ -158,7 +157,7 @@ Runs are **never** auto-resumed on session start. At most one run can be attache
158
157
  - `/workflow-prototype resume`, `/workflow-quick resume`, or `/workflow-task resume` lists active runs (and offers a UI picker when available).
159
158
  - `/workflow-prototype help`, `/workflow-quick help`, or `/workflow-task help` shows the start, resume, continue, and explicit-completion lifecycle.
160
159
 
161
- Resume validates the selected file is under the artifacts base, belongs to that mode, is active, and matches its containing directory. It reconciles the current expected artifact once before emitting the current prompt, covering a crash after `write_workflow_artifact` writes the file but before the phase-state write. Artifact writes do not inject the next phase prompt or launch the next subagent; they terminate the parent turn and wait for the user to continue. A mode can opt out of that pause after a parent artifact write; `task` does so at every parent-owned artifact boundary (`plan.md`, `reuse.md`, `handoff.md`) so the build loop starts immediately after a valid handoff. Corrupt records are skipped during discovery.
160
+ Resume validates the selected file is under the artifacts base, belongs to that mode, is active, and matches its containing directory. It reconciles the current expected artifact once before emitting the current prompt, covering a crash after `write_workflow_artifact` writes the file but before the phase-state write. Valid artifact writes queue one hidden engine-controlled continuation using `steer` and terminate the current parent turn; invalid writes stay in the current phase and do not terminate. Prompts injected by start, resume, and continue commands are hidden custom messages rather than visible synthetic user messages. Transition-capable calls (`write_workflow_artifact`, loop commentator transitions, and `end_workflow`) must be the sole tool call in their assistant batch; the adapter fails closed when that cannot be proven. Corrupt records are skipped during discovery. Reloads never auto-resume; explicit `resume_workflow` remains required.
162
161
 
163
162
  A valid terminal artifact makes a workflow **terminal-ready**; it does not complete the run. Call `end_workflow({ mode })` to write `status: "completed"`, append the done entry, and terminate. This is the only completion path.
164
163
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selesai/code",
3
- "version": "0.5.17",
3
+ "version": "0.5.18",
4
4
  "description": "Maintained, extension-first Pi coding agent with built-in workflows, subagents, web research, questions, skills, and an enhanced terminal UI.",
5
5
  "type": "module",
6
6
  "repository": {