pi-ultracode 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -0
- package/examples/workflows/loop-until-dry-bugs.workflow.js +74 -0
- package/extensions/ultracode.ts +56 -0
- package/package.json +57 -0
- package/src/commands.ts +161 -0
- package/src/index.ts +55 -0
- package/src/mode.ts +196 -0
- package/src/prompts.ts +83 -0
- package/src/workflow/agent-runner.ts +329 -0
- package/src/workflow/agent-types.ts +195 -0
- package/src/workflow/display.ts +245 -0
- package/src/workflow/journal.ts +0 -0
- package/src/workflow/json-schema.ts +116 -0
- package/src/workflow/parser.ts +213 -0
- package/src/workflow/registry.ts +65 -0
- package/src/workflow/runtime.ts +651 -0
- package/src/workflow/structured-output.ts +48 -0
- package/src/workflow/tool.ts +350 -0
- package/src/workflow/worktree.ts +381 -0
- package/types/workflow.d.ts +86 -0
package/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# pi-ultracode
|
|
2
|
+
|
|
3
|
+
> Claude-Code-style **ultracode** for [Pi](https://github.com/earendil-works/pi).
|
|
4
|
+
|
|
5
|
+
`ultracode` is an effort mode that turns Pi into a relentless, multi-agent
|
|
6
|
+
orchestrator. While it's on, Pi raises its thinking to **xhigh** and treats
|
|
7
|
+
"author and run a workflow" as the **default** for every substantive task —
|
|
8
|
+
decomposing work, fanning it out across isolated subagents, and adversarially
|
|
9
|
+
verifying findings before committing to an answer. Token cost is not the
|
|
10
|
+
constraint; correctness and coverage are.
|
|
11
|
+
|
|
12
|
+
This single extension implements the full ultracode surface:
|
|
13
|
+
|
|
14
|
+
| Pillar | What you get |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| **Ultracode mode** | `/ultracode on` raises thinking to xhigh, keeps the `workflow` tool active, and injects a standing "orchestrate + verify by default" system block on every turn. Survives reload / resume / fork via session entries. Optional token budget. |
|
|
17
|
+
| **The `workflow` tool** | A deterministic JavaScript orchestrator: `agent()`, `parallel()`, `pipeline()`, `phase()`, `log()`, `workflow()` (nesting), `args`, `budget`. Plus per-agent **model overrides**, **custom agent types**, **git-worktree isolation**, a real **token budget**, **resumable runs**, and **script persistence**. |
|
|
18
|
+
| **`/workflows` manager** | List recent and in-flight runs with live progress; inspect or abort runs. |
|
|
19
|
+
|
|
20
|
+
Inspired by Anthropic's [dynamic workflows in Claude Code](https://claude.com/blog/introducing-dynamic-workflows-in-claude-code),
|
|
21
|
+
and a superset of the `pi-dynamic-workflows` prototype.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pi install npm:pi-ultracode # or: pi install /path/to/pi-ultracode
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> **Conflict note:** pi-ultracode registers a tool named `workflow`, the same name
|
|
30
|
+
> used by `pi-dynamic-workflows`. pi-ultracode is a strict superset, so remove the
|
|
31
|
+
> prototype first: `pi remove npm:pi-dynamic-workflows`.
|
|
32
|
+
|
|
33
|
+
Then in Pi: `/reload`.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Turn ultracode on
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
/ultracode # TOGGLE on/off
|
|
41
|
+
/ultracode on # xhigh thinking + default workflow orchestration
|
|
42
|
+
/ultracode on 500k # also cap the per-session output-token budget at ~500k
|
|
43
|
+
/ultracode budget 1m # change the budget later
|
|
44
|
+
/ultracode status # show current mode
|
|
45
|
+
/ultracode off # restore the previous thinking level
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
> **Models without `xhigh`:** pi clamps the requested level down to the model's
|
|
49
|
+
> maximum (e.g. glm-5.2 tops out at `high`; non-reasoning models fall to `off`) —
|
|
50
|
+
> it never errors. `/ultracode status` shows the level that actually applied
|
|
51
|
+
> (e.g. `ultracode: on · thinking high`). The **workflow orchestration** half of
|
|
52
|
+
> ultracode doesn't depend on the thinking level at all, so it's unaffected by
|
|
53
|
+
> the model you run.
|
|
54
|
+
|
|
55
|
+
You can also start a session in ultracode mode with the CLI flag:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pi --ultracode
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
While on, just describe the task. Pi will write a workflow script and call the
|
|
62
|
+
`workflow` tool, with live progress shown inline:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
◆ ▶ audit_repo (4/7 done, 2 running) · 3 cached · 41.2k/500k tok
|
|
66
|
+
✓ Survey 1/1
|
|
67
|
+
#1 ✓ repo inventory
|
|
68
|
+
▶ Review 3/4 · 1 running
|
|
69
|
+
#2 ✓ auth module
|
|
70
|
+
#3 ✓ db layer
|
|
71
|
+
#4 ● payments module
|
|
72
|
+
▶ Verify 0/2 · 2 running
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Press `Esc` to cancel; running subagents are aborted and surfaced as skipped.
|
|
76
|
+
|
|
77
|
+
### Manage runs
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
/workflows # toggle the run panel above the editor (run again to hide)
|
|
81
|
+
/workflows wf_ab12-3 # show one run's detail
|
|
82
|
+
/workflows clear # hide the panel
|
|
83
|
+
/workflows abort # abort all active runs (and hide the panel)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The panel is a static snapshot taken when you run the command, so re-run
|
|
87
|
+
`/workflows` to refresh it, or `/workflows clear` to dismiss it. Live progress
|
|
88
|
+
while a workflow runs is shown inline in the tool result, not in this panel.
|
|
89
|
+
|
|
90
|
+
## Workflow script shape
|
|
91
|
+
|
|
92
|
+
A workflow is plain JavaScript whose first statement is a **pure literal**
|
|
93
|
+
`export const meta`:
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
export const meta = {
|
|
97
|
+
name: 'review_changes',
|
|
98
|
+
description: 'Review changed files across dimensions, verify each finding',
|
|
99
|
+
phases: [{ title: 'Review' }, { title: 'Verify' }],
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const DIMENSIONS = [
|
|
103
|
+
{ key: 'bugs', prompt: 'Find correctness bugs in the diff.' },
|
|
104
|
+
{ key: 'perf', prompt: 'Find performance regressions in the diff.' },
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
// pipeline: each dimension verifies as soon as its review completes (no barrier).
|
|
108
|
+
const results = await pipeline(
|
|
109
|
+
DIMENSIONS,
|
|
110
|
+
(d) => agent(d.prompt, { label: 'review:' + d.key, phase: 'Review', schema: FINDINGS }),
|
|
111
|
+
(review) => parallel((review.findings ?? []).map((f) => () =>
|
|
112
|
+
agent('Adversarially verify, default to refuted: ' + f.title,
|
|
113
|
+
{ label: 'verify:' + f.file, phase: 'Verify', agentType: 'code-reviewer', schema: VERDICT })
|
|
114
|
+
.then((v) => ({ ...f, verdict: v }))
|
|
115
|
+
)),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
const confirmed = results.flat().filter(Boolean).filter((f) => f.verdict?.isReal)
|
|
119
|
+
return { confirmed }
|
|
120
|
+
|
|
121
|
+
const FINDINGS = { type: 'object', properties: { findings: { type: 'array', items: { type: 'object' } } } }
|
|
122
|
+
const VERDICT = { type: 'object', properties: { isReal: { type: 'boolean' }, why: { type: 'string' } }, required: ['isReal'] }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Globals
|
|
126
|
+
|
|
127
|
+
| Global | Description |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `agent(prompt, opts)` | Spawn an isolated subagent. Returns its final text, or a validated object when `opts.schema` is set. |
|
|
130
|
+
| `parallel(thunks)` | Run `() => agent(...)` thunks concurrently. **A barrier.** Failures become `null`. |
|
|
131
|
+
| `pipeline(items, ...stages)` | Run each item through stages independently (no barrier). Stages get `(prev, original, index)`. **The default** for multi-stage work. |
|
|
132
|
+
| `workflow(nameOrRef, args)` | Run a saved workflow (by name) or `{ scriptPath }` inline, sharing the run's concurrency cap, agent counter, and budget. One level of nesting. |
|
|
133
|
+
| `phase(title)` | Mark the current progress group. |
|
|
134
|
+
| `log(message)` | Append a workflow-level log line. |
|
|
135
|
+
| `args`, `cwd` | The tool's `args` value; the working directory. |
|
|
136
|
+
| `budget` | `{ total, spent(), remaining() }` — real output-token budget. |
|
|
137
|
+
|
|
138
|
+
### `agent()` options
|
|
139
|
+
|
|
140
|
+
| Option | Effect |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| `label` | Short (2-5 word) name shown in live progress. |
|
|
143
|
+
| `phase` | Assign this agent to a progress group explicitly (use inside `parallel`/`pipeline`). |
|
|
144
|
+
| `schema` | Plain JSON Schema; the subagent returns a validated object via a terminating `structured_output` tool. |
|
|
145
|
+
| `model` | Override the subagent model by pattern, e.g. `'sonnet'` or `'provider/id:high'`. |
|
|
146
|
+
| `agentType` | Use a custom role: built-ins `claude`, `general-purpose`, `Explore`, `Plan`, `code-reviewer`, or your own (below). |
|
|
147
|
+
| `isolation: 'worktree'` | Run the agent in a throwaway git worktree (for parallel file mutation). Changes are applied back to the working tree under a lock; an unchanged worktree is auto-removed. |
|
|
148
|
+
|
|
149
|
+
### Determinism
|
|
150
|
+
|
|
151
|
+
Scripts run in a `vm` sandbox. `Date.now()`, `new Date()`, `Math.random()`,
|
|
152
|
+
`require`/`import`, `fs`, and network APIs are unavailable — this keeps `meta`
|
|
153
|
+
parseable and runs **reproducible and resumable**. Pass timestamps via `args`;
|
|
154
|
+
vary randomness by agent index.
|
|
155
|
+
|
|
156
|
+
## Custom agent types
|
|
157
|
+
|
|
158
|
+
Drop a Markdown file with frontmatter under
|
|
159
|
+
`.pi/ultracode/agents/<name>.md` (project) or `~/.pi/ultracode/agents/<name>.md`
|
|
160
|
+
(user):
|
|
161
|
+
|
|
162
|
+
```markdown
|
|
163
|
+
---
|
|
164
|
+
name: security
|
|
165
|
+
description: Security-focused reviewer
|
|
166
|
+
tools: read, grep, find, bash
|
|
167
|
+
model: sonnet
|
|
168
|
+
thinking: high
|
|
169
|
+
---
|
|
170
|
+
You are a security reviewer. Hunt for injection, authz gaps, and unsafe deserialization.
|
|
171
|
+
Cite exact file:line evidence and prefer false negatives over invented findings.
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Then `agent('Audit auth.ts', { agentType: 'security' })`.
|
|
175
|
+
|
|
176
|
+
## Saved & resumable runs
|
|
177
|
+
|
|
178
|
+
Every run persists its script and a JSONL journal under
|
|
179
|
+
`<sessionDir>/ultracode-runs/<runId>.{workflow.js,jsonl}`. To resume after a
|
|
180
|
+
pause, kill, or script edit, call the tool again with `resumeFromRunId`: the
|
|
181
|
+
longest unchanged prefix of `agent()` calls returns cached results instantly;
|
|
182
|
+
the first changed/new call and everything after it run live.
|
|
183
|
+
|
|
184
|
+
Save reusable workflows under `.pi/ultracode/workflows/<name>.workflow.js` and
|
|
185
|
+
run them with the tool's `name` parameter or `workflow('<name>')`.
|
|
186
|
+
|
|
187
|
+
## Library modules
|
|
188
|
+
|
|
189
|
+
| File | Purpose |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `src/prompts.ts` | Ultracode system block + workflow tool guidelines. |
|
|
192
|
+
| `src/mode.ts` | The ultracode mode controller (toggle, thinking, persistence, injection). |
|
|
193
|
+
| `src/commands.ts` | `/ultracode` and `/workflows` commands. |
|
|
194
|
+
| `src/workflow/parser.ts` | AST-validated, deterministic script parser. |
|
|
195
|
+
| `src/workflow/runtime.ts` | The sandboxed runtime (agent/parallel/pipeline/phase/log/workflow/budget). |
|
|
196
|
+
| `src/workflow/agent-runner.ts` | In-memory subagent runner (model, agent type, worktree, usage). |
|
|
197
|
+
| `src/workflow/worktree.ts` | Git worktree isolation. |
|
|
198
|
+
| `src/workflow/agent-types.ts` | Custom agent-type discovery. |
|
|
199
|
+
| `src/workflow/journal.ts` | Run journal + resume. |
|
|
200
|
+
| `src/workflow/registry.ts` | In-process run registry for `/workflows`. |
|
|
201
|
+
| `src/workflow/display.ts` | Live progress snapshots and renderers. |
|
|
202
|
+
| `src/workflow/structured-output.ts` / `json-schema.ts` | Terminating structured output + JSON-Schema → TypeBox. |
|
|
203
|
+
| `src/workflow/tool.ts` | The Pi `workflow` tool. |
|
|
204
|
+
| `extensions/ultracode.ts` | Extension entrypoint. |
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npm test # node --test over the parser, runtime, journal, agent types, mode, and extension
|
|
210
|
+
npm run typecheck # tsc --noEmit (requires a local TypeScript)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The test suite injects a mock subagent runner, so it runs without a model. A
|
|
214
|
+
faithful end-to-end check (real subagents) just calls `runWorkflow` with the
|
|
215
|
+
default runner.
|
|
216
|
+
|
|
217
|
+
**Dependencies.** The only hard runtime dependency is `acorn`; Pi provides
|
|
218
|
+
`typebox` and `@earendil-works/*` to extensions at load time. When running the
|
|
219
|
+
node test runner directly (which has no Pi loader), link those peers into
|
|
220
|
+
`node_modules/` — e.g. symlink them from your Pi install — alongside the
|
|
221
|
+
real `acorn` that `pi install` / `npm install` provides.
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/// <reference types="pi-ultracode/workflow" />
|
|
2
|
+
//
|
|
3
|
+
// Example saved workflow. Copy to `.pi/ultracode/workflows/` and run it with
|
|
4
|
+
// the `workflow` tool's `name` parameter, or call `workflow('loop-until-dry-bugs')`
|
|
5
|
+
// from another workflow.
|
|
6
|
+
//
|
|
7
|
+
// Pattern: loop-until-dry discovery + perspective-diverse adversarial verify.
|
|
8
|
+
// Keeps spawning finders until two consecutive rounds surface nothing new, then
|
|
9
|
+
// confirms each fresh bug with three distinct lenses.
|
|
10
|
+
|
|
11
|
+
export const meta = {
|
|
12
|
+
name: 'loop-until-dry-bugs',
|
|
13
|
+
description: 'Find bugs until the well runs dry, verifying each with three lenses',
|
|
14
|
+
phases: [{ title: 'Find' }, { title: 'Verify' }],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const FINDERS = [
|
|
18
|
+
'Find correctness bugs by reading the changed files closely.',
|
|
19
|
+
'Find bugs by tracing data flow across module boundaries.',
|
|
20
|
+
'Find bugs by hunting error-handling and edge-case gaps.',
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
const BUGS = {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
bugs: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
items: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: { file: { type: 'string' }, line: { type: 'number' }, desc: { type: 'string' } },
|
|
31
|
+
required: ['file', 'desc'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ['bugs'],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const VERDICT = {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: { real: { type: 'boolean' }, why: { type: 'string' } },
|
|
41
|
+
required: ['real'],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const seen = new Set()
|
|
45
|
+
const confirmed = []
|
|
46
|
+
let dry = 0
|
|
47
|
+
|
|
48
|
+
while (dry < 2) {
|
|
49
|
+
const rounds = await parallel(
|
|
50
|
+
FINDERS.map((prompt, i) => () => agent(prompt, { label: 'find ' + i, phase: 'Find', schema: BUGS })),
|
|
51
|
+
)
|
|
52
|
+
const found = rounds.filter(Boolean).flatMap((r) => r.bugs ?? [])
|
|
53
|
+
const fresh = found.filter((b) => !seen.has(b.file + ':' + b.desc))
|
|
54
|
+
if (fresh.length === 0) {
|
|
55
|
+
dry++
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
dry = 0
|
|
59
|
+
for (const b of fresh) seen.add(b.file + ':' + b.desc)
|
|
60
|
+
|
|
61
|
+
const judged = await parallel(
|
|
62
|
+
fresh.map((b) => () =>
|
|
63
|
+
parallel(
|
|
64
|
+
['correctness', 'security', 'does-it-reproduce'].map((lens) => () =>
|
|
65
|
+
agent('Judge via the ' + lens + ' lens — is this real? "' + b.desc + '" (' + b.file + '). Default to real:false if unsure.',
|
|
66
|
+
{ label: 'verify ' + lens, phase: 'Verify', agentType: 'code-reviewer', schema: VERDICT }),
|
|
67
|
+
),
|
|
68
|
+
).then((votes) => ({ bug: b, real: votes.filter(Boolean).filter((v) => v.real).length >= 2 })),
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
confirmed.push(...judged.filter((j) => j.real).map((j) => j.bug))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { confirmed, totalSeen: seen.size }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-ultracode extension entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* Wires together the three pillars of Claude-Code-style "ultracode":
|
|
5
|
+
* 1. The ultracode effort mode (xhigh thinking + standing workflow opt-in).
|
|
6
|
+
* 2. The full `workflow` orchestration tool.
|
|
7
|
+
* 3. The `/ultracode` and `/workflows` commands.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import { createWorkflowTool, type WorkflowToolDeps } from "../src/workflow/tool.ts";
|
|
12
|
+
import { UltracodeMode } from "../src/mode.ts";
|
|
13
|
+
import { registerCommands } from "../src/commands.ts";
|
|
14
|
+
|
|
15
|
+
export default function extension(pi: ExtensionAPI, extraDeps: Partial<WorkflowToolDeps> = {}): void {
|
|
16
|
+
const mode = new UltracodeMode("workflow");
|
|
17
|
+
|
|
18
|
+
const workflowTool = createWorkflowTool({
|
|
19
|
+
getDefaultBudget: () => mode.getBudget(),
|
|
20
|
+
getThinkingLevel: () => mode.getSubagentThinkingLevel(),
|
|
21
|
+
...extraDeps,
|
|
22
|
+
});
|
|
23
|
+
pi.registerTool(workflowTool);
|
|
24
|
+
|
|
25
|
+
registerCommands(pi, mode);
|
|
26
|
+
|
|
27
|
+
// Opt-in via CLI flag: `pi --ultracode`.
|
|
28
|
+
pi.registerFlag("ultracode", {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
description: "Start the session in ultracode mode (xhigh thinking + default workflow orchestration).",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
pi.on("session_start", (_event, ctx) => {
|
|
34
|
+
// Restore persisted mode state across reload / resume / fork.
|
|
35
|
+
try {
|
|
36
|
+
mode.restore(pi, ctx.sessionManager.getEntries() as any);
|
|
37
|
+
} catch {
|
|
38
|
+
// ignore
|
|
39
|
+
}
|
|
40
|
+
if (!mode.isEnabled() && pi.getFlag?.("ultracode") === true) {
|
|
41
|
+
mode.enable(pi);
|
|
42
|
+
}
|
|
43
|
+
// Always keep the workflow tool available so the model can use it on request.
|
|
44
|
+
try {
|
|
45
|
+
const active = pi.getActiveTools();
|
|
46
|
+
if (!active.includes(workflowTool.name)) pi.setActiveTools([...active, workflowTool.name]);
|
|
47
|
+
} catch {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
if (ctx.hasUI && mode.isEnabled()) ctx.ui.setStatus("ultracode", mode.statusLine());
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
pi.on("before_agent_start", (event) => {
|
|
54
|
+
return mode.beforeAgentStart(event);
|
|
55
|
+
});
|
|
56
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-ultracode",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Claude-Code-style \"ultracode\" for Pi: an effort mode that defaults to deterministic multi-agent workflow orchestration, with worktree isolation, per-agent model overrides, custom agent types, nested workflows, token budgets, resumable runs, and a /workflows manager.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi",
|
|
9
|
+
"pi-coding-agent",
|
|
10
|
+
"ultracode",
|
|
11
|
+
"workflow",
|
|
12
|
+
"workflows",
|
|
13
|
+
"agents",
|
|
14
|
+
"subagents",
|
|
15
|
+
"orchestration"
|
|
16
|
+
],
|
|
17
|
+
"author": "pi-ultracode",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"files": [
|
|
20
|
+
"extensions/",
|
|
21
|
+
"src/",
|
|
22
|
+
"types/",
|
|
23
|
+
"examples/",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"pi": {
|
|
27
|
+
"extensions": [
|
|
28
|
+
"./extensions/ultracode.ts"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
"./workflow": {
|
|
33
|
+
"types": "./types/workflow.d.ts"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "node --experimental-strip-types --test test/*.test.ts",
|
|
38
|
+
"typecheck": "tsc --noEmit"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"acorn": "^8.11.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@earendil-works/pi-agent-core": "*",
|
|
45
|
+
"@earendil-works/pi-ai": "*",
|
|
46
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
47
|
+
"@earendil-works/pi-tui": "*",
|
|
48
|
+
"typebox": "*"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@earendil-works/pi-agent-core": { "optional": true },
|
|
52
|
+
"@earendil-works/pi-ai": { "optional": true },
|
|
53
|
+
"@earendil-works/pi-coding-agent": { "optional": true },
|
|
54
|
+
"@earendil-works/pi-tui": { "optional": true },
|
|
55
|
+
"typebox": { "optional": true }
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/commands.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash commands: `/ultracode` (mode toggle) and `/workflows` (run manager).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { parseBudget, type UltracodeMode } from "./mode.ts";
|
|
7
|
+
import { getRegistry } from "./workflow/registry.ts";
|
|
8
|
+
import { renderWorkflowLines } from "./workflow/display.ts";
|
|
9
|
+
|
|
10
|
+
export function registerCommands(pi: ExtensionAPI, mode: UltracodeMode): void {
|
|
11
|
+
pi.registerCommand("ultracode", {
|
|
12
|
+
description: "Toggle ultracode mode (xhigh thinking + default workflow orchestration). Bare /ultracode toggles; subcommands: on|off|status|budget <n>",
|
|
13
|
+
getArgumentCompletions(prefix: string) {
|
|
14
|
+
return ["on", "off", "status", "budget"]
|
|
15
|
+
.filter((s) => s.startsWith(prefix))
|
|
16
|
+
.map((value) => ({ value, label: value }));
|
|
17
|
+
},
|
|
18
|
+
handler: async (args: string, ctx) => {
|
|
19
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
20
|
+
const sub = (parts[0] ?? "").toLowerCase();
|
|
21
|
+
|
|
22
|
+
// Bare `/ultracode` is a toggle.
|
|
23
|
+
if (sub === "") {
|
|
24
|
+
const nowOn = mode.toggle(pi);
|
|
25
|
+
ctx.ui.notify(nowOn ? `Ultracode on — ${mode.statusLine()}` : "Ultracode off — thinking restored.", "info");
|
|
26
|
+
ctx.ui.setStatus("ultracode", nowOn ? mode.statusLine() : undefined);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (sub === "status") {
|
|
31
|
+
ctx.ui.notify(mode.statusLine(), "info");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (sub === "off") {
|
|
36
|
+
mode.disable(pi);
|
|
37
|
+
ctx.ui.notify("Ultracode off — thinking restored, workflow orchestration is opt-in again.", "info");
|
|
38
|
+
ctx.ui.setStatus("ultracode", undefined);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (sub === "budget") {
|
|
43
|
+
const budget = parts[1] ? parseBudget(parts[1]) : null;
|
|
44
|
+
mode.setBudget(pi, budget);
|
|
45
|
+
ctx.ui.notify(
|
|
46
|
+
budget ? `Ultracode token budget set to ~${budget} output tokens.` : "Ultracode token budget cleared.",
|
|
47
|
+
"info",
|
|
48
|
+
);
|
|
49
|
+
ctx.ui.setStatus("ultracode", mode.statusLine());
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// "on", "on 500k", "500k", "+500k"
|
|
54
|
+
let budget: number | null | undefined;
|
|
55
|
+
const budgetToken = sub === "on" ? parts[1] : sub;
|
|
56
|
+
if (budgetToken) {
|
|
57
|
+
const parsed = parseBudget(budgetToken);
|
|
58
|
+
if (parsed) budget = parsed;
|
|
59
|
+
}
|
|
60
|
+
mode.enable(pi, budget !== undefined ? { budget } : {});
|
|
61
|
+
ctx.ui.notify(
|
|
62
|
+
`Ultracode on — ${mode.statusLine()}${budget ? ` (budget ~${budget} tokens)` : ""}`,
|
|
63
|
+
"info",
|
|
64
|
+
);
|
|
65
|
+
ctx.ui.setStatus("ultracode", mode.statusLine());
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Tracks whether the run panel is currently shown, so bare /workflows toggles it.
|
|
70
|
+
let panelVisible = false;
|
|
71
|
+
|
|
72
|
+
pi.registerCommand("workflows", {
|
|
73
|
+
description: "Toggle the workflow-run panel. Usage: /workflows [runId | clear | abort]",
|
|
74
|
+
getArgumentCompletions(prefix: string) {
|
|
75
|
+
return ["clear", "abort"]
|
|
76
|
+
.filter((s) => s.startsWith(prefix))
|
|
77
|
+
.map((value) => ({ value, label: value }));
|
|
78
|
+
},
|
|
79
|
+
handler: async (args: string, ctx) => {
|
|
80
|
+
const registry = getRegistry();
|
|
81
|
+
const arg = args.trim();
|
|
82
|
+
const sub = arg.toLowerCase();
|
|
83
|
+
|
|
84
|
+
const hide = () => {
|
|
85
|
+
ctx.ui.setWidget("ultracode-workflows", undefined);
|
|
86
|
+
panelVisible = false;
|
|
87
|
+
};
|
|
88
|
+
const show = (lines: string[]) => {
|
|
89
|
+
ctx.ui.setWidget("ultracode-workflows", lines);
|
|
90
|
+
panelVisible = true;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (sub === "clear" || sub === "hide" || sub === "off") {
|
|
94
|
+
hide();
|
|
95
|
+
ctx.ui.notify("Workflow panel hidden.", "info");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (sub === "abort") {
|
|
100
|
+
registry.abortAll();
|
|
101
|
+
hide();
|
|
102
|
+
ctx.ui.notify("Requested abort of all active workflow runs; panel hidden.", "info");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const runs = registry.list();
|
|
107
|
+
|
|
108
|
+
// Explicit run id -> show that run's detail.
|
|
109
|
+
if (arg) {
|
|
110
|
+
const handle = registry.get(arg) ?? runs.find((r) => r.snapshot.runId?.startsWith(arg));
|
|
111
|
+
if (!handle) {
|
|
112
|
+
ctx.ui.notify(`No workflow run matching "${arg}". /workflows to list, /workflows clear to hide.`, "warning");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
show(renderWorkflowLines(handle.snapshot, { maxAgents: 12, maxLogs: 6, showResultPreviews: true, showStream: true }));
|
|
116
|
+
ctx.ui.notify(`Showing ${handle.snapshot.runId ?? "run"}. /workflows clear to hide.`, "info");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Bare /workflows toggles the panel off if it's already up.
|
|
121
|
+
if (panelVisible) {
|
|
122
|
+
hide();
|
|
123
|
+
ctx.ui.notify("Workflow panel hidden.", "info");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (runs.length === 0) {
|
|
128
|
+
hide();
|
|
129
|
+
ctx.ui.notify("No workflow runs in this session yet.", "info");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const summary = runs.map((handle) => {
|
|
134
|
+
const s = handle.snapshot;
|
|
135
|
+
return `${statusGlyph(s.status)} ${s.runId} ${s.name} ${s.doneCount}/${s.agentCount}${
|
|
136
|
+
s.runningCount ? ` (${s.runningCount} running)` : ""
|
|
137
|
+
}`;
|
|
138
|
+
});
|
|
139
|
+
show(["◆ Ultracode workflow runs · /workflows clear to hide", ...summary.map((l) => ` ${l}`)]);
|
|
140
|
+
ctx.ui.notify(
|
|
141
|
+
`${runs.length} run(s), ${registry.active().length} active. /workflows again (or /workflows clear) to hide.`,
|
|
142
|
+
"info",
|
|
143
|
+
);
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function statusGlyph(status: string): string {
|
|
149
|
+
switch (status) {
|
|
150
|
+
case "completed":
|
|
151
|
+
return "✓";
|
|
152
|
+
case "running":
|
|
153
|
+
return "▶";
|
|
154
|
+
case "aborted":
|
|
155
|
+
return "■";
|
|
156
|
+
case "failed":
|
|
157
|
+
return "✗";
|
|
158
|
+
default:
|
|
159
|
+
return "·";
|
|
160
|
+
}
|
|
161
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Public API of pi-ultracode, for reuse and testing. */
|
|
2
|
+
|
|
3
|
+
export { default } from "../extensions/ultracode.ts";
|
|
4
|
+
export { UltracodeMode, parseBudget, MODE_ENTRY_TYPE } from "./mode.ts";
|
|
5
|
+
export { registerCommands } from "./commands.ts";
|
|
6
|
+
export {
|
|
7
|
+
ULTRACODE_TAGLINE,
|
|
8
|
+
ULTRACODE_ACTIVE_REMINDER,
|
|
9
|
+
ultracodeSystemBlock,
|
|
10
|
+
WORKFLOW_GUIDELINES,
|
|
11
|
+
WORKFLOW_PROMPT_SNIPPET,
|
|
12
|
+
WORKFLOW_TOOL_DESCRIPTION,
|
|
13
|
+
} from "./prompts.ts";
|
|
14
|
+
|
|
15
|
+
export { createWorkflowTool, type WorkflowToolDeps } from "./workflow/tool.ts";
|
|
16
|
+
export { runWorkflow, type WorkflowRunOptions, type WorkflowRunResult } from "./workflow/runtime.ts";
|
|
17
|
+
export { parseWorkflowScript, normalizeScript, type WorkflowMeta, type WorkflowMetaPhase } from "./workflow/parser.ts";
|
|
18
|
+
export { jsonSchemaToTypeBox } from "./workflow/json-schema.ts";
|
|
19
|
+
export {
|
|
20
|
+
WorkflowAgentRunner,
|
|
21
|
+
resolveModelSelection,
|
|
22
|
+
matchModelIn,
|
|
23
|
+
type AgentRunCall,
|
|
24
|
+
type AgentRunResult,
|
|
25
|
+
type ThinkingLevel,
|
|
26
|
+
type ModelLike,
|
|
27
|
+
type ModelRegistryLike,
|
|
28
|
+
} from "./workflow/agent-runner.ts";
|
|
29
|
+
export { createStructuredOutputTool, type StructuredOutputCapture } from "./workflow/structured-output.ts";
|
|
30
|
+
export {
|
|
31
|
+
discoverAgentTypes,
|
|
32
|
+
resolveAgentType,
|
|
33
|
+
parseFrontmatter,
|
|
34
|
+
parseAgentTypeFile,
|
|
35
|
+
type AgentTypeDef,
|
|
36
|
+
} from "./workflow/agent-types.ts";
|
|
37
|
+
export { RunJournal, agentCallKey, hashString, stableStringify } from "./workflow/journal.ts";
|
|
38
|
+
export { getRegistry, WorkflowRegistry } from "./workflow/registry.ts";
|
|
39
|
+
export {
|
|
40
|
+
createSnapshot,
|
|
41
|
+
recompute,
|
|
42
|
+
renderWorkflowLines,
|
|
43
|
+
renderWorkflowText,
|
|
44
|
+
preview,
|
|
45
|
+
type WorkflowSnapshot,
|
|
46
|
+
} from "./workflow/display.ts";
|
|
47
|
+
export {
|
|
48
|
+
createWorktree,
|
|
49
|
+
captureWorktreeDiff,
|
|
50
|
+
removeWorktree,
|
|
51
|
+
applyPatch,
|
|
52
|
+
writeRescuePatch,
|
|
53
|
+
isGitRepo,
|
|
54
|
+
type Worktree,
|
|
55
|
+
} from "./workflow/worktree.ts";
|