@selesai/code 0.9.13 → 0.9.14
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 +8 -0
- package/README.md +5 -10
- package/dist/extensions/enable-readonly-tools.test.ts +52 -0
- package/dist/extensions/enable-readonly-tools.ts +20 -0
- package/dist/extensions/package.json +2 -1
- package/dist/extensions/pi-powerline-footer/guide.ts +5 -20
- package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +2 -3
- package/dist/extensions/pi-subagents/agents/architect.md +2 -1
- package/dist/extensions/pi-subagents/agents/builder.md +3 -1
- package/dist/extensions/pi-subagents/agents/commentator.md +3 -1
- package/dist/extensions/pi-subagents/agents/explorer.md +2 -1
- package/dist/extensions/pi-subagents/agents/recapper.md +2 -1
- package/dist/extensions/pi-subagents/agents/researcher.md +1 -1
- package/dist/extensions/pi-subagents/agents/reviewer.md +3 -1
- package/dist/extensions/pi-subagents/agents/worker.md +2 -1
- package/dist/extensions/preview-tools-disabled.test.ts +37 -0
- package/dist/extensions/preview-tools-disabled.ts +11 -0
- package/dist/extensions/question/batch.ts +1 -1
- package/dist/extensions/question/schemas.ts +2 -2
- package/dist/extensions/question/tests/batch.test.ts +2 -2
- package/dist/skills/workflow/SKILL.md +85 -0
- package/docs/workflows.md +11 -50
- package/package.json +4 -3
- package/dist/extensions/workflow/extension.ts +0 -26
- package/dist/extensions/workflow/modes.ts +0 -107
- package/dist/extensions/workflow/package.json +0 -17
- package/dist/skills/workflow-creation/SKILL.md +0 -73
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@selesai/code` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.9.14] - 2026-08-26
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- **Adaptive workflow skill.** Replaced fixed `/workflow-*` commands with the bundled `$workflow` skill, which selects only the required pi-subagents planning, research, writing, review, and fix stages.
|
|
9
|
+
- **Workflow artifacts.** Built-in workflow roles persist named reports for the next stage, while the parent keeps one writer and final acceptance.
|
|
10
|
+
- **Question flexibility.** Select and multi-select questions now permit an Other answer by default; set `allowOther: false` to restrict choices.
|
|
11
|
+
- **Tool bootstrap.** The standard read-only tool catalog activates after the first durable tool call. Preview-export tools remain hidden without disabling Markdown preview rendering.
|
|
12
|
+
|
|
5
13
|
## [0.9.13] - 2026-08-26
|
|
6
14
|
|
|
7
15
|
### Changed
|
package/README.md
CHANGED
|
@@ -60,18 +60,13 @@ Run parallel commentator agents for correctness, tests, and unnecessary complexi
|
|
|
60
60
|
Have the builder implement this plan, then review the result.
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
###
|
|
63
|
+
### Adaptive implementation workflow
|
|
64
64
|
|
|
65
|
-
Selesai
|
|
65
|
+
Selesai's `workflow` skill is a parent-directed implementation policy, not a fixed slash-command pipeline. It chooses the smallest useful flow for the task: direct implementation for a trivial change; optional reconnaissance or research for uncertainty; one writer; then only the review, fix, and validation passes the risk warrants.
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
| --- | --- | --- |
|
|
69
|
-
| `prototype` | grill → research → plan → reuse → handoff → build/review loop → audit | New prototypes that need discovery and external research |
|
|
70
|
-
| `quicktype` | grill → plan → reuse → handoff → build/review loop → audit | Quicker prototype: same flow without research |
|
|
71
|
-
| `task` | plan → reuse → handoff → build/review loop | Direct implementation work |
|
|
72
|
-
| `loop` | build/review loop | Work with an already-agreed plan |
|
|
67
|
+
The parent remains the decision-maker and keeps one writer per checkout. Reviewers inspect the shared working-tree diff directly, while the parent selectively passes synthesized findings to a scoped fix worker. A handoff artifact is optional—use one only for cross-session continuity, a milestone boundary, or a durable user-facing record.
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
Invoke it inline with `$workflow`, or ask Selesai to orchestrate an implementation. Material architecture or product decisions are resolved before implementation, using an oracle or Council Mode when appropriate.
|
|
75
70
|
|
|
76
71
|
### Web research
|
|
77
72
|
|
|
@@ -140,7 +135,7 @@ Skills are shipped with Selesai and loaded at boot. They include:
|
|
|
140
135
|
- `handoff` and `handoff-text` for session continuity
|
|
141
136
|
- `improve-codebase` for architecture and maintainability reviews
|
|
142
137
|
- `ponytail-review`, `ponytail-audit`, `ponytail-gain`, `ponytail-debt`, and `ponytail-help`
|
|
143
|
-
- `workflow
|
|
138
|
+
- `workflow` for adaptive implementation orchestration
|
|
144
139
|
|
|
145
140
|
Invoke one inline with `$skill-name` (for example, `$grill-me`).
|
|
146
141
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const { createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition } = vi.hoisted(() => ({
|
|
4
|
+
createFindToolDefinition: vi.fn(() => ({ name: "find" })),
|
|
5
|
+
createGrepToolDefinition: vi.fn(() => ({ name: "grep" })),
|
|
6
|
+
createLsToolDefinition: vi.fn(() => ({ name: "ls" })),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock("@selesai/code", () => ({
|
|
10
|
+
createFindToolDefinition,
|
|
11
|
+
createGrepToolDefinition,
|
|
12
|
+
createLsToolDefinition,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
import enableReadonlyTools from "./enable-readonly-tools.ts";
|
|
16
|
+
|
|
17
|
+
function setup() {
|
|
18
|
+
let handler!: (event: { toolName: string; isError: boolean }, ctx: { cwd: string }) => void;
|
|
19
|
+
const active = new Set(["read", "bash", "edit", "write"]);
|
|
20
|
+
const pi = {
|
|
21
|
+
on: vi.fn((_event: string, registeredHandler: typeof handler) => {
|
|
22
|
+
handler = registeredHandler;
|
|
23
|
+
}),
|
|
24
|
+
registerTool: vi.fn((tool: { name: string }) => active.add(tool.name)),
|
|
25
|
+
};
|
|
26
|
+
enableReadonlyTools(pi as any);
|
|
27
|
+
return { active, handler, pi };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("enable-readonly-tools", () => {
|
|
31
|
+
it("does not activate readonly tools after failed or unrelated calls", () => {
|
|
32
|
+
const { active, handler, pi } = setup();
|
|
33
|
+
|
|
34
|
+
handler({ toolName: "read", isError: true }, { cwd: "/project" });
|
|
35
|
+
handler({ toolName: "extension-tool", isError: false }, { cwd: "/project" });
|
|
36
|
+
|
|
37
|
+
expect(pi.registerTool).not.toHaveBeenCalled();
|
|
38
|
+
expect([...active]).toEqual(["read", "bash", "edit", "write"]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it.each(["read", "bash", "edit", "write"])("activates once after a successful %s call", (toolName) => {
|
|
42
|
+
const { active, handler, pi } = setup();
|
|
43
|
+
|
|
44
|
+
handler({ toolName, isError: false }, { cwd: "/project" });
|
|
45
|
+
expect(pi.registerTool).toHaveBeenCalledTimes(3);
|
|
46
|
+
expect([...active]).toEqual(["read", "bash", "edit", "write", "grep", "find", "ls"]);
|
|
47
|
+
|
|
48
|
+
handler({ toolName: "bash", isError: false }, { cwd: "/other" });
|
|
49
|
+
expect(pi.registerTool).toHaveBeenCalledTimes(3);
|
|
50
|
+
expect(createGrepToolDefinition).toHaveBeenCalledWith("/project");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createFindToolDefinition,
|
|
3
|
+
createGrepToolDefinition,
|
|
4
|
+
createLsToolDefinition,
|
|
5
|
+
type ExtensionAPI,
|
|
6
|
+
} from "@selesai/code";
|
|
7
|
+
|
|
8
|
+
const bootstrapTools = new Set(["read", "bash", "edit", "write"]);
|
|
9
|
+
|
|
10
|
+
export default function (pi: ExtensionAPI) {
|
|
11
|
+
let enabled = false;
|
|
12
|
+
|
|
13
|
+
pi.on("tool_execution_end", (event, ctx) => {
|
|
14
|
+
if (enabled || event.isError || !bootstrapTools.has(event.toolName)) return;
|
|
15
|
+
enabled = true;
|
|
16
|
+
pi.registerTool(createGrepToolDefinition(ctx.cwd));
|
|
17
|
+
pi.registerTool(createFindToolDefinition(ctx.cwd));
|
|
18
|
+
pi.registerTool(createLsToolDefinition(ctx.cwd));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"./agent-browser.ts",
|
|
9
9
|
"./copy-turn.ts",
|
|
10
10
|
"./context-compaction-reminder.ts",
|
|
11
|
+
"./enable-readonly-tools.ts",
|
|
12
|
+
"./preview-tools-disabled.ts",
|
|
11
13
|
"./pi-intercom/index.ts",
|
|
12
14
|
"./ponytail/index.js",
|
|
13
15
|
"./question",
|
|
@@ -18,7 +20,6 @@
|
|
|
18
20
|
"./rtk.ts",
|
|
19
21
|
"./tokenin-onboarding.ts",
|
|
20
22
|
"./undo.ts",
|
|
21
|
-
"./workflow",
|
|
22
23
|
"./pi-subagents",
|
|
23
24
|
"./pi-web-agent",
|
|
24
25
|
"./web-agent-onboarding.ts",
|
|
@@ -26,25 +26,11 @@ export interface GuidePreferencesUpdate {
|
|
|
26
26
|
// or prompt from the root README without reproducing the README in the overlay.
|
|
27
27
|
export const GUIDE_FEATURES: readonly GuideFeature[] = [
|
|
28
28
|
{
|
|
29
|
-
id: "workflow
|
|
29
|
+
id: "workflow",
|
|
30
30
|
section: "Start here",
|
|
31
|
-
title: "
|
|
32
|
-
example: '
|
|
33
|
-
introducedIn: "0.
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: "workflow-quicktype",
|
|
37
|
-
section: "Start here",
|
|
38
|
-
title: "Quicktype",
|
|
39
|
-
example: '/workflow-quicktype "<goal>"',
|
|
40
|
-
introducedIn: "0.5.13",
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: "workflow-prototype",
|
|
44
|
-
section: "Start here",
|
|
45
|
-
title: "Prototype",
|
|
46
|
-
example: '/workflow-prototype "<goal>"',
|
|
47
|
-
introducedIn: "0.5.13",
|
|
31
|
+
title: "Workflow",
|
|
32
|
+
example: '"Use $workflow to orchestrate this implementation."',
|
|
33
|
+
introducedIn: "0.9.14",
|
|
48
34
|
},
|
|
49
35
|
{
|
|
50
36
|
id: "settings",
|
|
@@ -101,8 +87,7 @@ export const GUIDE_DISMISS_HINT = "Press any key to continue";
|
|
|
101
87
|
|
|
102
88
|
export const GUIDE_COMPACT_LINES = [
|
|
103
89
|
"/guide full · /settings",
|
|
104
|
-
'
|
|
105
|
-
'/workflow-prototype "<goal>"',
|
|
90
|
+
'"Use $workflow to orchestrate this implementation."',
|
|
106
91
|
'"Ask the researcher to research <topic>."',
|
|
107
92
|
"/skill:* · /undo · /handoff-new <next focus>",
|
|
108
93
|
] as const;
|
|
@@ -49,15 +49,14 @@ test("full and compact guide content expose README commands and examples", () =>
|
|
|
49
49
|
const compact = GUIDE_COMPACT_LINES.join("\n");
|
|
50
50
|
|
|
51
51
|
assert.match(full, /\/settings/);
|
|
52
|
-
assert.match(full,
|
|
53
|
-
assert.match(full, /\/workflow-prototype/);
|
|
52
|
+
assert.match(full, /\$workflow/);
|
|
54
53
|
assert.match(full, /Ask the architect to challenge this plan/);
|
|
55
54
|
assert.match(full, /Ask the researcher to research <topic> and cite sources/);
|
|
56
55
|
assert.match(full, /\/skill:\*/);
|
|
57
56
|
assert.match(full, /\/handoff-new Next: fix <problem>; start with a coding plan/);
|
|
58
57
|
assert.doesNotMatch(full, /question\(|\/intercom|powerline/);
|
|
59
58
|
assert.match(compact, /\/settings/);
|
|
60
|
-
assert.match(compact,
|
|
59
|
+
assert.match(compact, /\$workflow/);
|
|
61
60
|
assert.match(compact, /Ask the researcher to research <topic>/);
|
|
62
61
|
});
|
|
63
62
|
|
|
@@ -8,6 +8,7 @@ inheritProjectContext: true
|
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
skill: ponytail, planger
|
|
10
10
|
defaultContext: fork
|
|
11
|
+
output: plan.md
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
## Goal
|
|
@@ -20,7 +21,7 @@ Create implementation plans that can be executed by a small coding model with:
|
|
|
20
21
|
- Weak architectural understanding
|
|
21
22
|
- No ability to infer missing steps
|
|
22
23
|
|
|
23
|
-
Assume the executor only knows what is written in the plan. Return the complete plan in your final response
|
|
24
|
+
Assume the executor only knows what is written in the plan. Return the complete plan in your final response. The runtime persists it as `plan.md` so the next workflow stage can read it.
|
|
24
25
|
|
|
25
26
|
# Core Principles
|
|
26
27
|
|
|
@@ -9,9 +9,11 @@ inheritSkills: false
|
|
|
9
9
|
skill: ponytail, implanger
|
|
10
10
|
inheritProjectContext: true
|
|
11
11
|
defaultContext: fresh
|
|
12
|
+
output: implementation.md
|
|
13
|
+
defaultReads: context.md, research.md, plan.md, implementation.md, review.md
|
|
12
14
|
---
|
|
13
15
|
|
|
14
|
-
You are `builder`, the sole writer for the delegated task. The main agent and user remain the decision authority.
|
|
16
|
+
You are `builder`, the sole writer for the delegated task. The main agent and user remain the decision authority. The runtime persists your final report as `implementation.md` for review and fix stages.
|
|
15
17
|
|
|
16
18
|
Read the supplied task, artifacts, and relevant code before changing anything. Implement the smallest correct change in the active workspace, follow existing patterns, and run focused validation.
|
|
17
19
|
|
|
@@ -8,11 +8,13 @@ inheritProjectContext: true
|
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
defaultContext: fresh
|
|
10
10
|
skill: ponytail, planger
|
|
11
|
+
output: review.md
|
|
12
|
+
defaultReads: context.md, research.md, plan.md, implementation.md
|
|
11
13
|
completionGuard: false
|
|
12
14
|
acceptanceRole: read-only
|
|
13
15
|
---
|
|
14
16
|
|
|
15
|
-
You are a review-only subagent. Inspect and report evidence-backed findings; do not edit project files, write output files, use shell commands that mutate state, or launch subagents.
|
|
17
|
+
You are a review-only subagent. Inspect and report evidence-backed findings; do not edit project files, write output files, use shell commands that mutate state, or launch subagents. The runtime persists your final report as `review.md` for a scoped fix stage.
|
|
16
18
|
|
|
17
19
|
Review the supplied target directly. If the task names a progress file, read it first and scope your review to its latest round entry: inspect the diff restricted to the files that entry lists (`git diff -- <files>`). Older entries are already reviewed—re-inspect only files the latest entry repeats. If no progress file is named, or it is missing or empty, review the full uncommitted diff. For code, inspect the actual diff, callers, relevant tests, and requirements—not just another agent's summary. Use `bash` only for read-only inspection or test commands.
|
|
18
20
|
|
|
@@ -7,10 +7,11 @@ inheritProjectContext: true
|
|
|
7
7
|
inheritSkills: false
|
|
8
8
|
skill: ponytail
|
|
9
9
|
defaultContext: fresh
|
|
10
|
+
output: context.md
|
|
10
11
|
acceptanceRole: read-only
|
|
11
12
|
---
|
|
12
13
|
|
|
13
|
-
You are a codebase reconnaissance subagent. Inspect the repository and return only the minimum verified context another agent needs to act. Do not edit project files
|
|
14
|
+
You are a codebase reconnaissance subagent. Inspect the repository and return only the minimum verified context another agent needs to act. Do not edit project files or launch subagents. The runtime persists your final response as `context.md` for the next stage.
|
|
14
15
|
|
|
15
16
|
Use targeted `grep`, `find`, `ls`, and `read`. Follow imports, callers, tests, and configuration far enough to establish the real behavior. Do not guess.
|
|
16
17
|
|
|
@@ -7,10 +7,11 @@ inheritProjectContext: true
|
|
|
7
7
|
inheritSkills: false
|
|
8
8
|
skill: ponytail
|
|
9
9
|
defaultContext: fork
|
|
10
|
+
output: handoff.md
|
|
10
11
|
acceptanceRole: read-only
|
|
11
12
|
---
|
|
12
13
|
|
|
13
|
-
Create a concise, self-contained handoff for a fresh agent. Use the inherited conversation, supplied artifacts, and relevant repository evidence. Do not edit project files
|
|
14
|
+
Create a concise, self-contained handoff for a fresh agent. Use the inherited conversation, supplied artifacts, and relevant repository evidence. Do not edit project files or launch subagents. The runtime persists your final response as `handoff.md`.
|
|
14
15
|
|
|
15
16
|
Do not duplicate plans, ADRs, issues, commits, diffs, or other artifacts: reference them by exact path or URL. Redact secrets and personal data. If the task names a next focus, tailor the handoff to it.
|
|
16
17
|
|
|
@@ -12,7 +12,7 @@ defaultProgress: true
|
|
|
12
12
|
|
|
13
13
|
You are a research subagent.
|
|
14
14
|
|
|
15
|
-
Given a question or topic, run focused web research and produce a concise, well-sourced brief that answers the question directly.
|
|
15
|
+
Given a question or topic, run focused web research and produce a concise, well-sourced brief that answers the question directly. The runtime persists the final response as `research.md` for the next stage.
|
|
16
16
|
|
|
17
17
|
Working rules:
|
|
18
18
|
- Break the problem into 2-4 distinct research angles.
|
|
@@ -6,9 +6,11 @@ thinking: high
|
|
|
6
6
|
systemPromptMode: replace
|
|
7
7
|
inheritProjectContext: true
|
|
8
8
|
inheritSkills: false
|
|
9
|
+
output: review.md
|
|
10
|
+
defaultReads: context.md, research.md, plan.md, implementation.md
|
|
9
11
|
---
|
|
10
12
|
|
|
11
|
-
You are a disciplined review subagent. Your job is to inspect, evaluate, and report findings with evidence. You do not guess; you verify from the code, tests, docs, or requirements.
|
|
13
|
+
You are a disciplined review subagent. Your job is to inspect, evaluate, and report findings with evidence. You do not guess; you verify from the code, tests, docs, or requirements. The runtime persists your final report as `review.md` for a scoped fix stage.
|
|
12
14
|
|
|
13
15
|
## Review types you handle
|
|
14
16
|
|
|
@@ -8,7 +8,8 @@ inheritProjectContext: true
|
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
tools: read, grep, find, ls, bash, edit, write, contact_supervisor
|
|
10
10
|
defaultContext: fork
|
|
11
|
-
|
|
11
|
+
output: implementation.md
|
|
12
|
+
defaultReads: context.md, research.md, plan.md, implementation.md, review.md
|
|
12
13
|
defaultProgress: true
|
|
13
14
|
---
|
|
14
15
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import disablePreviewTools from "./preview-tools-disabled.ts";
|
|
4
|
+
|
|
5
|
+
function setup(active: string[]) {
|
|
6
|
+
let handler!: () => void;
|
|
7
|
+
const getActiveTools = vi.fn(() => active);
|
|
8
|
+
const setActiveTools = vi.fn();
|
|
9
|
+
const pi = {
|
|
10
|
+
on: vi.fn((_event: string, registeredHandler: () => void) => {
|
|
11
|
+
handler = registeredHandler;
|
|
12
|
+
}),
|
|
13
|
+
getActiveTools,
|
|
14
|
+
setActiveTools,
|
|
15
|
+
};
|
|
16
|
+
disablePreviewTools(pi as any);
|
|
17
|
+
return { handler, getActiveTools, setActiveTools };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("preview-tools-disabled", () => {
|
|
21
|
+
it("keeps a session without preview_export unchanged", () => {
|
|
22
|
+
const { handler, getActiveTools, setActiveTools } = setup(["read", "bash", "edit", "write"]);
|
|
23
|
+
|
|
24
|
+
handler();
|
|
25
|
+
|
|
26
|
+
expect(getActiveTools).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(setActiveTools).not.toHaveBeenCalled();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("removes preview_export and keeps every other active tool", () => {
|
|
31
|
+
const { handler, setActiveTools } = setup(["read", "bash", "edit", "write", "preview_export", "web_explore"]);
|
|
32
|
+
|
|
33
|
+
handler();
|
|
34
|
+
|
|
35
|
+
expect(setActiveTools).toHaveBeenCalledWith(["read", "bash", "edit", "write", "web_explore"]);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@selesai/code";
|
|
2
|
+
|
|
3
|
+
const DISABLED_TOOL_NAMES = new Set(["preview_export"]);
|
|
4
|
+
|
|
5
|
+
export default function (pi: ExtensionAPI) {
|
|
6
|
+
pi.on("session_start", () => {
|
|
7
|
+
const active = pi.getActiveTools();
|
|
8
|
+
const next = active.filter((name) => !DISABLED_TOOL_NAMES.has(name));
|
|
9
|
+
if (next.length !== active.length) pi.setActiveTools(next);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -70,7 +70,7 @@ export function prepareQuestions(value: unknown): { questions: PreparedQuestion[
|
|
|
70
70
|
...common,
|
|
71
71
|
type: raw.type,
|
|
72
72
|
options: preparedOptions.options,
|
|
73
|
-
allowOther: raw.allowOther ??
|
|
73
|
+
allowOther: raw.allowOther ?? true,
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
return { questions };
|
|
@@ -20,14 +20,14 @@ const SelectQuestionSchema = Type.Object({
|
|
|
20
20
|
...QuestionBase,
|
|
21
21
|
type: StringEnum(["select"] as const),
|
|
22
22
|
options: Type.Array(OptionSchema, { minItems: 1, description: "Available choices" }),
|
|
23
|
-
allowOther: Type.Optional(Type.Boolean({ description: "Allow a custom Other text answer" })),
|
|
23
|
+
allowOther: Type.Optional(Type.Boolean({ description: "Allow a custom Other text answer (default: true)" })),
|
|
24
24
|
}, { additionalProperties: false });
|
|
25
25
|
|
|
26
26
|
const MultiSelectQuestionSchema = Type.Object({
|
|
27
27
|
...QuestionBase,
|
|
28
28
|
type: StringEnum(["multiselect"] as const),
|
|
29
29
|
options: Type.Array(OptionSchema, { minItems: 1, description: "Available choices" }),
|
|
30
|
-
allowOther: Type.Optional(Type.Boolean({ description: "Allow custom Other text alongside selected values" })),
|
|
30
|
+
allowOther: Type.Optional(Type.Boolean({ description: "Allow custom Other text alongside selected values (default: true)" })),
|
|
31
31
|
}, { additionalProperties: false });
|
|
32
32
|
|
|
33
33
|
const TextQuestionSchema = Type.Object({
|
|
@@ -22,7 +22,7 @@ describe("question batch helpers", () => {
|
|
|
22
22
|
{ value: "red", label: "Red", description: undefined },
|
|
23
23
|
{ value: "blue", label: "Blue", description: undefined },
|
|
24
24
|
],
|
|
25
|
-
allowOther:
|
|
25
|
+
allowOther: true,
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
id: "notes",
|
|
@@ -70,7 +70,7 @@ describe("question batch helpers", () => {
|
|
|
70
70
|
context: "ctx",
|
|
71
71
|
type: "multiselect",
|
|
72
72
|
options: [{ value: "a", label: "A", description: "desc" }],
|
|
73
|
-
allowOther:
|
|
73
|
+
allowOther: true,
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
76
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workflow
|
|
3
|
+
description: Adaptively orchestrate non-trivial implementation work with Selesai subagents. Use when the user asks to use a workflow, orchestrate a task, run an implementation/review/fix loop, or coordinate architecture, workers, and reviewers. Choose only the necessary stages; do not use static slash-command flows.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Adaptive Engineering Workflow
|
|
7
|
+
|
|
8
|
+
This is a parent-only policy layer over `pi-subagents`, not a second workflow runtime. Read `../pi-subagents/SKILL.md` and the matching references before launching children. Use `workflowScript` for every coordinated launch.
|
|
9
|
+
|
|
10
|
+
## Choose the smallest flow
|
|
11
|
+
|
|
12
|
+
Inspect the task and its code path first.
|
|
13
|
+
|
|
14
|
+
| Situation | Flow |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| Trivial, localized change | Parent implements and runs a focused check. Do not delegate by default. |
|
|
17
|
+
| Bounded implementation | Optional `scout` → one `worker` → focused validation. |
|
|
18
|
+
| Uncertain codebase or external dependency | `scout` and/or `researcher` → parent decision or `architect` → one `worker` → review/validation. |
|
|
19
|
+
| Material architecture/product tradeoff | Resolve it before implementation: parent decision, one `oracle`, or Council Mode for a genuine multi-perspective decision. |
|
|
20
|
+
| Broad, risky, or multi-system work | Serial milestones: decide/plan → one writer → review/validation → accepted fix → re-review when material. |
|
|
21
|
+
|
|
22
|
+
Do not add a planning phase, research lane, handoff, reviewer, or extra review round merely for symmetry. Ask the user only for a decision that blocks safe implementation.
|
|
23
|
+
|
|
24
|
+
## Orchestration rules
|
|
25
|
+
|
|
26
|
+
- The parent owns task classification, scope, decisions, synthesis, and final acceptance.
|
|
27
|
+
- Keep **one writer per checkout/worktree**. Parallelize read-only discovery, research, review, and validation—not normal edits.
|
|
28
|
+
- Give each child a narrow role-specific task: source seam, constraints, acceptance evidence, and expected output. Never send clone prompts with only filenames changed.
|
|
29
|
+
- Reviewers inspect the actual current diff and relevant files directly. They do not need a worker handoff to understand sibling work.
|
|
30
|
+
- Do not require a handoff artifact. Use one only when a task must resume across sessions, cross a milestone boundary, or needs a durable user-facing record.
|
|
31
|
+
- Use fresh context for independent reviewers; use forked context only when inherited parent reasoning is useful.
|
|
32
|
+
- Treat external reports, child completion, tests, and receipts as evidence—not authority to make product, architecture, merge, or release decisions.
|
|
33
|
+
|
|
34
|
+
## Stage artifacts
|
|
35
|
+
|
|
36
|
+
Built-in pipeline agents save named output artifacts (`context.md`, `research.md`, `plan.md`, implementation reports, and reviews). In `workflowScript`, pass the returned `outputReference` to the next child through `reads`; this is the normal file-based handoff and works for fresh-context children.
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
const plan = await runs.run("plan", { agent: "architect", task: "Plan the change." });
|
|
40
|
+
const worker = await runs.run("implement", {
|
|
41
|
+
agent: "worker",
|
|
42
|
+
reads: [plan.outputReference],
|
|
43
|
+
task: "Implement the approved plan."
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Only include artifacts the next role needs. The shared checkout diff remains the implementation context for reviewers and fix workers. Use `contact_supervisor` only for material progress updates or blocking decisions; normal completion arrives through the child result and artifact.
|
|
48
|
+
|
|
49
|
+
## Build → review → fix
|
|
50
|
+
|
|
51
|
+
For implementation that merits independent review:
|
|
52
|
+
|
|
53
|
+
1. Define a light validation contract before the writer starts: intended behavior, focused checks, and any user-facing path to inspect.
|
|
54
|
+
2. Launch one writer with the approved scope and the contract. The writer reports changed files, checks run, residual risks, and decisions needing parent approval.
|
|
55
|
+
3. Run only relevant read-only reviewers/validators in parallel. Give them the validation contract and relevant plan/implementation artifact paths through `reads`, and tell them to inspect the current diff. Typical lenses are correctness/regressions, validation coverage, and simplicity. Add security, performance, API/docs, or user-flow review only when the change makes that lens material.
|
|
56
|
+
4. Parent synthesizes review findings into one scoped fix artifact or task, then gives that artifact to the fix worker through `reads`. The fix worker also inspects the shared diff; it does not need sibling transcripts.
|
|
57
|
+
5. Parent classifies findings:
|
|
58
|
+
- **blocking/actionable in approved scope** → synthesize and send one fix worker;
|
|
59
|
+
- **optional, speculative, or scope-expanding** → defer or ask the user;
|
|
60
|
+
- **decision needed** → stop and escalate to the user.
|
|
61
|
+
6. Re-review only after a material fix. Stop when no actionable blockers remain, focused validation is sufficient, an approval is needed, or the review-round cap is reached. Default cap: two review rounds; use three only for risky work.
|
|
62
|
+
7. Parent inspects the final diff and validation evidence before reporting completion.
|
|
63
|
+
|
|
64
|
+
Use `workflowScript` with top-level `await`, `runs.run` for ordered stages, and `runs.all` for independent read-only fanout. Do not use legacy `chain`/`tasks` fields or nested async functions. Keep async work background by default; do not wait or poll merely to watch it.
|
|
65
|
+
|
|
66
|
+
## Minimum workflow shape
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
const plan = await runs.run("plan", {
|
|
70
|
+
agent: "architect",
|
|
71
|
+
task: "Plan the requested change. Return files, implementation order, acceptance checks, and non-goals."
|
|
72
|
+
});
|
|
73
|
+
const worker = await runs.run("implement", {
|
|
74
|
+
agent: "worker",
|
|
75
|
+
reads: [plan.outputReference],
|
|
76
|
+
task: "Implement the approved plan. Return changed files, checks run, residual risks, and decisions needing approval."
|
|
77
|
+
});
|
|
78
|
+
const reviews = await runs.all([
|
|
79
|
+
{ key: "correctness", agent: "reviewer", reads: [plan.outputReference, worker.outputReference], task: "Inspect the current diff for correctness and regressions. Do not edit." },
|
|
80
|
+
{ key: "simplicity", agent: "reviewer", reads: [plan.outputReference, worker.outputReference], task: "Inspect the current diff for unnecessary complexity. Do not edit." }
|
|
81
|
+
]);
|
|
82
|
+
return { plan: plan.outputReference, worker: worker.outputReference, reviews: reviews.map(result => result.outputReference) };
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The parent reads and synthesizes that result before launching any fix worker. Pass the synthesized finding list in that fix worker's task. Do not encode an unbounded auto-loop in a slash command or a child prompt.
|
package/docs/workflows.md
CHANGED
|
@@ -1,57 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Adaptive implementation workflow
|
|
2
2
|
|
|
3
|
-
Selesai
|
|
3
|
+
Selesai uses the built-in [`workflow`](../src/skills/workflow/SKILL.md) skill for implementation orchestration. It is guidance for the parent agent, built on `pi-subagents`' `workflowScript` runtime; it is not a separate extension, slash-command system, or durable workflow engine.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Invoke it inline with `$workflow`, or ask Selesai to orchestrate the implementation.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
/workflow-task <goal>
|
|
9
|
-
/workflow-prototype <goal>
|
|
10
|
-
/workflow-quicktype <goal>
|
|
11
|
-
/workflow-loop <goal>
|
|
12
|
-
```
|
|
7
|
+
## Policy
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
The parent first inspects the task and chooses the smallest flow that can safely finish it:
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
- localized changes stay local or use one worker and focused validation;
|
|
12
|
+
- uncertain code or external dependencies add scoped discovery/research;
|
|
13
|
+
- architecture or product tradeoffs are settled before implementation, with an oracle or Council Mode only when warranted;
|
|
14
|
+
- substantial work uses one writer, independent read-only review/validation, a scoped fix worker for accepted findings, and another review only after material changes.
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
| --- | --- |
|
|
20
|
-
| `/workflow-task` | plan → reuse → handoff → build/review/fix loop |
|
|
21
|
-
| `/workflow-prototype` | parallel research + codebase exploration → plan → handoff → build/review/fix loop → audit |
|
|
22
|
-
| `/workflow-quicktype` | plan → reuse → handoff → build/review/fix loop → audit |
|
|
23
|
-
| `/workflow-loop` | direct build/review/fix loop for an already-agreed plan |
|
|
16
|
+
Reviewers inspect the shared checkout's actual diff. Sibling transcripts are not shared automatically, so the parent passes a concise synthesis to a later fix worker. Handoffs are optional and reserved for cross-session continuity, milestone boundaries, or durable records.
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## Extending workflows
|
|
28
|
-
|
|
29
|
-
The extension seam is `src/extensions/workflow/modes.ts`.
|
|
30
|
-
|
|
31
|
-
Add one `WorkflowMode` entry to `WORKFLOW_MODES`:
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
{
|
|
35
|
-
command: "workflow-rigorous",
|
|
36
|
-
description: "Run the rigorous workflow.",
|
|
37
|
-
launch: (goal) => ({
|
|
38
|
-
workflowScript: `const goal = ${JSON.stringify(goal)};
|
|
39
|
-
return runs.run("plan", { agent: "architect", task: "Plan: " + goal });`,
|
|
40
|
-
}),
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
`launch(goal)` returns pi-subagents public execution fields. Prefer the native execution shapes where the mode is static:
|
|
45
|
-
|
|
46
|
-
- `chain` for a fixed ordered sequence, including human checkpoints.
|
|
47
|
-
- `tasks` for independent, read-only parallel work.
|
|
48
|
-
- `workflowScript` only when the orchestration is conditional, iterative, needs dynamic fan-out, or combines native run operations.
|
|
49
|
-
|
|
50
|
-
`extension.ts` automatically registers every entry in `WORKFLOW_MODES`; no new command plumbing is needed. The mode owns task wording and execution shape. The extension owns only argument validation, async launch, agent scope, and mission creation.
|
|
51
|
-
|
|
52
|
-
## Constraints
|
|
53
|
-
|
|
54
|
-
- `workflowScript`, `chain`, and `tasks` are alternative top-level pi-subagents execution modes. A mode that needs an auto-loop and preceding/following phases should use `workflowScript` and call `runs.run` / `runs.all` within it.
|
|
55
|
-
- Keep one writer at a time. Parallel lanes should be research or review unless they are isolated in worktrees.
|
|
56
|
-
- Workflow progress ledgers are under `.pi-subagents/progress/` and are local runtime artifacts, not durable workflow state.
|
|
57
|
-
- The outer mission and pi-subagents run artifacts are the recovery record.
|
|
18
|
+
`pi-subagents` missions and receipts provide recovery evidence for delegated work. They do not turn a static flow into the source of truth or replace parent acceptance.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selesai/code",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.14",
|
|
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": {
|
|
@@ -52,13 +52,14 @@
|
|
|
52
52
|
"clean": "shx rm -rf dist",
|
|
53
53
|
"dev": "tsx src/cli.ts",
|
|
54
54
|
"dev:print": "tsx src/cli.ts --print",
|
|
55
|
-
"test": "vitest run src/extensions/undo.test.ts src/extensions/custom-provider-ollama/index.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/
|
|
56
|
-
"test:coverage": "vitest run --coverage src/extensions/undo.test.ts src/extensions/custom-provider-ollama/index.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/
|
|
55
|
+
"test": "vitest run src/extensions/undo.test.ts src/extensions/custom-provider-ollama/index.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/model-registry-defaults.test.ts src/cli/args.test.ts",
|
|
56
|
+
"test:coverage": "vitest run --coverage src/extensions/undo.test.ts src/extensions/custom-provider-ollama/index.test.ts src/extensions/agent-browser.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/model-prompt-injector/index.test.ts src/extensions/web-agent-onboarding.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/model-registry-defaults.test.ts src/cli/args.test.ts",
|
|
57
57
|
"prepare": "npm run build",
|
|
58
58
|
"build": "npm run clean && tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets",
|
|
59
59
|
"copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/ && shx mkdir -p dist/defaults && shx cp src/defaults/* dist/defaults/ && shx mkdir -p dist/extensions && node scripts/copy-extensions.mjs && shx mkdir -p dist/themes && shx cp -r src/themes/. dist/themes/ && shx mkdir -p dist/skills && shx cp -r src/skills/. dist/skills/"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
+
"acorn": "8.18.0",
|
|
62
63
|
"@ast-grep/cli": "0.45.0",
|
|
63
64
|
"@earendil-works/pi-agent-core": "0.84.3",
|
|
64
65
|
"@earendil-works/pi-ai": "0.84.3",
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// ponytail: thin slash-command adapter for the pi-subagents workflow runtime.
|
|
2
|
-
|
|
3
|
-
import type { ExtensionAPI } from "@selesai/code";
|
|
4
|
-
import { launchSlashSubagent } from "../pi-subagents/src/slash/slash-commands.ts";
|
|
5
|
-
import { WORKFLOW_MODES, type WorkflowMode } from "./modes.ts";
|
|
6
|
-
|
|
7
|
-
export default function workflowModesExtension(pi: ExtensionAPI): void {
|
|
8
|
-
const register = (mode: WorkflowMode) => pi.registerCommand(mode.command, {
|
|
9
|
-
description: mode.description,
|
|
10
|
-
handler: async (args, ctx) => {
|
|
11
|
-
const goal = args.trim();
|
|
12
|
-
if (!goal) {
|
|
13
|
-
ctx.ui.notify(`${mode.description}\nUsage: /${mode.command} <goal>`, "info");
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
launchSlashSubagent(pi, ctx, {
|
|
17
|
-
...mode.launch(goal),
|
|
18
|
-
async: true,
|
|
19
|
-
agentScope: "both",
|
|
20
|
-
mission: { title: goal },
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
for (const mode of WORKFLOW_MODES) register(mode);
|
|
26
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// ponytail: workflow mode registry over pi-subagents' public workflowScript seam.
|
|
2
|
-
// A mode returns launch parameters; the extension owns slash-command plumbing.
|
|
3
|
-
|
|
4
|
-
import type { SubagentParamsLike } from "../pi-subagents/src/runs/foreground/subagent-executor.ts";
|
|
5
|
-
|
|
6
|
-
export interface WorkflowMode {
|
|
7
|
-
command: string;
|
|
8
|
-
description: string;
|
|
9
|
-
launch(goal: string): Pick<SubagentParamsLike, "workflowScript" | "chain" | "tasks" | "concurrency">;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function js(value: string): string {
|
|
13
|
-
return JSON.stringify(value);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// A loop depends on the previous review's output, so it belongs in pi-subagents'
|
|
17
|
-
// scripted workflow runtime rather than a fixed native chain.
|
|
18
|
-
const AUTO_LOOP = String.raw`
|
|
19
|
-
const autoLoop = async (goal, context, progressFile) => {
|
|
20
|
-
emit({ phase: 'start', goal });
|
|
21
|
-
let round = 1;
|
|
22
|
-
let previousReview = '';
|
|
23
|
-
let completed = 0;
|
|
24
|
-
while (true) {
|
|
25
|
-
try {
|
|
26
|
-
const build = await runs.run('build-' + round, {
|
|
27
|
-
agent: 'builder',
|
|
28
|
-
timeoutMs: 45 * 60 * 1000,
|
|
29
|
-
task: 'Implement the next bounded step of the approved work in the workspace and run relevant checks. Work on one small, self-contained slice this round; do not attempt the whole plan.\n\nSource of truth (handoff/plan):\n' + context + '\n\nGoal:\n' + goal + (round > 1 ? '\n\nPrevious review (its findings were addressed in the fix round; use its "Remaining work" notes to pick your next slice, do not re-apply findings):\n' + previousReview : '') + '\n\nRead the progress file first for prior-round context.\n\nProgress ledger: append a "## Round ' + round + '" entry to the progress file at ' + progressFile + ' before finishing. List every file you changed and a short summary of the work.',
|
|
30
|
-
});
|
|
31
|
-
const review = await runs.run('review-' + round, {
|
|
32
|
-
agent: 'commentator',
|
|
33
|
-
timeoutMs: 15 * 60 * 1000,
|
|
34
|
-
task: 'Independently review the builder work for this round and report concrete evidence (what you inspected and what you ran). Do not modify the workspace.\n\nAcceptance criteria (source of truth):\n' + context + '\n\nProgress file (scope your review to its latest round entry; also re-check the files from the immediately preceding fix entry if one exists; fall back to the full uncommitted diff if it is missing or empty):\n' + progressFile + '\n\nBuilder completion summary:\n' + build.output + '\n\nIf the plan is not yet complete, add a "Remaining work:" section listing the next concrete step(s). End with exactly one line: WORKFLOW_REVIEW_STATUS: clean OR WORKFLOW_REVIEW_STATUS: blocking.',
|
|
35
|
-
});
|
|
36
|
-
const hasRemainingWork = /Remaining work\s*:\s*\S/i.test(review.output);
|
|
37
|
-
if (/WORKFLOW_REVIEW_STATUS\s*:\s*clean/i.test(review.output) && !hasRemainingWork) {
|
|
38
|
-
return { result: 'clean', rounds: completed + 1 };
|
|
39
|
-
}
|
|
40
|
-
previousReview = review.output;
|
|
41
|
-
await runs.run('fix-' + round, {
|
|
42
|
-
agent: 'builder',
|
|
43
|
-
timeoutMs: 45 * 60 * 1000,
|
|
44
|
-
task: 'Address ONLY the findings from the review below. The "Remaining work:" section (if present) is for the next round; do not act on it. If the review is clean but has Remaining work, make no changes and record that fact.\n\nProgress ledger: append a "## Round ' + round + ' fix" entry to the progress file at ' + progressFile + ' before finishing. List every file you changed and a short summary of the fixes.\n\nReviewer findings:\n' + review.output,
|
|
45
|
-
});
|
|
46
|
-
completed += 1;
|
|
47
|
-
round += 1;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
-
if (/Run fan-out limit reached/i.test(message)) {
|
|
51
|
-
return { result: 'budget', rounds: completed, note: 'Run fan-out budget exhausted before the goal was reached. The progress file is current.' };
|
|
52
|
-
}
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
};`;
|
|
57
|
-
|
|
58
|
-
const PROGRESS_DIR = ".pi-subagents/progress/";
|
|
59
|
-
|
|
60
|
-
export function buildLoopScript(goal: string): string {
|
|
61
|
-
return String.raw`const goal = ${js(goal)};
|
|
62
|
-
${AUTO_LOOP}
|
|
63
|
-
return await autoLoop(goal, goal, ${js(PROGRESS_DIR + "loop.md")});`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function buildTaskScript(goal: string): string {
|
|
67
|
-
return String.raw`const goal = ${js(goal)};
|
|
68
|
-
const plan = await runs.run('plan', { agent: 'architect', task: 'Produce a concrete implementation plan for: ' + goal + '. Cover what to build, how, in what order, which files and components, and the finished result. Return inline.' });
|
|
69
|
-
const reuse = await runs.run('reuse', { agent: 'explorer', task: 'Explore the codebase for reusable patterns relevant to: ' + plan.output + '. Point at relevant areas and dependencies; skip cleanly if wholly new. Return inline.' });
|
|
70
|
-
const handoff = await runs.run('handoff', { agent: 'recapper', task: 'Compile a self-contained handoff from the plan and reuse findings so fresh agents understand the goal, constraints, and acceptance criteria without re-planning.\n\nPlan:\n' + plan.output + '\n\nReuse findings:\n' + reuse.output + '\n\nReturn inline.' });
|
|
71
|
-
${AUTO_LOOP}
|
|
72
|
-
return await autoLoop(goal, handoff.output, ${js(PROGRESS_DIR + "task.md")});`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function buildPrototypeScript(goal: string): string {
|
|
76
|
-
return String.raw`const goal = ${js(goal)};
|
|
77
|
-
const discovery = await runs.all([
|
|
78
|
-
{ key: 'research', agent: 'researcher', task: 'Research the external, fast-changing knowledge this task depends on (libraries, SDKs, APIs, unfamiliar alternatives). Task: ' + goal + '. Synthesize actionable findings with sources. Return inline.' },
|
|
79
|
-
{ key: 'explore', agent: 'explorer', task: 'Map existing code, dependencies, and reusable patterns relevant to: ' + goal + '. Return inline.' },
|
|
80
|
-
]);
|
|
81
|
-
const research = discovery.find(result => result.key === 'research');
|
|
82
|
-
const reuse = discovery.find(result => result.key === 'explore');
|
|
83
|
-
const plan = await runs.run('plan', { agent: 'architect', task: 'Produce a concrete build plan from the research and codebase findings.\n\nResearch:\n' + research.output + '\n\nCodebase findings:\n' + reuse.output + '\n\nRequest:\n' + goal + '\n\nReturn inline.' });
|
|
84
|
-
const handoff = await runs.run('handoff', { agent: 'recapper', task: 'Compile a self-contained handoff from the plan and reuse findings.\n\nPlan:\n' + plan.output + '\n\nReuse:\n' + reuse.output + '\n\nReturn inline.' });
|
|
85
|
-
${AUTO_LOOP}
|
|
86
|
-
const loop = await autoLoop(goal, handoff.output, ${js(PROGRESS_DIR + "prototype.md")});
|
|
87
|
-
const audit = await runs.run('audit', { agent: 'commentator', task: 'Final audit of the uncommitted changes for correctness, plan adherence, and over-engineering (cut bloat, dead flexibility, reinvented stdlib). Plan:\n' + plan.output + '\n\nReport concrete evidence. Do not modify the workspace.' });
|
|
88
|
-
return { ...loop, audited: true };`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function buildQuicktypeScript(goal: string): string {
|
|
92
|
-
return String.raw`const goal = ${js(goal)};
|
|
93
|
-
const plan = await runs.run('plan', { agent: 'architect', task: 'Produce a concrete build plan for: ' + goal + '. Cover what to build, how, in what order, which components, and the finished result. Return inline.' });
|
|
94
|
-
const reuse = await runs.run('reuse', { agent: 'explorer', task: 'Explore the codebase for reusable patterns relevant to: ' + plan.output + '. Return inline.' });
|
|
95
|
-
const handoff = await runs.run('handoff', { agent: 'recapper', task: 'Compile a self-contained handoff from the plan and reuse findings.\n\nPlan:\n' + plan.output + '\n\nReuse:\n' + reuse.output + '\n\nReturn inline.' });
|
|
96
|
-
${AUTO_LOOP}
|
|
97
|
-
const loop = await autoLoop(goal, handoff.output, ${js(PROGRESS_DIR + "quicktype.md")});
|
|
98
|
-
const audit = await runs.run('audit', { agent: 'commentator', task: 'Final audit of the uncommitted changes for correctness, plan adherence, and over-engineering (cut bloat, dead flexibility, reinvented stdlib). Plan:\n' + plan.output + '\n\nReport concrete evidence. Do not modify the workspace.' });
|
|
99
|
-
return { ...loop, audited: true };`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export const WORKFLOW_MODES: readonly WorkflowMode[] = [
|
|
103
|
-
{ command: "workflow-task", description: "Run the task workflow (plan → reuse → handoff → build/review/fix loop).", launch: (goal) => ({ workflowScript: buildTaskScript(goal) }) },
|
|
104
|
-
{ command: "workflow-prototype", description: "Run the prototype workflow (parallel research/reuse → plan → handoff → loop → audit).", launch: (goal) => ({ workflowScript: buildPrototypeScript(goal) }) },
|
|
105
|
-
{ command: "workflow-quicktype", description: "Run the quicker prototype workflow (plan → reuse → handoff → loop → audit).", launch: (goal) => ({ workflowScript: buildQuicktypeScript(goal) }) },
|
|
106
|
-
{ command: "workflow-loop", description: "Run a direct build/review/fix loop for an already-agreed plan.", launch: (goal) => ({ workflowScript: buildLoopScript(goal) }) },
|
|
107
|
-
];
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@selesai/workflow",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"private": true,
|
|
5
|
-
"description": "Selesai workflow engine + modes (prototype, quick). Loaded as a bundled pi extension package.",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"pi": {
|
|
8
|
-
"extensions": [
|
|
9
|
-
"./extension.ts"
|
|
10
|
-
]
|
|
11
|
-
},
|
|
12
|
-
"peerDependencies": {
|
|
13
|
-
"@selesai/code": "*",
|
|
14
|
-
"@earendil-works/pi-tui": "*",
|
|
15
|
-
"typebox": "*"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: workflow-creation
|
|
3
|
-
description: Creates durable Selesai workflow modes. Use when a user asks to create or change a workflow mode, phased agent flow, or slash-command workflow.
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Durable Workflows
|
|
8
|
-
|
|
9
|
-
**Durable** means `workflow.json`, not session history, is the run authority. Build on the shared engine in `src/extensions/workflow/`; a mode is configuration, not a second orchestrator.
|
|
10
|
-
|
|
11
|
-
## 1. Choose the smallest fit
|
|
12
|
-
|
|
13
|
-
Read `docs/workflows.md`, every file in `src/extensions/workflow/modes/`, and the relevant workflow tests.
|
|
14
|
-
|
|
15
|
-
- Reuse `prototype`, `quick`, or `task` when its phase graph and terminal gate fit; change only its prompts/configuration.
|
|
16
|
-
- Add a mode only for a materially different graph, artifact contract, or close gate.
|
|
17
|
-
|
|
18
|
-
Done when the request is mapped to one existing mode or a named new mode with its phase list and terminal artifact.
|
|
19
|
-
|
|
20
|
-
## 2. Trace the durable seam
|
|
21
|
-
|
|
22
|
-
Before changing engine-facing behavior, read completely:
|
|
23
|
-
|
|
24
|
-
- `state-machine.ts` — graph, artifact gates, terminal-ready, completion;
|
|
25
|
-
- `adapter.ts` — tools, event handlers, persistence, reload guards;
|
|
26
|
-
- `run-state.ts` — canonical record and resume validation;
|
|
27
|
-
- `extension.ts` — single extension mounting all modes.
|
|
28
|
-
|
|
29
|
-
Done when every proposed behavior has one owner: state machine, shared adapter, or mode configuration.
|
|
30
|
-
|
|
31
|
-
## 3. Implement the mode
|
|
32
|
-
|
|
33
|
-
For a new mode, add `src/extensions/workflow/modes/<name>.ts`, modeled on `quick.ts`, with only `WorkflowConfig` and `WorkflowModeRegistration`:
|
|
34
|
-
|
|
35
|
-
- ordered phases, phase artifacts, prompts, validators, close artifacts/validators;
|
|
36
|
-
- unique mode/status/entry identities and slash-command name; users start/resume through that command, while the shared end tool selects the mode;
|
|
37
|
-
- prompts that name exact artifact paths and use `write_workflow_artifact` only for workflow artifacts.
|
|
38
|
-
|
|
39
|
-
Register the mode once in `MODES` in `extension.ts`; document its lifecycle and commands in `docs/workflows.md`.
|
|
40
|
-
|
|
41
|
-
Done when the mode file has no filesystem, persistence, event-registration, or controller code.
|
|
42
|
-
|
|
43
|
-
## 4. Preserve the durable contract
|
|
44
|
-
|
|
45
|
-
The shared adapter owns UUID artifact directories, atomic saves, resume, loop review state, and reload safety. Do not reimplement them per mode.
|
|
46
|
-
|
|
47
|
-
- Persisted state changes after start, artifact/loop transition, resume reconciliation, and explicit end.
|
|
48
|
-
- Never auto-resume on `session_start`; only a user-invoked mode command with an explicit selector attaches a run.
|
|
49
|
-
- Artifact completion advances durable state then stops the parent turn; the user deliberately continues the attached mode.
|
|
50
|
-
- Terminal-ready stays active. Only `end_workflow({ mode })` marks the record completed and terminates.
|
|
51
|
-
- One `ExtensionAPI` hosts all modes: shared writer once, stale reload handlers inert, one attached run total.
|
|
52
|
-
- Builder/reviewer loops use adapter-owned rounds, review files, markers, and max-iteration pause.
|
|
53
|
-
|
|
54
|
-
Done when the new behavior preserves every applicable invariant above.
|
|
55
|
-
|
|
56
|
-
## 5. Lock the mode with real seams
|
|
57
|
-
|
|
58
|
-
Extend the existing fake-Pi tests; do not add another framework. Cover the real mode, not only state-machine units:
|
|
59
|
-
|
|
60
|
-
- start writes valid `workflow.json`; artifact transition updates it; explicit end completes it;
|
|
61
|
-
- explicit resume reconciles an artifact written before a phase save;
|
|
62
|
-
- loop round/review path resumes when the mode has a loop;
|
|
63
|
-
- terminal-ready does not complete early;
|
|
64
|
-
- extension reload ignores stale handlers; inactive sibling modes do not react.
|
|
65
|
-
|
|
66
|
-
Run the narrow mode test, then:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
npx vitest run src/__tests__/state-machine.test.ts src/__tests__/adapter.test.ts src/__tests__/workflow-race.test.ts src/__tests__/workflow-run-state.test.ts src/__tests__/<mode>-workflow.test.ts
|
|
70
|
-
npm run build
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Done when those checks pass and the diff contains only the selected mode, shared-engine changes proven necessary by a failing regression, documentation, and tests.
|