@wrongstack/tui 0.4.1 → 0.5.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/dist/index.d.ts +14 -0
- package/dist/index.js +102 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ interface RunTuiOptions {
|
|
|
26
26
|
queueStore?: QueueStore;
|
|
27
27
|
/** Surfaces the "⚠ YOLO" chip in the status bar. */
|
|
28
28
|
yolo?: boolean;
|
|
29
|
+
/** Query live YOLO state from the permission policy. */
|
|
30
|
+
getYolo?: () => boolean;
|
|
31
|
+
/** Query the live autonomy mode. */
|
|
32
|
+
getAutonomy?: () => 'off' | 'suggest' | 'auto';
|
|
29
33
|
/** Renders in the startup banner. Read from the CLI's package.json. */
|
|
30
34
|
appVersion?: string;
|
|
31
35
|
/** Provider id for the startup banner ("openai", "anthropic", ...). */
|
|
@@ -110,6 +114,16 @@ interface RunTuiOptions {
|
|
|
110
114
|
* Ignored when `initialGoal` is also set.
|
|
111
115
|
*/
|
|
112
116
|
initialAsk?: string;
|
|
117
|
+
/**
|
|
118
|
+
* SDD session context getter. When an SDD session is active, returns
|
|
119
|
+
* the AI prompt context to inject into user messages.
|
|
120
|
+
*/
|
|
121
|
+
getSDDContext?: () => string | null;
|
|
122
|
+
/**
|
|
123
|
+
* Process AI output for SDD auto-detection (spec, tasks, plan).
|
|
124
|
+
* Returns displayable status messages.
|
|
125
|
+
*/
|
|
126
|
+
onSDDOutput?: (output: string) => Promise<string[]>;
|
|
113
127
|
}
|
|
114
128
|
declare function runTui(opts: RunTuiOptions): Promise<number>;
|
|
115
129
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { render, useApp, Box, useStdout, Static, Text, useInput, useStdin } from 'ink';
|
|
2
|
-
import
|
|
2
|
+
import React2, { useState, useReducer, useRef, useEffect, useMemo } from 'react';
|
|
3
3
|
import * as fs2 from 'fs/promises';
|
|
4
4
|
import * as path2 from 'path';
|
|
5
5
|
import { InputBuilder, formatTodosList, buildChildEnv } from '@wrongstack/core';
|
|
@@ -39,7 +39,7 @@ function ConfirmPrompt({
|
|
|
39
39
|
suggestedPattern,
|
|
40
40
|
onDecision
|
|
41
41
|
}) {
|
|
42
|
-
|
|
42
|
+
React2.useEffect(() => {
|
|
43
43
|
process.stdout.write("\x07");
|
|
44
44
|
}, []);
|
|
45
45
|
useInput((input2, key) => {
|
|
@@ -244,6 +244,15 @@ function FleetPanel({ entries, totalCost, roster }) {
|
|
|
244
244
|
"msg: ",
|
|
245
245
|
message
|
|
246
246
|
] }) }, `${entry.id}-msg-${index}-${message}`)),
|
|
247
|
+
entry.budgetWarning ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
248
|
+
"\u26A1 hitting ",
|
|
249
|
+
entry.budgetWarning.kind,
|
|
250
|
+
" limit (",
|
|
251
|
+
entry.budgetWarning.used,
|
|
252
|
+
"/",
|
|
253
|
+
entry.budgetWarning.limit,
|
|
254
|
+
") \u2014 extending"
|
|
255
|
+
] }) }) : null,
|
|
247
256
|
entry.status === "running" && entry.streamingText ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
248
257
|
">",
|
|
249
258
|
" ",
|
|
@@ -1563,6 +1572,7 @@ function StatusBar({
|
|
|
1563
1572
|
hint,
|
|
1564
1573
|
queueCount = 0,
|
|
1565
1574
|
yolo = false,
|
|
1575
|
+
autonomy,
|
|
1566
1576
|
elapsedMs,
|
|
1567
1577
|
todos,
|
|
1568
1578
|
plan,
|
|
@@ -1578,7 +1588,7 @@ function StatusBar({
|
|
|
1578
1588
|
const cache2 = tokenCounter?.cacheStats();
|
|
1579
1589
|
const stateColor = state === "idle" ? "cyan" : state === "aborting" ? "yellow" : "green";
|
|
1580
1590
|
const stateLabel = state === "idle" ? "idle" : state === "aborting" ? "aborting\u2026" : "thinking\u2026";
|
|
1581
|
-
const hasSecondLine = yolo || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0;
|
|
1591
|
+
const hasSecondLine = yolo || autonomy && autonomy !== "off" || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0;
|
|
1582
1592
|
const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
|
|
1583
1593
|
const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity;
|
|
1584
1594
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1643,6 +1653,13 @@ function StatusBar({
|
|
|
1643
1653
|
] }),
|
|
1644
1654
|
hasSecondLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
|
|
1645
1655
|
yolo ? /* @__PURE__ */ jsx(Text, { color: "red", bold: true, children: "\u26A0 YOLO" }) : null,
|
|
1656
|
+
autonomy && autonomy !== "off" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1657
|
+
yolo ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
|
|
1658
|
+
/* @__PURE__ */ jsxs(Text, { color: autonomy === "auto" ? "yellow" : "cyan", bold: true, children: [
|
|
1659
|
+
"\u221E ",
|
|
1660
|
+
autonomy.toUpperCase()
|
|
1661
|
+
] })
|
|
1662
|
+
] }) : null,
|
|
1646
1663
|
elapsedMs !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1647
1664
|
yolo ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
|
|
1648
1665
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
@@ -2341,6 +2358,8 @@ function reducer(state, action) {
|
|
|
2341
2358
|
...cur,
|
|
2342
2359
|
status: "running",
|
|
2343
2360
|
streamingText: "",
|
|
2361
|
+
budgetWarning: void 0,
|
|
2362
|
+
// clear on restart
|
|
2344
2363
|
startedAt: Date.now()
|
|
2345
2364
|
}
|
|
2346
2365
|
}
|
|
@@ -2423,6 +2442,23 @@ function reducer(state, action) {
|
|
|
2423
2442
|
toolCalls: action.toolCalls,
|
|
2424
2443
|
streamingText: "",
|
|
2425
2444
|
currentTool: void 0,
|
|
2445
|
+
budgetWarning: void 0,
|
|
2446
|
+
// clear on done/restart
|
|
2447
|
+
lastEventAt: Date.now()
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
case "fleetBudgetWarning": {
|
|
2453
|
+
const cur = state.fleet[action.id];
|
|
2454
|
+
if (!cur) return state;
|
|
2455
|
+
return {
|
|
2456
|
+
...state,
|
|
2457
|
+
fleet: {
|
|
2458
|
+
...state.fleet,
|
|
2459
|
+
[action.id]: {
|
|
2460
|
+
...cur,
|
|
2461
|
+
budgetWarning: { kind: action.kind, used: action.used, limit: action.limit, at: Date.now() },
|
|
2426
2462
|
lastEventAt: Date.now()
|
|
2427
2463
|
}
|
|
2428
2464
|
}
|
|
@@ -2552,6 +2588,10 @@ function App({
|
|
|
2552
2588
|
banner = true,
|
|
2553
2589
|
queueStore,
|
|
2554
2590
|
yolo = false,
|
|
2591
|
+
getYolo,
|
|
2592
|
+
getAutonomy,
|
|
2593
|
+
getSDDContext,
|
|
2594
|
+
onSDDOutput,
|
|
2555
2595
|
appVersion,
|
|
2556
2596
|
provider,
|
|
2557
2597
|
family,
|
|
@@ -2570,6 +2610,8 @@ function App({
|
|
|
2570
2610
|
const { exit } = useApp();
|
|
2571
2611
|
const [liveModel, setLiveModel] = useState(model);
|
|
2572
2612
|
const [liveProvider, setLiveProvider] = useState(provider ?? "agent");
|
|
2613
|
+
const [yoloLive, setYoloLive] = useState(yolo);
|
|
2614
|
+
const [autonomyLive, setAutonomyLive] = useState(getAutonomy?.() ?? "off");
|
|
2573
2615
|
const [state, dispatch] = useReducer(reducer, {
|
|
2574
2616
|
entries: banner ? [
|
|
2575
2617
|
{
|
|
@@ -2622,7 +2664,7 @@ function App({
|
|
|
2622
2664
|
const inputGateRef = useRef(false);
|
|
2623
2665
|
const lastEnterAtRef = useRef(0);
|
|
2624
2666
|
const projectRoot = agent.ctx.projectRoot;
|
|
2625
|
-
const projectName =
|
|
2667
|
+
const projectName = React2.useMemo(() => {
|
|
2626
2668
|
const base = path2.basename(projectRoot);
|
|
2627
2669
|
return base && base !== path2.sep ? base : void 0;
|
|
2628
2670
|
}, [projectRoot]);
|
|
@@ -2642,13 +2684,13 @@ function App({
|
|
|
2642
2684
|
dispatch({ type: "clearInput" });
|
|
2643
2685
|
};
|
|
2644
2686
|
const startedAtRef = useRef(Date.now());
|
|
2645
|
-
const [nowTick, setNowTick] =
|
|
2687
|
+
const [nowTick, setNowTick] = React2.useState(Date.now());
|
|
2646
2688
|
useEffect(() => {
|
|
2647
2689
|
const t = setInterval(() => setNowTick(Date.now()), 1e3);
|
|
2648
2690
|
return () => clearInterval(t);
|
|
2649
2691
|
}, []);
|
|
2650
2692
|
const elapsedMs = nowTick - startedAtRef.current;
|
|
2651
|
-
const [gitInfo, setGitInfo] =
|
|
2693
|
+
const [gitInfo, setGitInfo] = React2.useState(null);
|
|
2652
2694
|
useEffect(() => {
|
|
2653
2695
|
let cancelled = false;
|
|
2654
2696
|
const refresh = () => {
|
|
@@ -2663,12 +2705,13 @@ function App({
|
|
|
2663
2705
|
clearInterval(t);
|
|
2664
2706
|
};
|
|
2665
2707
|
}, [agent.ctx.cwd]);
|
|
2666
|
-
|
|
2708
|
+
(tokenCounter?.total().input ?? 0) + (tokenCounter?.total().cacheRead ?? 0) + (tokenCounter?.total().cacheWrite ?? 0);
|
|
2667
2709
|
const maxContext = effectiveMaxContext ?? agent.ctx.provider.capabilities.maxContext;
|
|
2710
|
+
const currentContextTokens = (tokenCounter?.currentRequestTokens()?.input ?? 0) + (tokenCounter?.currentRequestTokens()?.cacheRead ?? 0);
|
|
2668
2711
|
const contextWindow = useMemo(() => {
|
|
2669
2712
|
void state.contextChipVersion;
|
|
2670
|
-
return
|
|
2671
|
-
}, [
|
|
2713
|
+
return currentContextTokens > 0 && maxContext > 0 ? { used: currentContextTokens, max: maxContext } : void 0;
|
|
2714
|
+
}, [currentContextTokens, maxContext, state.contextChipVersion]);
|
|
2672
2715
|
const todos = useMemo(() => {
|
|
2673
2716
|
const counts = { pending: 0, inProgress: 0, completed: 0 };
|
|
2674
2717
|
for (const t of agent.ctx.todos) {
|
|
@@ -3264,6 +3307,20 @@ function App({
|
|
|
3264
3307
|
}
|
|
3265
3308
|
});
|
|
3266
3309
|
});
|
|
3310
|
+
const offBudgetWarning = events.on("subagent.budget_warning", (e) => {
|
|
3311
|
+
const lbl = labelFor(e.subagentId);
|
|
3312
|
+
dispatch({ type: "fleetBudgetWarning", id: e.subagentId, kind: e.kind, used: e.used, limit: e.limit });
|
|
3313
|
+
dispatch({
|
|
3314
|
+
type: "addEntry",
|
|
3315
|
+
entry: {
|
|
3316
|
+
kind: "subagent",
|
|
3317
|
+
agentLabel: lbl.label,
|
|
3318
|
+
agentColor: lbl.color,
|
|
3319
|
+
icon: "\u26A1",
|
|
3320
|
+
text: `hitting ${e.kind} limit (${e.used}/${e.limit}) \u2014 extending`
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3323
|
+
});
|
|
3267
3324
|
const offTool = events.on("subagent.tool_executed", (e) => {
|
|
3268
3325
|
if (director) return;
|
|
3269
3326
|
dispatch({
|
|
@@ -3280,6 +3337,7 @@ function App({
|
|
|
3280
3337
|
offSpawned();
|
|
3281
3338
|
offStarted();
|
|
3282
3339
|
offCompleted();
|
|
3340
|
+
offBudgetWarning();
|
|
3283
3341
|
offTool();
|
|
3284
3342
|
};
|
|
3285
3343
|
}, [events, director]);
|
|
@@ -3513,7 +3571,7 @@ function App({
|
|
|
3513
3571
|
return () => {
|
|
3514
3572
|
process.off("SIGINT", onSigint);
|
|
3515
3573
|
};
|
|
3516
|
-
}, [
|
|
3574
|
+
}, [director]);
|
|
3517
3575
|
const handleKey = async (input, key) => {
|
|
3518
3576
|
if (state.status === "aborting" && state.interrupts === 0) return;
|
|
3519
3577
|
if (state.confirmQueue.length > 0) return;
|
|
@@ -3840,6 +3898,15 @@ function App({
|
|
|
3840
3898
|
entry: { kind: "warn", text: `Hit max iterations (${result.iterations}).` }
|
|
3841
3899
|
});
|
|
3842
3900
|
}
|
|
3901
|
+
if (result.status === "done" && result.finalText && onSDDOutput) {
|
|
3902
|
+
try {
|
|
3903
|
+
const sddMessages = await onSDDOutput(result.finalText);
|
|
3904
|
+
for (const msg of sddMessages) {
|
|
3905
|
+
dispatch({ type: "addEntry", entry: { kind: "info", text: msg } });
|
|
3906
|
+
}
|
|
3907
|
+
} catch {
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3843
3910
|
if (tokenCounter && before) {
|
|
3844
3911
|
const after = tokenCounter.total();
|
|
3845
3912
|
const costAfter = tokenCounter.estimateCost().total;
|
|
@@ -3895,6 +3962,14 @@ function App({
|
|
|
3895
3962
|
if (ctxModel && ctxModel !== liveModel) setLiveModel(ctxModel);
|
|
3896
3963
|
const ctxProviderId = agent.ctx.provider?.id;
|
|
3897
3964
|
if (ctxProviderId && ctxProviderId !== liveProvider) setLiveProvider(ctxProviderId);
|
|
3965
|
+
if (getYolo) {
|
|
3966
|
+
const currentYolo = getYolo();
|
|
3967
|
+
if (currentYolo !== yoloLive) setYoloLive(currentYolo);
|
|
3968
|
+
}
|
|
3969
|
+
if (getAutonomy) {
|
|
3970
|
+
const currentAutonomy = getAutonomy();
|
|
3971
|
+
if (currentAutonomy !== autonomyLive) setAutonomyLive(currentAutonomy);
|
|
3972
|
+
}
|
|
3898
3973
|
if (res?.exit) {
|
|
3899
3974
|
exit();
|
|
3900
3975
|
onExit(0);
|
|
@@ -3926,6 +4001,15 @@ function App({
|
|
|
3926
4001
|
const builder = builderRef.current;
|
|
3927
4002
|
if (!builder) return;
|
|
3928
4003
|
const steering = state.steeringPending;
|
|
4004
|
+
const sddContext = getSDDContext?.();
|
|
4005
|
+
if (sddContext && trimmed) {
|
|
4006
|
+
builder.appendText(`[SDD SESSION ACTIVE]
|
|
4007
|
+
${sddContext}
|
|
4008
|
+
|
|
4009
|
+
---
|
|
4010
|
+
User message:
|
|
4011
|
+
`);
|
|
4012
|
+
}
|
|
3929
4013
|
if (trimmed) {
|
|
3930
4014
|
const toAppend = steering ? buildSteeringPreamble(state.steerSnapshot, trimmed) : trimmed;
|
|
3931
4015
|
builder.appendText(toAppend);
|
|
@@ -4056,7 +4140,8 @@ function App({
|
|
|
4056
4140
|
tokenCounter,
|
|
4057
4141
|
hint: renderRunningTools(state.runningTools) || state.hint,
|
|
4058
4142
|
queueCount: state.queue.length,
|
|
4059
|
-
yolo,
|
|
4143
|
+
yolo: yoloLive,
|
|
4144
|
+
autonomy: autonomyLive,
|
|
4060
4145
|
elapsedMs,
|
|
4061
4146
|
todos,
|
|
4062
4147
|
plan: planCounts ?? void 0,
|
|
@@ -4179,7 +4264,7 @@ async function runTui(opts) {
|
|
|
4179
4264
|
let instance;
|
|
4180
4265
|
try {
|
|
4181
4266
|
instance = render(
|
|
4182
|
-
|
|
4267
|
+
React2.createElement(App, {
|
|
4183
4268
|
agent: opts.agent,
|
|
4184
4269
|
slashRegistry: opts.slashRegistry,
|
|
4185
4270
|
attachments: opts.attachments,
|
|
@@ -4191,6 +4276,8 @@ async function runTui(opts) {
|
|
|
4191
4276
|
banner: opts.banner ?? true,
|
|
4192
4277
|
queueStore: opts.queueStore,
|
|
4193
4278
|
yolo: opts.yolo,
|
|
4279
|
+
getYolo: opts.getYolo,
|
|
4280
|
+
getAutonomy: opts.getAutonomy,
|
|
4194
4281
|
appVersion: opts.appVersion,
|
|
4195
4282
|
provider: opts.provider,
|
|
4196
4283
|
family: opts.family,
|
|
@@ -4204,7 +4291,9 @@ async function runTui(opts) {
|
|
|
4204
4291
|
onClearHistory: opts.onClearHistory ? (dispatch) => opts.onClearHistory(dispatch) : void 0,
|
|
4205
4292
|
fleetStreamController: opts.fleetStreamController,
|
|
4206
4293
|
initialGoal: opts.initialGoal,
|
|
4207
|
-
initialAsk: opts.initialAsk
|
|
4294
|
+
initialAsk: opts.initialAsk,
|
|
4295
|
+
getSDDContext: opts.getSDDContext,
|
|
4296
|
+
onSDDOutput: opts.onSDDOutput
|
|
4208
4297
|
}),
|
|
4209
4298
|
{ exitOnCtrlC: false }
|
|
4210
4299
|
);
|