@selesai/code 0.9.13 → 0.9.15

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +5 -10
  3. package/dist/defaults/models.json +77 -27
  4. package/dist/extensions/auto-model/auto-model.test.ts +18 -0
  5. package/dist/extensions/auto-model/classifier.ts +23 -0
  6. package/dist/extensions/auto-model/config.ts +51 -0
  7. package/dist/extensions/auto-model/index.ts +22 -0
  8. package/dist/extensions/auto-model/lifecycle.test.ts +24 -0
  9. package/dist/extensions/enable-readonly-tools.test.ts +52 -0
  10. package/dist/extensions/enable-readonly-tools.ts +20 -0
  11. package/dist/extensions/package.json +4 -2
  12. package/dist/extensions/pi-powerline-footer/guide.ts +5 -20
  13. package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +2 -3
  14. package/dist/extensions/pi-subagents/agents/architect.md +2 -1
  15. package/dist/extensions/pi-subagents/agents/builder.md +3 -1
  16. package/dist/extensions/pi-subagents/agents/commentator.md +3 -1
  17. package/dist/extensions/pi-subagents/agents/explorer.md +2 -1
  18. package/dist/extensions/pi-subagents/agents/recapper.md +2 -1
  19. package/dist/extensions/pi-subagents/agents/researcher.md +1 -1
  20. package/dist/extensions/pi-subagents/agents/reviewer.md +3 -1
  21. package/dist/extensions/pi-subagents/agents/worker.md +2 -1
  22. package/dist/extensions/preview-tools-disabled.test.ts +37 -0
  23. package/dist/extensions/preview-tools-disabled.ts +11 -0
  24. package/dist/extensions/question/batch.ts +1 -1
  25. package/dist/extensions/question/schemas.ts +2 -2
  26. package/dist/extensions/question/tests/batch.test.ts +2 -2
  27. package/dist/skills/workflow/SKILL.md +85 -0
  28. package/docs/plans/auto-model-routing-extension.md +490 -0
  29. package/docs/workflows.md +11 -50
  30. package/package.json +4 -3
  31. package/dist/extensions/workflow/extension.ts +0 -26
  32. package/dist/extensions/workflow/modes.ts +0 -107
  33. package/dist/extensions/workflow/package.json +0 -17
  34. package/dist/skills/workflow-creation/SKILL.md +0 -73
@@ -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.