ralph-flow-pi 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/LICENSE +21 -0
- package/README.md +428 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +56 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/prompts.d.ts +25 -0
- package/dist/commands/prompts.d.ts.map +1 -0
- package/dist/commands/prompts.js +249 -0
- package/dist/commands/prompts.js.map +1 -0
- package/dist/commands/tools.d.ts +47 -0
- package/dist/commands/tools.d.ts.map +1 -0
- package/dist/commands/tools.js +633 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/engine/check-bash.d.ts +121 -0
- package/dist/engine/check-bash.d.ts.map +1 -0
- package/dist/engine/check-bash.js +373 -0
- package/dist/engine/check-bash.js.map +1 -0
- package/dist/engine/check.d.ts +47 -0
- package/dist/engine/check.d.ts.map +1 -0
- package/dist/engine/check.js +298 -0
- package/dist/engine/check.js.map +1 -0
- package/dist/engine/core.d.ts +153 -0
- package/dist/engine/core.d.ts.map +1 -0
- package/dist/engine/core.js +1984 -0
- package/dist/engine/core.js.map +1 -0
- package/dist/engine/lock.d.ts +27 -0
- package/dist/engine/lock.d.ts.map +1 -0
- package/dist/engine/lock.js +121 -0
- package/dist/engine/lock.js.map +1 -0
- package/dist/engine/runner.d.ts +108 -0
- package/dist/engine/runner.d.ts.map +1 -0
- package/dist/engine/runner.js +510 -0
- package/dist/engine/runner.js.map +1 -0
- package/dist/engine/skills.d.ts +53 -0
- package/dist/engine/skills.d.ts.map +1 -0
- package/dist/engine/skills.js +109 -0
- package/dist/engine/skills.js.map +1 -0
- package/dist/engine/step-tools.d.ts +22 -0
- package/dist/engine/step-tools.d.ts.map +1 -0
- package/dist/engine/step-tools.js +45 -0
- package/dist/engine/step-tools.js.map +1 -0
- package/dist/engine/types.d.ts +136 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +20 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/headless.d.ts +57 -0
- package/dist/headless.d.ts.map +1 -0
- package/dist/headless.js +318 -0
- package/dist/headless.js.map +1 -0
- package/dist/pi/adapter.d.ts +135 -0
- package/dist/pi/adapter.d.ts.map +1 -0
- package/dist/pi/adapter.js +231 -0
- package/dist/pi/adapter.js.map +1 -0
- package/dist/pi/interactive.d.ts +28 -0
- package/dist/pi/interactive.d.ts.map +1 -0
- package/dist/pi/interactive.js +58 -0
- package/dist/pi/interactive.js.map +1 -0
- package/dist/pi/tui.d.ts +12 -0
- package/dist/pi/tui.d.ts.map +1 -0
- package/dist/pi/tui.js +12 -0
- package/dist/pi/tui.js.map +1 -0
- package/dist/tui/app.d.ts +25 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +47 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/embed.d.ts +42 -0
- package/dist/tui/embed.d.ts.map +1 -0
- package/dist/tui/embed.js +38 -0
- package/dist/tui/embed.js.map +1 -0
- package/dist/tui/extension.d.ts +88 -0
- package/dist/tui/extension.d.ts.map +1 -0
- package/dist/tui/extension.js +114 -0
- package/dist/tui/extension.js.map +1 -0
- package/dist/tui/history-editor.d.ts +38 -0
- package/dist/tui/history-editor.d.ts.map +1 -0
- package/dist/tui/history-editor.js +55 -0
- package/dist/tui/history-editor.js.map +1 -0
- package/dist/tui/launcher.d.ts +24 -0
- package/dist/tui/launcher.d.ts.map +1 -0
- package/dist/tui/launcher.js +97 -0
- package/dist/tui/launcher.js.map +1 -0
- package/dist/tui/render.d.ts +87 -0
- package/dist/tui/render.d.ts.map +1 -0
- package/dist/tui/render.js +266 -0
- package/dist/tui/render.js.map +1 -0
- package/dist/tui/run-app.d.ts +49 -0
- package/dist/tui/run-app.d.ts.map +1 -0
- package/dist/tui/run-app.js +317 -0
- package/dist/tui/run-app.js.map +1 -0
- package/dist/tui/run-model.d.ts +162 -0
- package/dist/tui/run-model.d.ts.map +1 -0
- package/dist/tui/run-model.js +280 -0
- package/dist/tui/run-model.js.map +1 -0
- package/dist/tui/run-view.d.ts +71 -0
- package/dist/tui/run-view.d.ts.map +1 -0
- package/dist/tui/run-view.js +167 -0
- package/dist/tui/run-view.js.map +1 -0
- package/dist/tui/welcome-header.d.ts +40 -0
- package/dist/tui/welcome-header.d.ts.map +1 -0
- package/dist/tui/welcome-header.js +90 -0
- package/dist/tui/welcome-header.js.map +1 -0
- package/package.json +55 -0
- package/skills/c-to-rust-audit/SKILL.md +67 -0
- package/skills/c-to-rust-implement/SKILL.md +151 -0
- package/skills/c-to-rust-implement/references/c-to-rust-patterns.md +86 -0
- package/skills/c-to-rust-implement/references/conditional-compilation.md +47 -0
- package/skills/c-to-rust-implement/references/crate-reference.md +15 -0
- package/skills/c-to-rust-implement/references/error-strategies.md +80 -0
- package/skills/c-to-rust-implement/references/inline-asm.md +37 -0
- package/skills/c-to-rust-plan/SKILL.md +166 -0
- package/skills/c-to-rust-plan/references/detection-commands.md +66 -0
- package/skills/c-to-rust-test-gen/SKILL.md +130 -0
- package/skills/c-to-rust-test-gen/references/proptest-patterns.md +81 -0
- package/skills/c-to-rust-test-gen/references/test-porting.md +56 -0
- package/skills/c-to-rust-validate/SKILL.md +121 -0
- package/skills/everything2rust-audit/SKILL.md +69 -0
- package/skills/everything2rust-design/SKILL.md +121 -0
- package/skills/everything2rust-design/references/domain-playbooks.md +68 -0
- package/skills/everything2rust-design/references/paradigm-map.md +99 -0
- package/skills/everything2rust-implement/SKILL.md +101 -0
- package/skills/everything2rust-spec/SKILL.md +86 -0
- package/skills/everything2rust-spec/references/oracle-strategies.md +96 -0
- package/skills/everything2rust-survey/SKILL.md +99 -0
- package/skills/everything2rust-test-gen/SKILL.md +68 -0
- package/skills/everything2rust-test-gen/references/harness-patterns.md +186 -0
- package/skills/everything2rust-validate/SKILL.md +85 -0
- package/workflows/c-to-rust.yaml +202 -0
- package/workflows/everything2rust.yaml +259 -0
- package/workflows/loop.yaml +68 -0
- package/workflows/spec.yaml +183 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ralph-flow extension for the main chat session.
|
|
3
|
+
*
|
|
4
|
+
* This is the whole TUI. The plan budgeted ~870 lines for a hand-built one
|
|
5
|
+
* (transcript pane, streaming renderer, slash dispatcher, status bar) — none of
|
|
6
|
+
* that got written, because pi's InteractiveMode already IS a Claude-Code-style
|
|
7
|
+
* TUI and its extension API exposes exactly the three seams we need:
|
|
8
|
+
*
|
|
9
|
+
* registerTool → the six ralphflow_* tools, so the model can drive them
|
|
10
|
+
* from plain language ("start the spec workflow on X")
|
|
11
|
+
* registerCommand → the eight /ralphflow-* slash commands, verbatim
|
|
12
|
+
* sendMessage → the runner's output, rendered into the same transcript
|
|
13
|
+
*
|
|
14
|
+
* The important thing this does NOT do: hand pi the workflow. Step sessions are
|
|
15
|
+
* still created and driven by our runner, one fresh context per step. The main
|
|
16
|
+
* chat session is a control surface and a viewport — if it were the DO session
|
|
17
|
+
* we would have rebuilt the opencode plugin's central flaw on a new stack.
|
|
18
|
+
*/
|
|
19
|
+
import { createRunner } from "../engine/runner.js";
|
|
20
|
+
import { createTools } from "../commands/tools.js";
|
|
21
|
+
import { COMMAND_PROMPTS } from "../commands/prompts.js";
|
|
22
|
+
import { loadSkillIndex } from "../engine/skills.js";
|
|
23
|
+
import { historyEditorFactory } from "./history-editor.js";
|
|
24
|
+
import { createWelcomeHeaderFactory } from "./welcome-header.js";
|
|
25
|
+
/**
|
|
26
|
+
* Build the inline extension. `getSessionId` must return the main session's id
|
|
27
|
+
* — it is the ownership token written into every instance's state.json.
|
|
28
|
+
*/
|
|
29
|
+
export function createRalphExtension(engine, getSessionId) {
|
|
30
|
+
let runner;
|
|
31
|
+
let api;
|
|
32
|
+
/** Post engine-authored text into the transcript without provoking a turn. */
|
|
33
|
+
const post = (text) => {
|
|
34
|
+
api.sendMessage({ customType: "ralph-flow", content: text, display: text }, { triggerTurn: false });
|
|
35
|
+
};
|
|
36
|
+
// Pi's InlineExtension shape: { name, factory } — the factory IS the activate
|
|
37
|
+
// hook, called with the extension API.
|
|
38
|
+
const extension = {
|
|
39
|
+
name: "ralph-flow",
|
|
40
|
+
factory(a) {
|
|
41
|
+
api = a;
|
|
42
|
+
// `ui` doesn't exist on `factory`'s own `api` (see the interface
|
|
43
|
+
// comment above) — "session_start" is the first point an
|
|
44
|
+
// ExtensionContext, and therefore `ctx.ui`, is available. `mode ===
|
|
45
|
+
// "tui"` guards it per ExtensionContext.mode's own doc comment ("Use
|
|
46
|
+
// 'tui' to guard terminal-only UI such as custom components") — rpc/
|
|
47
|
+
// print modes have no editor to replace.
|
|
48
|
+
api.on("session_start", (_event, ctx) => {
|
|
49
|
+
if (ctx.mode !== "tui")
|
|
50
|
+
return;
|
|
51
|
+
ctx.ui.setEditorComponent(historyEditorFactory);
|
|
52
|
+
ctx.ui.setHeader(createWelcomeHeaderFactory(() => engine.listWorkflows()));
|
|
53
|
+
});
|
|
54
|
+
// Deliberately NO onStepEvent here: run-app.ts's own temporary listener
|
|
55
|
+
// (added only while a run view is actually attached — see embed.ts)
|
|
56
|
+
// already shows every DO/CHECK tool call live for anyone watching. A
|
|
57
|
+
// permanent listener that also streamed those one-liners into chat used
|
|
58
|
+
// to sit here — real-terminal testing found it was *why* the model kept
|
|
59
|
+
// reaching for ralphflow_watch on totally unrelated turns: those lines
|
|
60
|
+
// ("▸ [create] bash mkdir -p ...", "▸ [create] report_done") accumulate
|
|
61
|
+
// in the transcript the model re-reads on every future invocation
|
|
62
|
+
// (`triggerTurn: false` only skips waking it up immediately — the text
|
|
63
|
+
// is still there next time it IS woken up, for any reason), and a model
|
|
64
|
+
// that notices background activity in its own context tends to comment
|
|
65
|
+
// on or act on it, unprompted. This directly violated the product's own
|
|
66
|
+
// stated promise ("不需要你现在做任何事,也不需要主动去查看或汇报进度")
|
|
67
|
+
// — DO/CHECK tool calls were never a "needs you" moment. Removing this
|
|
68
|
+
// loses nothing for someone actually watching; it only stops narrating
|
|
69
|
+
// to someone who isn't.
|
|
70
|
+
runner = createRunner(engine, {
|
|
71
|
+
onMessage: (_instId, text) => post(text),
|
|
72
|
+
onGate: () => { }, // the gate's own message comes through onMessage
|
|
73
|
+
onPaused: () => { }, // ditto
|
|
74
|
+
onStalled: () => { }, // ditto
|
|
75
|
+
onCompleted: () => { }, // ditto
|
|
76
|
+
}, {
|
|
77
|
+
skillPaths: () => loadSkillIndex(engine.getRalphFlowDir(), engine.getGlobalConfigHome()).paths,
|
|
78
|
+
});
|
|
79
|
+
// The engine needs to reach into this process to abort a DO session when
|
|
80
|
+
// an instance is destroyed. (abortActiveCheck is wired at engine creation.)
|
|
81
|
+
engine.setAbortActiveStep?.(runner.abortActiveStep);
|
|
82
|
+
for (const tool of createTools({ engine, runner, getSessionId })) {
|
|
83
|
+
api.registerTool(tool);
|
|
84
|
+
}
|
|
85
|
+
// Slash commands are thin: each hands its prompt template to the model,
|
|
86
|
+
// which then calls the matching tool. Keeping the model in the loop is
|
|
87
|
+
// what makes "/ralphflow-start" with no arguments ask what you want, and
|
|
88
|
+
// what makes /ralphflow-create an actual conversation.
|
|
89
|
+
for (const [name, template] of Object.entries(COMMAND_PROMPTS)) {
|
|
90
|
+
api.registerCommand(name, {
|
|
91
|
+
description: template.description,
|
|
92
|
+
handler: async (args) => {
|
|
93
|
+
api.sendUserMessage(template.render(args));
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Adopt whatever was left running: a crashed process leaves live instance
|
|
98
|
+
// dirs behind, and the user should not have to know to type continue.
|
|
99
|
+
for (const info of engine.listInstances()) {
|
|
100
|
+
if (info.state.paused)
|
|
101
|
+
continue;
|
|
102
|
+
if (engine.foreignRunnerPid(info.id) !== null)
|
|
103
|
+
continue; // someone else has it
|
|
104
|
+
runner.ensureRunning(info.id);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
extension,
|
|
110
|
+
get runner() { return runner; },
|
|
111
|
+
shutdown: () => runner?.pauseAllForShutdown(),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/tui/extension.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completes a feature pi's own chat editor ships half-wired: `Editor` (the
|
|
3
|
+
* base class `CustomEditor` extends, in pi-tui) already implements Up/Down
|
|
4
|
+
* arrow history navigation in full — `history`/`historyIndex`/`navigateHistory`
|
|
5
|
+
* and the key-handling branches that call it are all there, and `addToHistory`
|
|
6
|
+
* is documented "Called after successful submission." Nothing in
|
|
7
|
+
* pi-coding-agent's interactive mode actually calls it, though (verified by
|
|
8
|
+
* grepping the compiled output of all four @earendil-works packages for
|
|
9
|
+
* `addToHistory` — the only hit is the definition itself). So the editor never
|
|
10
|
+
* has anything to navigate to, even though the navigation logic works.
|
|
11
|
+
*
|
|
12
|
+
* The obvious hook points don't work: `submitValue()` (where the base class's
|
|
13
|
+
* own doc comment says addToHistory should be called) is `private` in the
|
|
14
|
+
* .d.ts, so a subclass can't override or call it. `onSubmit` looks like a
|
|
15
|
+
* public callback property to wrap, but the base class declares it as a bare
|
|
16
|
+
* class field (`onSubmit;`, no initializer) — under ES2022 class-field
|
|
17
|
+
* semantics that materializes as an own `[[DefineOwnProperty]]` on every
|
|
18
|
+
* instance during `super()`, which would silently SHADOW a get/set accessor
|
|
19
|
+
* defined in this subclass rather than route through it.
|
|
20
|
+
*
|
|
21
|
+
* What's actually safe: override the public `handleInput(data)` (already
|
|
22
|
+
* overridden once by `CustomEditor` itself, so this is a well-trodden
|
|
23
|
+
* extension point) and replicate the base class's own submit-detection using
|
|
24
|
+
* only its public surface (`getLines`/`getCursor`/`disableSubmit`/
|
|
25
|
+
* `getExpandedText`/`addToHistory` — none of them private). The backslash-
|
|
26
|
+
* before-cursor check mirrors submitValue's own "Shift+Enter workaround"
|
|
27
|
+
* branch exactly, so a `\` + Enter that inserts a newline instead of
|
|
28
|
+
* submitting doesn't pollute history with a half-typed draft.
|
|
29
|
+
*/
|
|
30
|
+
import { CustomEditor, type EditorTheme, type KeybindingsManager, type TUI } from "../pi/tui.js";
|
|
31
|
+
export declare class HistoryEditor extends CustomEditor {
|
|
32
|
+
private kb;
|
|
33
|
+
constructor(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager);
|
|
34
|
+
handleInput(data: string): void;
|
|
35
|
+
}
|
|
36
|
+
/** Matches Pi's `EditorFactory` shape for `ctx.ui.setEditorComponent(...)`. */
|
|
37
|
+
export declare function historyEditorFactory(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager): HistoryEditor;
|
|
38
|
+
//# sourceMappingURL=history-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-editor.d.ts","sourceRoot":"","sources":["../../src/tui/history-editor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,kBAAkB,EAAE,KAAK,GAAG,EAAE,MAAM,cAAc,CAAC;AAEjG,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,EAAE,CAAqB;gBAEnB,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB;IAKhE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAYzC;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,GAAG,aAAa,CAEjH"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completes a feature pi's own chat editor ships half-wired: `Editor` (the
|
|
3
|
+
* base class `CustomEditor` extends, in pi-tui) already implements Up/Down
|
|
4
|
+
* arrow history navigation in full — `history`/`historyIndex`/`navigateHistory`
|
|
5
|
+
* and the key-handling branches that call it are all there, and `addToHistory`
|
|
6
|
+
* is documented "Called after successful submission." Nothing in
|
|
7
|
+
* pi-coding-agent's interactive mode actually calls it, though (verified by
|
|
8
|
+
* grepping the compiled output of all four @earendil-works packages for
|
|
9
|
+
* `addToHistory` — the only hit is the definition itself). So the editor never
|
|
10
|
+
* has anything to navigate to, even though the navigation logic works.
|
|
11
|
+
*
|
|
12
|
+
* The obvious hook points don't work: `submitValue()` (where the base class's
|
|
13
|
+
* own doc comment says addToHistory should be called) is `private` in the
|
|
14
|
+
* .d.ts, so a subclass can't override or call it. `onSubmit` looks like a
|
|
15
|
+
* public callback property to wrap, but the base class declares it as a bare
|
|
16
|
+
* class field (`onSubmit;`, no initializer) — under ES2022 class-field
|
|
17
|
+
* semantics that materializes as an own `[[DefineOwnProperty]]` on every
|
|
18
|
+
* instance during `super()`, which would silently SHADOW a get/set accessor
|
|
19
|
+
* defined in this subclass rather than route through it.
|
|
20
|
+
*
|
|
21
|
+
* What's actually safe: override the public `handleInput(data)` (already
|
|
22
|
+
* overridden once by `CustomEditor` itself, so this is a well-trodden
|
|
23
|
+
* extension point) and replicate the base class's own submit-detection using
|
|
24
|
+
* only its public surface (`getLines`/`getCursor`/`disableSubmit`/
|
|
25
|
+
* `getExpandedText`/`addToHistory` — none of them private). The backslash-
|
|
26
|
+
* before-cursor check mirrors submitValue's own "Shift+Enter workaround"
|
|
27
|
+
* branch exactly, so a `\` + Enter that inserts a newline instead of
|
|
28
|
+
* submitting doesn't pollute history with a half-typed draft.
|
|
29
|
+
*/
|
|
30
|
+
import { CustomEditor } from "../pi/tui.js";
|
|
31
|
+
export class HistoryEditor extends CustomEditor {
|
|
32
|
+
kb;
|
|
33
|
+
constructor(tui, theme, keybindings) {
|
|
34
|
+
super(tui, theme, keybindings);
|
|
35
|
+
this.kb = keybindings;
|
|
36
|
+
}
|
|
37
|
+
handleInput(data) {
|
|
38
|
+
if (!this.disableSubmit && this.kb.matches(data, "tui.input.submit")) {
|
|
39
|
+
const cursor = this.getCursor();
|
|
40
|
+
const currentLine = this.getLines()[cursor.line] || "";
|
|
41
|
+
const isBackslashContinuation = cursor.col > 0 && currentLine[cursor.col - 1] === "\\";
|
|
42
|
+
if (!isBackslashContinuation) {
|
|
43
|
+
const text = this.getExpandedText().trim();
|
|
44
|
+
if (text)
|
|
45
|
+
this.addToHistory(text);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
super.handleInput(data);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Matches Pi's `EditorFactory` shape for `ctx.ui.setEditorComponent(...)`. */
|
|
52
|
+
export function historyEditorFactory(tui, theme, keybindings) {
|
|
53
|
+
return new HistoryEditor(tui, theme, keybindings);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=history-editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-editor.js","sourceRoot":"","sources":["../../src/tui/history-editor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAuD,MAAM,cAAc,CAAC;AAEjG,MAAM,OAAO,aAAc,SAAQ,YAAY;IACrC,EAAE,CAAqB;IAE/B,YAAY,GAAQ,EAAE,KAAkB,EAAE,WAA+B;QACvE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,GAAG,WAAW,CAAC;IACxB,CAAC;IAEQ,WAAW,CAAC,IAAY;QAC/B,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,kBAAkB,CAAC,EAAE,CAAC;YACrE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACvD,MAAM,uBAAuB,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC;YACvF,IAAI,CAAC,uBAAuB,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,CAAC;gBAC3C,IAAI,IAAI;oBAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,GAAQ,EAAE,KAAkB,EAAE,WAA+B;IAChG,OAAO,IAAI,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The launch flow: pick a workflow, describe the task — a form, not a chat.
|
|
3
|
+
*
|
|
4
|
+
* A deterministic pipeline is launched, not conversed with, so the entry point
|
|
5
|
+
* is a picker + a text field, not an LLM turn. (Designing a NEW workflow IS a
|
|
6
|
+
* conversation — that lives in the separate `ralphflow create` mode.)
|
|
7
|
+
*
|
|
8
|
+
* These helpers drive the shared TUI: they swap in a picker / input as the root,
|
|
9
|
+
* resolve a promise on selection, and leave the TUI clean for the run view.
|
|
10
|
+
*/
|
|
11
|
+
import type { Engine } from "../engine/core.js";
|
|
12
|
+
import { TUI } from "../pi/tui.js";
|
|
13
|
+
export interface LaunchChoice {
|
|
14
|
+
kind: "start" | "resume" | "quit";
|
|
15
|
+
workflow?: string;
|
|
16
|
+
task?: string;
|
|
17
|
+
instanceId?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Full launch flow. Resolves with the user's choice. Resume takes priority: if
|
|
21
|
+
* live instances exist, offer to attach before starting something new.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runLauncher(tui: TUI, engine: Engine): Promise<LaunchChoice>;
|
|
24
|
+
//# sourceMappingURL=launcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../../src/tui/launcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAiD,GAAG,EAAuC,MAAM,cAAc,CAAC;AAGvH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAaD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAejF"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The launch flow: pick a workflow, describe the task — a form, not a chat.
|
|
3
|
+
*
|
|
4
|
+
* A deterministic pipeline is launched, not conversed with, so the entry point
|
|
5
|
+
* is a picker + a text field, not an LLM turn. (Designing a NEW workflow IS a
|
|
6
|
+
* conversation — that lives in the separate `ralphflow create` mode.)
|
|
7
|
+
*
|
|
8
|
+
* These helpers drive the shared TUI: they swap in a picker / input as the root,
|
|
9
|
+
* resolve a promise on selection, and leave the TUI clean for the run view.
|
|
10
|
+
*/
|
|
11
|
+
import { Input, SelectList, Text, getSelectListTheme } from "../pi/tui.js";
|
|
12
|
+
import { bold, dim } from "./render.js";
|
|
13
|
+
/** Swap the TUI root to a single component (clearing prior children). */
|
|
14
|
+
function setRoot(tui, ...components) {
|
|
15
|
+
tui.clear();
|
|
16
|
+
for (const c of components)
|
|
17
|
+
tui.addChild(c);
|
|
18
|
+
tui.requestRender();
|
|
19
|
+
}
|
|
20
|
+
function heading(text) {
|
|
21
|
+
return new Text(text, 1, 1);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Full launch flow. Resolves with the user's choice. Resume takes priority: if
|
|
25
|
+
* live instances exist, offer to attach before starting something new.
|
|
26
|
+
*/
|
|
27
|
+
export async function runLauncher(tui, engine) {
|
|
28
|
+
const instances = engine.listInstances();
|
|
29
|
+
if (instances.length > 0) {
|
|
30
|
+
const resumeChoice = await pickResumeOrNew(tui, engine, instances);
|
|
31
|
+
if (resumeChoice.kind !== "start")
|
|
32
|
+
return resumeChoice; // resume or quit
|
|
33
|
+
// fall through to the new-workflow flow
|
|
34
|
+
}
|
|
35
|
+
const workflow = await pickWorkflow(tui, engine);
|
|
36
|
+
if (!workflow)
|
|
37
|
+
return { kind: "quit" };
|
|
38
|
+
const task = await enterTask(tui, workflow);
|
|
39
|
+
if (task === null)
|
|
40
|
+
return { kind: "quit" };
|
|
41
|
+
return { kind: "start", workflow, task };
|
|
42
|
+
}
|
|
43
|
+
/** When instances already exist: resume one, start new, or quit. */
|
|
44
|
+
function pickResumeOrNew(tui, engine, instances) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const items = [
|
|
47
|
+
...instances.map((i) => ({
|
|
48
|
+
value: `resume:${i.id}`,
|
|
49
|
+
label: `恢复 ${i.state.workflow_name} · ${i.state.current_step}`,
|
|
50
|
+
description: (i.state.user_task || "").replace(/\s+/g, " ").slice(0, 60),
|
|
51
|
+
})),
|
|
52
|
+
{ value: "new", label: "开始一个新工作流", description: "选择工作流并输入任务" },
|
|
53
|
+
];
|
|
54
|
+
const list = new SelectList(items, 10, getSelectListTheme());
|
|
55
|
+
list.onSelect = (item) => {
|
|
56
|
+
if (item.value === "new")
|
|
57
|
+
resolve({ kind: "start" });
|
|
58
|
+
else
|
|
59
|
+
resolve({ kind: "resume", instanceId: item.value.slice("resume:".length) });
|
|
60
|
+
};
|
|
61
|
+
list.onCancel = () => resolve({ kind: "quit" });
|
|
62
|
+
setRoot(tui, heading(bold("有未完成的工作流") + dim(" ↑↓ 选择 · 回车确认 · Esc 退出")), list);
|
|
63
|
+
tui.setFocus(list);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** Pick a workflow from the resolved list (project → global → built-in). */
|
|
67
|
+
function pickWorkflow(tui, engine) {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
const workflows = engine.listWorkflows().filter((w) => !w.invalid);
|
|
70
|
+
if (workflows.length === 0) {
|
|
71
|
+
resolve(null);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const items = workflows.map((w) => ({ value: w.name, label: w.name, description: w.desc }));
|
|
75
|
+
const list = new SelectList(items, 12, getSelectListTheme());
|
|
76
|
+
list.onSelect = (item) => resolve(item.value);
|
|
77
|
+
list.onCancel = () => resolve(null);
|
|
78
|
+
setRoot(tui, heading(bold("选择工作流") + dim(" ↑↓ 选择 · 回车确认 · Esc 退出")), list);
|
|
79
|
+
tui.setFocus(list);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Capture the task description. Resolves null on Esc. */
|
|
83
|
+
function enterTask(tui, workflow) {
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
const input = new Input();
|
|
86
|
+
input.placeholder = "描述这次要完成的任务,回车开始…";
|
|
87
|
+
input.onSubmit = (value) => {
|
|
88
|
+
const task = value.trim();
|
|
89
|
+
if (task)
|
|
90
|
+
resolve(task); // empty → keep waiting
|
|
91
|
+
};
|
|
92
|
+
input.onEscape = () => resolve(null);
|
|
93
|
+
setRoot(tui, heading(bold(`工作流:${workflow}`) + dim(" 回车开始 · Esc 退出")), new Text(dim("引擎会逐步执行,每步一个全新会话,独立验证。你可以随时看到进度。"), 1, 0), input);
|
|
94
|
+
tui.setFocus(input);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=launcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/tui/launcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure rendering: RunModel → terminal lines.
|
|
3
|
+
*
|
|
4
|
+
* No pi-tui, no side effects, no I/O. Given a model and a width it returns the
|
|
5
|
+
* exact lines the run view shows, so the entire look of the product is unit-
|
|
6
|
+
* testable without a terminal. run-view.ts is a thin pi-tui wrapper that pipes
|
|
7
|
+
* these lines to the screen and calls back in on input.
|
|
8
|
+
*
|
|
9
|
+
* The layout is a growing stream on top with a persistent status block pinned at
|
|
10
|
+
* the bottom (pi-tui's TUI keeps the tail visible, so "bottom" is always on
|
|
11
|
+
* screen while stream history scrolls up — the same shape as a chat's fixed
|
|
12
|
+
* input bar). This is what a chat transcript could never give: a live pipeline
|
|
13
|
+
* you can see all of, with each step's real work streaming in place.
|
|
14
|
+
*/
|
|
15
|
+
import { type RunModel, type StreamBlock } from "./run-model.js";
|
|
16
|
+
export declare const dim: (s: string) => string;
|
|
17
|
+
export declare const bold: (s: string) => string;
|
|
18
|
+
export declare const green: (s: string) => string;
|
|
19
|
+
export declare const red: (s: string) => string;
|
|
20
|
+
export declare const yellow: (s: string) => string;
|
|
21
|
+
export declare const cyan: (s: string) => string;
|
|
22
|
+
export declare const gray: (s: string) => string;
|
|
23
|
+
export declare const magenta: (s: string) => string;
|
|
24
|
+
/**
|
|
25
|
+
* Display width, counting CJK/emoji as 2 columns (delegates to pi-tui so it
|
|
26
|
+
* agrees exactly with the width pi-tui's renderer enforces — a mismatch makes
|
|
27
|
+
* the TUI throw "line exceeds terminal width").
|
|
28
|
+
*/
|
|
29
|
+
export declare function visibleLength(s: string): number;
|
|
30
|
+
/**
|
|
31
|
+
* Wrap a logical line to `width` display columns, ANSI- and wide-char-aware.
|
|
32
|
+
* Uses pi-tui's own wrapper, so every line we emit is guaranteed to fit.
|
|
33
|
+
*/
|
|
34
|
+
export declare function wrap(text: string, width: number): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Pipeline strip: an icon-only progress row, then a dedicated line naming
|
|
37
|
+
* the active step — number, description, DO/CHECK, elapsed. Two lines, not
|
|
38
|
+
* one, on purpose: cramming "步骤 3/7:技术方案设计 DO 45s" onto the same
|
|
39
|
+
* line as the icon row (the original design) made a user report they could
|
|
40
|
+
* only tell "some step is running" from a glance — the icons read fine as a
|
|
41
|
+
* progress bar, but the one piece of text answering "which step, doing
|
|
42
|
+
* what, DO or CHECK" was easy to miss appended after seven icons. Giving it
|
|
43
|
+
* its own line, led with the step's ordinal position, fixes that without
|
|
44
|
+
* adding new information — everything here was already computed, just
|
|
45
|
+
* under-emphasized. `now` defaults to the real clock but is an explicit
|
|
46
|
+
* param so tests stay deterministic — this is the only place render.ts
|
|
47
|
+
* touches wall-clock time at all.
|
|
48
|
+
*/
|
|
49
|
+
export declare function renderStepStrip(m: RunModel, width: number, now?: number): string[];
|
|
50
|
+
/** Render one block. Reasoning is dimmed; tools show their key arg + a few result lines. */
|
|
51
|
+
export declare function renderBlock(b: StreamBlock, width: number): string[];
|
|
52
|
+
/** The active step's stream as lines. */
|
|
53
|
+
export declare function renderStream(m: RunModel, width: number): string[];
|
|
54
|
+
/** Header + step strip + action prompt + hints — always visible at the bottom. */
|
|
55
|
+
export declare function renderStatus(m: RunModel, width: number, now?: number): string[];
|
|
56
|
+
/** The actionable block for gate / pause / stall / completion. */
|
|
57
|
+
export declare function renderAction(m: RunModel, width: number): string[];
|
|
58
|
+
/**
|
|
59
|
+
* The complete document. pi-tui's `TUI` has no real scrollable viewport —
|
|
60
|
+
* there's no API for "pin this footer, scroll that region above it" (checked:
|
|
61
|
+
* `TUI extends Container`'s public surface has nothing scroll-related; the
|
|
62
|
+
* `previousViewportTop` field it does have is a private diffing internal, not
|
|
63
|
+
* something a Component can use). What `TUI` actually does is redraw the last
|
|
64
|
+
* `terminalRows` lines of *whatever this function returns this frame* — so
|
|
65
|
+
* "scrolling" only has one degree of freedom available: change how many lines
|
|
66
|
+
* this function returns, in front of a renderer that always anchors to the
|
|
67
|
+
* bottom of that array.
|
|
68
|
+
*
|
|
69
|
+
* The bug this used to have: the earlier version dropped lines straight off
|
|
70
|
+
* the end of `stream` as `scrollBack` grew, which shrinks the *whole returned
|
|
71
|
+
* array* by that much every keypress — since the status block sits at the very
|
|
72
|
+
* end, its position relative to the top of the (now shorter) array moves every
|
|
73
|
+
* time, and because the renderer bottom-anchors, that reads as the status
|
|
74
|
+
* block itself sliding up the screen the moment you press Up, before there's
|
|
75
|
+
* any real "scrolling" sensation at all. A user reported exactly this: "方向键
|
|
76
|
+
* 上去看的时候,不是滚动看,而是下面分割线的布局先往上跑."
|
|
77
|
+
*
|
|
78
|
+
* The fix: keep the returned array's total length **invariant to
|
|
79
|
+
* `scrollBack`** by left-padding with blank lines instead of shrinking the
|
|
80
|
+
* array. The status block then always sits at the same offset from the end of
|
|
81
|
+
* the document no matter how far back you've scrolled, so the bottom-anchored
|
|
82
|
+
* renderer keeps it visually still — only the *content* revealed above it
|
|
83
|
+
* changes as you page up, which is what "scrolling" is supposed to look like
|
|
84
|
+
* within a renderer that has no actual scroll primitive.
|
|
85
|
+
*/
|
|
86
|
+
export declare function renderScreen(m: RunModel, width: number, scrollBack?: number, now?: number): string[];
|
|
87
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/tui/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAEL,KAAK,QAAQ,EAAkD,KAAK,WAAW,EAChF,MAAM,gBAAgB,CAAC;AAmBxB,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,WAAoB,CAAC;AAClD,eAAO,MAAM,IAAI,GAAI,GAAG,MAAM,WAAqB,CAAC;AACpD,eAAO,MAAM,KAAK,GAAI,GAAG,MAAM,WAAsB,CAAC;AACtD,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,WAAoB,CAAC;AAClD,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,WAAuB,CAAC;AACxD,eAAO,MAAM,IAAI,GAAI,GAAG,MAAM,WAAqB,CAAC;AACpD,eAAO,MAAM,IAAI,GAAI,GAAG,MAAM,WAAqB,CAAC;AACpD,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,WAAwB,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAS1D;AAkBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,EAAE,CAM9F;AAwBD,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4BnE;AAQD,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAOjE;AAaD,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,EAAE,CAiB3F;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBjE;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,SAAI,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,EAAE,CAO3G"}
|