takomi 2.1.2 → 2.1.4
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/.pi/README.md +124 -124
- package/.pi/agents/architect.md +15 -15
- package/.pi/agents/coder.md +14 -14
- package/.pi/agents/designer.md +17 -17
- package/.pi/agents/orchestrator.md +22 -22
- package/.pi/agents/reviewer.md +16 -16
- package/.pi/extensions/oauth-router/README.md +125 -125
- package/.pi/extensions/oauth-router/commands.ts +380 -380
- package/.pi/extensions/oauth-router/config.ts +200 -200
- package/.pi/extensions/oauth-router/index.ts +41 -41
- package/.pi/extensions/oauth-router/oauth-flow.ts +154 -154
- package/.pi/extensions/oauth-router/oauth-store.ts +121 -121
- package/.pi/extensions/oauth-router/package.json +14 -14
- package/.pi/extensions/oauth-router/policies.ts +27 -27
- package/.pi/extensions/oauth-router/provider.ts +492 -492
- package/.pi/extensions/oauth-router/scripts/vibe-verify.py +98 -98
- package/.pi/extensions/oauth-router/state.ts +174 -174
- package/.pi/extensions/oauth-router/types.ts +153 -153
- package/.pi/extensions/takomi-runtime/command-text.ts +130 -130
- package/.pi/extensions/takomi-runtime/commands.ts +179 -179
- package/.pi/extensions/takomi-runtime/context-panel.ts +282 -282
- package/.pi/extensions/takomi-runtime/index.ts +1288 -1288
- package/.pi/extensions/takomi-runtime/profile.ts +114 -114
- package/.pi/extensions/takomi-runtime/routing-policy.ts +105 -105
- package/.pi/extensions/takomi-runtime/shared.ts +511 -492
- package/.pi/extensions/takomi-runtime/subagent-controller.ts +364 -364
- package/.pi/extensions/takomi-runtime/subagent-render.ts +501 -501
- package/.pi/extensions/takomi-runtime/subagent-types.ts +90 -83
- package/.pi/extensions/takomi-runtime/ui.ts +133 -133
- package/.pi/extensions/takomi-subagents/agent-aliases.ts +18 -18
- package/.pi/extensions/takomi-subagents/agents.ts +113 -113
- package/.pi/extensions/takomi-subagents/delegation-plan.ts +95 -95
- package/.pi/extensions/takomi-subagents/dispatch-helpers.ts +26 -26
- package/.pi/extensions/takomi-subagents/dispatch.ts +306 -215
- package/.pi/extensions/takomi-subagents/index.ts +76 -75
- package/.pi/extensions/takomi-subagents/live-updates.ts +136 -83
- package/.pi/extensions/takomi-subagents/native-render.ts +5 -142
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +228 -0
- package/.pi/extensions/takomi-subagents/tool-runner.ts +209 -209
- package/.pi/themes/takomi-noir.json +81 -81
- package/package.json +59 -59
- package/src/cli.js +14 -0
- package/src/doctor.js +87 -84
- package/src/pi-harness.js +355 -351
- package/src/pi-installer.js +193 -171
- package/src/pi-takomi-core/index.ts +4 -4
- package/src/pi-takomi-core/orchestration.ts +402 -402
- package/src/pi-takomi-core/routing.ts +93 -93
- package/src/pi-takomi-core/types.ts +173 -173
- package/src/pi-takomi-core/workflows.ts +299 -299
- package/src/skills-installer.js +101 -101
- package/src/update-check.js +140 -0
|
@@ -1,83 +1,90 @@
|
|
|
1
|
-
import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
|
|
2
|
-
import type { ChecklistInput } from "./shared";
|
|
3
|
-
|
|
4
|
-
export type SubagentViewMode = "compact" | "expanded" | "fullscreen";
|
|
5
|
-
export type SubagentFocusDirection = "next" | "prev";
|
|
6
|
-
export type TakomiSubagentStatus = "running" | "completed" | "blocked";
|
|
7
|
-
export type TakomiBoardTaskStatus = "pending" | "in-progress" | "completed" | "blocked";
|
|
8
|
-
export type TakomiSubagentSource = "runtime-board" | "takomi-tool";
|
|
9
|
-
export const TAKOMI_SUBAGENT_EVENT_CHANNEL = "takomi:subagent-runtime";
|
|
10
|
-
|
|
11
|
-
export type TakomiSubagentRun = {
|
|
12
|
-
runKey: string;
|
|
13
|
-
conversationId?: string;
|
|
14
|
-
parentTaskId?: string;
|
|
15
|
-
parentRunKey?: string;
|
|
16
|
-
agent: string;
|
|
17
|
-
taskLabel: string;
|
|
18
|
-
status: TakomiSubagentStatus;
|
|
19
|
-
stage?: string;
|
|
20
|
-
workflow?: string;
|
|
21
|
-
model?: string;
|
|
22
|
-
fallbackModels?: string[];
|
|
23
|
-
thinking?: TakomiThinkingLevel;
|
|
24
|
-
checklist?: ChecklistInput;
|
|
25
|
-
boardTaskStatus?: TakomiBoardTaskStatus;
|
|
26
|
-
summary?: string;
|
|
27
|
-
outputText?: string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
logs
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
|
|
2
|
+
import type { ChecklistInput } from "./shared";
|
|
3
|
+
|
|
4
|
+
export type SubagentViewMode = "compact" | "expanded" | "fullscreen";
|
|
5
|
+
export type SubagentFocusDirection = "next" | "prev";
|
|
6
|
+
export type TakomiSubagentStatus = "running" | "completed" | "blocked";
|
|
7
|
+
export type TakomiBoardTaskStatus = "pending" | "in-progress" | "completed" | "blocked";
|
|
8
|
+
export type TakomiSubagentSource = "runtime-board" | "takomi-tool";
|
|
9
|
+
export const TAKOMI_SUBAGENT_EVENT_CHANNEL = "takomi:subagent-runtime";
|
|
10
|
+
|
|
11
|
+
export type TakomiSubagentRun = {
|
|
12
|
+
runKey: string;
|
|
13
|
+
conversationId?: string;
|
|
14
|
+
parentTaskId?: string;
|
|
15
|
+
parentRunKey?: string;
|
|
16
|
+
agent: string;
|
|
17
|
+
taskLabel: string;
|
|
18
|
+
status: TakomiSubagentStatus;
|
|
19
|
+
stage?: string;
|
|
20
|
+
workflow?: string;
|
|
21
|
+
model?: string;
|
|
22
|
+
fallbackModels?: string[];
|
|
23
|
+
thinking?: TakomiThinkingLevel;
|
|
24
|
+
checklist?: ChecklistInput;
|
|
25
|
+
boardTaskStatus?: TakomiBoardTaskStatus;
|
|
26
|
+
summary?: string;
|
|
27
|
+
outputText?: string;
|
|
28
|
+
currentTool?: string;
|
|
29
|
+
currentToolArgs?: string;
|
|
30
|
+
currentToolStartedAt?: number;
|
|
31
|
+
recentTools?: Array<{ tool: string; args: string; endMs: number }>;
|
|
32
|
+
recentOutput?: string[];
|
|
33
|
+
toolCount?: number;
|
|
34
|
+
sessionFile?: string;
|
|
35
|
+
logs: string[];
|
|
36
|
+
startedAt: number;
|
|
37
|
+
updatedAt: number;
|
|
38
|
+
source: TakomiSubagentSource;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type TakomiSubagentRunInit = Omit<TakomiSubagentRun, "runKey" | "logs" | "startedAt" | "updatedAt" | "status"> & {
|
|
42
|
+
logs?: string[];
|
|
43
|
+
status?: TakomiSubagentStatus;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type TakomiSubagentRunPatch = Partial<Omit<TakomiSubagentRun, "runKey" | "startedAt" | "source">> & {
|
|
47
|
+
source?: TakomiSubagentSource;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type TakomiSubagentRenderEntry = {
|
|
51
|
+
run: TakomiSubagentRun;
|
|
52
|
+
depth: number;
|
|
53
|
+
relation: "focused" | "ancestor" | "peer";
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type TakomiSubagentRenderState = {
|
|
57
|
+
mode: SubagentViewMode;
|
|
58
|
+
activeCount: number;
|
|
59
|
+
focusPosition: number;
|
|
60
|
+
focusedRun?: TakomiSubagentRun;
|
|
61
|
+
activePath: TakomiSubagentRenderEntry[];
|
|
62
|
+
peerRuns: TakomiSubagentRenderEntry[];
|
|
63
|
+
compactRuns: TakomiSubagentRenderEntry[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export interface TakomiSubagentController {
|
|
67
|
+
hasRuns(): boolean;
|
|
68
|
+
getStatusSummary(): string;
|
|
69
|
+
getViewMode(): SubagentViewMode;
|
|
70
|
+
start(ctx: import("@mariozechner/pi-coding-agent").ExtensionContext, state: TakomiSubagentRunInit, runKey?: string): Promise<void>;
|
|
71
|
+
update(ctx: import("@mariozechner/pi-coding-agent").ExtensionContext, patch: TakomiSubagentRunPatch, runKey?: string): Promise<void>;
|
|
72
|
+
appendLog(ctx: import("@mariozechner/pi-coding-agent").ExtensionContext, chunk: string, runKey?: string): Promise<void>;
|
|
73
|
+
complete(ctx: import("@mariozechner/pi-coding-agent").ExtensionContext, patch?: TakomiSubagentRunPatch, runKey?: string): Promise<void>;
|
|
74
|
+
block(ctx: import("@mariozechner/pi-coding-agent").ExtensionContext, patch?: TakomiSubagentRunPatch, runKey?: string): Promise<void>;
|
|
75
|
+
reset(ctx?: import("@mariozechner/pi-coding-agent").ExtensionContext): void;
|
|
76
|
+
refresh(): void;
|
|
77
|
+
refreshWithContext(ctx: import("@mariozechner/pi-coding-agent").ExtensionContext): void;
|
|
78
|
+
cycleFocus(direction: SubagentFocusDirection, ctx?: import("@mariozechner/pi-coding-agent").ExtensionContext): boolean;
|
|
79
|
+
setViewMode(mode: SubagentViewMode, ctx?: import("@mariozechner/pi-coding-agent").ExtensionContext): SubagentViewMode | undefined;
|
|
80
|
+
cycleViewMode(ctx?: import("@mariozechner/pi-coding-agent").ExtensionContext): SubagentViewMode | undefined;
|
|
81
|
+
closeFullscreen(ctx?: import("@mariozechner/pi-coding-agent").ExtensionContext): SubagentViewMode;
|
|
82
|
+
getKnownParentRunKey(parentTaskId: string): string | undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type TakomiSubagentRuntimeEvent =
|
|
86
|
+
| { type: "start"; runKey?: string; state: TakomiSubagentRunInit }
|
|
87
|
+
| { type: "update"; runKey?: string; patch: TakomiSubagentRunPatch }
|
|
88
|
+
| { type: "appendLog"; runKey?: string; chunk: string }
|
|
89
|
+
| { type: "complete"; runKey?: string; patch?: TakomiSubagentRunPatch }
|
|
90
|
+
| { type: "block"; runKey?: string; patch?: TakomiSubagentRunPatch };
|
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
|
2
|
-
import type { ExtensionContext, ReadonlyFooterDataProvider, Theme } from "@mariozechner/pi-coding-agent";
|
|
3
|
-
import {
|
|
4
|
-
ellipsizeMiddle,
|
|
5
|
-
formatFooterNumber,
|
|
6
|
-
truncateToWidth,
|
|
7
|
-
visibleWidth,
|
|
8
|
-
} from "./shared";
|
|
9
|
-
|
|
10
|
-
interface Component {
|
|
11
|
-
render(width: number): string[];
|
|
12
|
-
invalidate(): void;
|
|
13
|
-
dispose?(): void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface RenderTui {
|
|
17
|
-
requestRender(force?: boolean): void;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type RuntimeHudState = {
|
|
21
|
-
enabled: boolean;
|
|
22
|
-
autoOrch: boolean;
|
|
23
|
-
planMode: boolean;
|
|
24
|
-
role: string;
|
|
25
|
-
stage?: string;
|
|
26
|
-
workflow?: string;
|
|
27
|
-
activeSessionId?: string;
|
|
28
|
-
launchMode?: string;
|
|
29
|
-
subagentsEnabled?: boolean;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type Tone = "accent" | "warning" | "success" | "error" | "muted" | "dim" | "thinkingMinimal";
|
|
33
|
-
|
|
34
|
-
function stageTone(stage?: string): Tone {
|
|
35
|
-
switch (stage) {
|
|
36
|
-
case "genesis":
|
|
37
|
-
return "thinkingMinimal";
|
|
38
|
-
case "design":
|
|
39
|
-
return "accent";
|
|
40
|
-
case "build":
|
|
41
|
-
return "warning";
|
|
42
|
-
default:
|
|
43
|
-
return "muted";
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function badge(theme: Theme, label: string, tone: Tone): string {
|
|
48
|
-
return theme.fg(tone, `<${label.toUpperCase()}>`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function renderRuntimeStatus(theme: Theme, state: RuntimeHudState): string {
|
|
52
|
-
const primary = state.stage ?? state.role;
|
|
53
|
-
const stageBadge = badge(theme, primary, stageTone(state.stage));
|
|
54
|
-
const auto = state.autoOrch ? theme.fg("accent", "auto") : theme.fg("dim", "manual");
|
|
55
|
-
const gate = state.launchMode === "manual" ? theme.fg("warning", "review-gate") : theme.fg("accent", "auto-gate");
|
|
56
|
-
const plan = state.planMode ? theme.fg("warning", "plan") : theme.fg("dim", "direct");
|
|
57
|
-
const subagents = state.subagentsEnabled === false ? theme.fg("error", "subagents:off") : theme.fg("dim", "subagents:on");
|
|
58
|
-
return [theme.fg("accent", "Takomi"), stageBadge, theme.fg("dim", `role:${state.role}`), auto, gate, plan, subagents].join(" ");
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function renderRuntimeWidget(theme: Theme, state: RuntimeHudState): string[] {
|
|
62
|
-
if (!state.enabled) return [];
|
|
63
|
-
const primary = state.stage ?? state.role;
|
|
64
|
-
const parts = [
|
|
65
|
-
theme.fg("accent", "Takomi"),
|
|
66
|
-
badge(theme, primary, stageTone(state.stage)),
|
|
67
|
-
theme.fg("dim", `role:${state.role}`),
|
|
68
|
-
state.autoOrch ? theme.fg("accent", "auto") : theme.fg("dim", "manual"),
|
|
69
|
-
state.launchMode === "manual" ? theme.fg("warning", "review-gate") : theme.fg("accent", "auto-gate"),
|
|
70
|
-
state.planMode ? theme.fg("warning", "plan") : theme.fg("dim", "direct"),
|
|
71
|
-
state.subagentsEnabled === false ? theme.fg("error", "subagents:off") : theme.fg("dim", "subagents:on"),
|
|
72
|
-
state.workflow ? theme.fg("dim", `wf:${state.workflow}`) : "",
|
|
73
|
-
state.activeSessionId ? theme.fg("dim", `session:${ellipsizeMiddle(state.activeSessionId, 12)}`) : "",
|
|
74
|
-
].filter(Boolean);
|
|
75
|
-
return [parts.join(" ")];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export class TakomiFooterComponent implements Component {
|
|
79
|
-
private readonly unsubscribeBranchChange: () => void;
|
|
80
|
-
|
|
81
|
-
constructor(
|
|
82
|
-
private readonly tui: RenderTui,
|
|
83
|
-
private readonly theme: Theme,
|
|
84
|
-
private readonly footerData: ReadonlyFooterDataProvider,
|
|
85
|
-
private readonly ctx: ExtensionContext,
|
|
86
|
-
private readonly getState: () => RuntimeHudState,
|
|
87
|
-
) {
|
|
88
|
-
this.unsubscribeBranchChange = footerData.onBranchChange(() => {
|
|
89
|
-
this.tui.requestRender();
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
dispose(): void {
|
|
94
|
-
this.unsubscribeBranchChange();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
invalidate(): void {}
|
|
98
|
-
|
|
99
|
-
render(width: number): string[] {
|
|
100
|
-
const state = this.getState();
|
|
101
|
-
let input = 0;
|
|
102
|
-
let output = 0;
|
|
103
|
-
let cost = 0;
|
|
104
|
-
|
|
105
|
-
for (const entry of this.ctx.sessionManager.getBranch()) {
|
|
106
|
-
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
107
|
-
const message = entry.message as AssistantMessage;
|
|
108
|
-
input += message.usage.input;
|
|
109
|
-
output += message.usage.output;
|
|
110
|
-
cost += message.usage.cost.total;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const cwd = this.theme.fg("dim", this.ctx.cwd);
|
|
115
|
-
const stats = this.theme.fg("dim", `up:${formatFooterNumber(input)} down:${formatFooterNumber(output)} $${cost.toFixed(3)}`);
|
|
116
|
-
const leftPad = " ".repeat(Math.max(1, width - visibleWidth(cwd) - visibleWidth(stats)));
|
|
117
|
-
const topLine = truncateToWidth(`${cwd}${leftPad}${stats}`, width);
|
|
118
|
-
|
|
119
|
-
const extensionStatuses = [...this.footerData.getExtensionStatuses().entries()]
|
|
120
|
-
.filter(([key]) => key !== "takomi-runtime")
|
|
121
|
-
.map(([, value]) => value)
|
|
122
|
-
.filter(Boolean);
|
|
123
|
-
const runtimeStatus = renderRuntimeStatus(this.theme, state);
|
|
124
|
-
const left = [runtimeStatus, ...extensionStatuses].join(this.theme.fg("dim", " | "));
|
|
125
|
-
const branch = this.footerData.getGitBranch();
|
|
126
|
-
const rightText = [this.ctx.model?.id || "no-model", branch ? `git:${branch}` : ""].filter(Boolean).join(" | ");
|
|
127
|
-
const right = this.theme.fg("dim", rightText);
|
|
128
|
-
const rightPad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
|
|
129
|
-
const bottomLine = truncateToWidth(`${left}${rightPad}${right}`, width);
|
|
130
|
-
|
|
131
|
-
return [topLine, bottomLine];
|
|
132
|
-
}
|
|
133
|
-
}
|
|
1
|
+
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
|
2
|
+
import type { ExtensionContext, ReadonlyFooterDataProvider, Theme } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import {
|
|
4
|
+
ellipsizeMiddle,
|
|
5
|
+
formatFooterNumber,
|
|
6
|
+
truncateToWidth,
|
|
7
|
+
visibleWidth,
|
|
8
|
+
} from "./shared";
|
|
9
|
+
|
|
10
|
+
interface Component {
|
|
11
|
+
render(width: number): string[];
|
|
12
|
+
invalidate(): void;
|
|
13
|
+
dispose?(): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface RenderTui {
|
|
17
|
+
requestRender(force?: boolean): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type RuntimeHudState = {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
autoOrch: boolean;
|
|
23
|
+
planMode: boolean;
|
|
24
|
+
role: string;
|
|
25
|
+
stage?: string;
|
|
26
|
+
workflow?: string;
|
|
27
|
+
activeSessionId?: string;
|
|
28
|
+
launchMode?: string;
|
|
29
|
+
subagentsEnabled?: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type Tone = "accent" | "warning" | "success" | "error" | "muted" | "dim" | "thinkingMinimal";
|
|
33
|
+
|
|
34
|
+
function stageTone(stage?: string): Tone {
|
|
35
|
+
switch (stage) {
|
|
36
|
+
case "genesis":
|
|
37
|
+
return "thinkingMinimal";
|
|
38
|
+
case "design":
|
|
39
|
+
return "accent";
|
|
40
|
+
case "build":
|
|
41
|
+
return "warning";
|
|
42
|
+
default:
|
|
43
|
+
return "muted";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function badge(theme: Theme, label: string, tone: Tone): string {
|
|
48
|
+
return theme.fg(tone, `<${label.toUpperCase()}>`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function renderRuntimeStatus(theme: Theme, state: RuntimeHudState): string {
|
|
52
|
+
const primary = state.stage ?? state.role;
|
|
53
|
+
const stageBadge = badge(theme, primary, stageTone(state.stage));
|
|
54
|
+
const auto = state.autoOrch ? theme.fg("accent", "auto") : theme.fg("dim", "manual");
|
|
55
|
+
const gate = state.launchMode === "manual" ? theme.fg("warning", "review-gate") : theme.fg("accent", "auto-gate");
|
|
56
|
+
const plan = state.planMode ? theme.fg("warning", "plan") : theme.fg("dim", "direct");
|
|
57
|
+
const subagents = state.subagentsEnabled === false ? theme.fg("error", "subagents:off") : theme.fg("dim", "subagents:on");
|
|
58
|
+
return [theme.fg("accent", "Takomi"), stageBadge, theme.fg("dim", `role:${state.role}`), auto, gate, plan, subagents].join(" ");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function renderRuntimeWidget(theme: Theme, state: RuntimeHudState): string[] {
|
|
62
|
+
if (!state.enabled) return [];
|
|
63
|
+
const primary = state.stage ?? state.role;
|
|
64
|
+
const parts = [
|
|
65
|
+
theme.fg("accent", "Takomi"),
|
|
66
|
+
badge(theme, primary, stageTone(state.stage)),
|
|
67
|
+
theme.fg("dim", `role:${state.role}`),
|
|
68
|
+
state.autoOrch ? theme.fg("accent", "auto") : theme.fg("dim", "manual"),
|
|
69
|
+
state.launchMode === "manual" ? theme.fg("warning", "review-gate") : theme.fg("accent", "auto-gate"),
|
|
70
|
+
state.planMode ? theme.fg("warning", "plan") : theme.fg("dim", "direct"),
|
|
71
|
+
state.subagentsEnabled === false ? theme.fg("error", "subagents:off") : theme.fg("dim", "subagents:on"),
|
|
72
|
+
state.workflow ? theme.fg("dim", `wf:${state.workflow}`) : "",
|
|
73
|
+
state.activeSessionId ? theme.fg("dim", `session:${ellipsizeMiddle(state.activeSessionId, 12)}`) : "",
|
|
74
|
+
].filter(Boolean);
|
|
75
|
+
return [parts.join(" ")];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class TakomiFooterComponent implements Component {
|
|
79
|
+
private readonly unsubscribeBranchChange: () => void;
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
private readonly tui: RenderTui,
|
|
83
|
+
private readonly theme: Theme,
|
|
84
|
+
private readonly footerData: ReadonlyFooterDataProvider,
|
|
85
|
+
private readonly ctx: ExtensionContext,
|
|
86
|
+
private readonly getState: () => RuntimeHudState,
|
|
87
|
+
) {
|
|
88
|
+
this.unsubscribeBranchChange = footerData.onBranchChange(() => {
|
|
89
|
+
this.tui.requestRender();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
dispose(): void {
|
|
94
|
+
this.unsubscribeBranchChange();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
invalidate(): void {}
|
|
98
|
+
|
|
99
|
+
render(width: number): string[] {
|
|
100
|
+
const state = this.getState();
|
|
101
|
+
let input = 0;
|
|
102
|
+
let output = 0;
|
|
103
|
+
let cost = 0;
|
|
104
|
+
|
|
105
|
+
for (const entry of this.ctx.sessionManager.getBranch()) {
|
|
106
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
107
|
+
const message = entry.message as AssistantMessage;
|
|
108
|
+
input += message.usage.input;
|
|
109
|
+
output += message.usage.output;
|
|
110
|
+
cost += message.usage.cost.total;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const cwd = this.theme.fg("dim", this.ctx.cwd);
|
|
115
|
+
const stats = this.theme.fg("dim", `up:${formatFooterNumber(input)} down:${formatFooterNumber(output)} $${cost.toFixed(3)}`);
|
|
116
|
+
const leftPad = " ".repeat(Math.max(1, width - visibleWidth(cwd) - visibleWidth(stats)));
|
|
117
|
+
const topLine = truncateToWidth(`${cwd}${leftPad}${stats}`, width);
|
|
118
|
+
|
|
119
|
+
const extensionStatuses = [...this.footerData.getExtensionStatuses().entries()]
|
|
120
|
+
.filter(([key]) => key !== "takomi-runtime")
|
|
121
|
+
.map(([, value]) => value)
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
const runtimeStatus = renderRuntimeStatus(this.theme, state);
|
|
124
|
+
const left = [runtimeStatus, ...extensionStatuses].join(this.theme.fg("dim", " | "));
|
|
125
|
+
const branch = this.footerData.getGitBranch();
|
|
126
|
+
const rightText = [this.ctx.model?.id || "no-model", branch ? `git:${branch}` : ""].filter(Boolean).join(" | ");
|
|
127
|
+
const right = this.theme.fg("dim", rightText);
|
|
128
|
+
const rightPad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
|
|
129
|
+
const bottomLine = truncateToWidth(`${left}${rightPad}${right}`, width);
|
|
130
|
+
|
|
131
|
+
return [topLine, bottomLine];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type { TakomiAgentConfig } from "./agents";
|
|
2
|
-
|
|
3
|
-
const AGENT_ALIASES: Record<string, string[]> = {
|
|
4
|
-
general: ["orchestrator", "coder", "architect"],
|
|
5
|
-
code: ["coder"],
|
|
6
|
-
build: ["coder", "orchestrator"],
|
|
7
|
-
design: ["designer"],
|
|
8
|
-
review: ["reviewer"],
|
|
9
|
-
architecture: ["architect"],
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function resolveAgentName(name: string, agents: Map<string, TakomiAgentConfig>): string {
|
|
13
|
-
if (agents.has(name)) return name;
|
|
14
|
-
const lower = name.toLowerCase();
|
|
15
|
-
const exactCase = [...agents.keys()].find((agentName) => agentName.toLowerCase() === lower);
|
|
16
|
-
if (exactCase) return exactCase;
|
|
17
|
-
return AGENT_ALIASES[lower]?.find((candidate) => agents.has(candidate)) ?? name;
|
|
18
|
-
}
|
|
1
|
+
import type { TakomiAgentConfig } from "./agents";
|
|
2
|
+
|
|
3
|
+
const AGENT_ALIASES: Record<string, string[]> = {
|
|
4
|
+
general: ["orchestrator", "coder", "architect"],
|
|
5
|
+
code: ["coder"],
|
|
6
|
+
build: ["coder", "orchestrator"],
|
|
7
|
+
design: ["designer"],
|
|
8
|
+
review: ["reviewer"],
|
|
9
|
+
architecture: ["architect"],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function resolveAgentName(name: string, agents: Map<string, TakomiAgentConfig>): string {
|
|
13
|
+
if (agents.has(name)) return name;
|
|
14
|
+
const lower = name.toLowerCase();
|
|
15
|
+
const exactCase = [...agents.keys()].find((agentName) => agentName.toLowerCase() === lower);
|
|
16
|
+
if (exactCase) return exactCase;
|
|
17
|
+
return AGENT_ALIASES[lower]?.find((candidate) => agents.has(candidate)) ?? name;
|
|
18
|
+
}
|