@selesai/code 0.5.17 → 0.5.19
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/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +6 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/extensions/workflow/adapter.ts +59 -28
- package/dist/extensions/workflow/modes/prototype.ts +9 -16
- package/dist/extensions/workflow/modes/quick.ts +11 -18
- package/dist/extensions/workflow/modes/task.ts +0 -2
- package/dist/extensions/workflow/state-machine.ts +1 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.test.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.test.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-mode.test.js +45 -0
- package/dist/modes/interactive/interactive-mode.test.js.map +1 -0
- package/dist/skills/batch-grill-me/SKILL.md +15 -0
- package/dist/skills/selesai-handoff/SKILL.md +20 -0
- package/docs/workflows.md +3 -4
- package/package.json +1 -1
|
@@ -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"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: batch-grill-me
|
|
3
|
+
description: A relentless interview that asks every frontier question at once, round by round.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it.
|
|
8
|
+
|
|
9
|
+
Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled — the questions you can ask *now* without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round.
|
|
10
|
+
|
|
11
|
+
Each round the user answers reshapes the tree — settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a *later* round, not this one.
|
|
12
|
+
|
|
13
|
+
Finding *facts* is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it — don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report — ask the rest of the frontier now. The *decisions* are the user's — put each to them and wait.
|
|
14
|
+
|
|
15
|
+
The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: selesai-handoff
|
|
3
|
+
description: Hand the current conversation off to a fresh background agent that picks up the work immediately.
|
|
4
|
+
argument-hint: "What will the next session be used for?"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Write a handoff summary of the current conversation so a fresh agent can continue the work. Instead of saving it, launch a background agent seeded with the summary as its prompt: `selesai --name "<descriptive name>" -p "<handoff summary>"`. It starts in the current working directory and returns immediately; the user manages it with `selesai agents`.
|
|
9
|
+
|
|
10
|
+
Always pass `-n`/`--name` with a descriptive name (e.g. `--name "Fix login bug"`) — it sets the display name shown in the job list, session picker, and terminal title.
|
|
11
|
+
|
|
12
|
+
You might want to use something like `nohup` or `Start-Process` or `start /b` or etc. before the selesai command to spawn background command.
|
|
13
|
+
|
|
14
|
+
Include a "suggested skills" section in the summary, which suggests skills that the agent should invoke.
|
|
15
|
+
|
|
16
|
+
Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
|
|
17
|
+
|
|
18
|
+
Redact any sensitive information, such as API keys, passwords, or personally identifiable information — the summary becomes the agent's prompt.
|
|
19
|
+
|
|
20
|
+
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the summary accordingly.
|
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
|
|
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
|
|
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.
|
|
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.
|
|
3
|
+
"version": "0.5.19",
|
|
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": {
|