@mrciphersmith/keryx 0.2.139 → 0.2.143
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 +10 -0
- package/dist/cli.js +844 -79
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -248,6 +248,16 @@ What is in it today:
|
|
|
248
248
|
session that keeps its ancestry, without editing a transcript by hand.
|
|
249
249
|
`/status` is the session inspector (identity, context window and limits when
|
|
250
250
|
the provider reported them); `/session-info` and `/info` are not aliases.
|
|
251
|
+
- **Plans that survive the turn.** For multi-step work, the agent can keep a
|
|
252
|
+
structured execution plan in the session. The TUI shows pending, active,
|
|
253
|
+
completed, blocked, and skipped steps in the sidebar, and restored sessions
|
|
254
|
+
continue from the same plan instead of rebuilding it from transcript text.
|
|
255
|
+
- **Theme-aware rich transcripts.** Assistant prose, headings, emphasis,
|
|
256
|
+
inline code, fenced code, diffs, and GFM pipe tables use semantic colors
|
|
257
|
+
derived from the selected `/theme`. Use a language fence such as
|
|
258
|
+
```` ```typescript ```` for syntax color, ```` ```diff ```` for addition and
|
|
259
|
+
deletion rows, or ```` ```text ````/```` ```txt ```` when content must remain
|
|
260
|
+
literal, including terminal output and ASCII diagrams.
|
|
251
261
|
- **Responsive busy-turn UX.** A running main turn can be interrupted with
|
|
252
262
|
`/interrupt`; additional prompts are queued and answered as read-only side workers
|
|
253
263
|
(`side-1`) in the TUI so the shell stays usable under long-running turns.
|
package/dist/cli.js
CHANGED
|
@@ -23987,7 +23987,7 @@ async function openSlateAtomic(dir, mintAttemptId, build) {
|
|
|
23987
23987
|
if (existing !== undefined) {
|
|
23988
23988
|
await archiveIfExistsLocked(dir, mintAttemptId());
|
|
23989
23989
|
}
|
|
23990
|
-
const next = build();
|
|
23990
|
+
const next = build(existing);
|
|
23991
23991
|
await writeFileAtomic(slatePath(dir), `${JSON.stringify(next, null, 2)}
|
|
23992
23992
|
`);
|
|
23993
23993
|
await rm7(path93.join(dir, "terminal-state.json"), { force: true });
|
|
@@ -24089,6 +24089,123 @@ var init_slate = __esm(() => {
|
|
|
24089
24089
|
];
|
|
24090
24090
|
});
|
|
24091
24091
|
|
|
24092
|
+
// src/session/execution-plan.ts
|
|
24093
|
+
function clonePlan(plan) {
|
|
24094
|
+
return { revision: plan.revision, items: plan.items.map((item) => ({ ...item })) };
|
|
24095
|
+
}
|
|
24096
|
+
function validateItems(items) {
|
|
24097
|
+
const ids = new Set;
|
|
24098
|
+
let active = 0;
|
|
24099
|
+
return items.map((item) => {
|
|
24100
|
+
const id = item.id.trim();
|
|
24101
|
+
const title = item.title.trim();
|
|
24102
|
+
if (id.length === 0)
|
|
24103
|
+
throw new ExecutionPlanValidationError("item id must be non-empty");
|
|
24104
|
+
if (ids.has(id))
|
|
24105
|
+
throw new ExecutionPlanValidationError(`duplicate item id: ${id}`);
|
|
24106
|
+
ids.add(id);
|
|
24107
|
+
if (title.length === 0)
|
|
24108
|
+
throw new ExecutionPlanValidationError(`item ${id} has an empty title`);
|
|
24109
|
+
if (!EXECUTION_PLAN_STATUSES.includes(item.status)) {
|
|
24110
|
+
throw new ExecutionPlanValidationError(`item ${id} has unknown status ${JSON.stringify(item.status)}`);
|
|
24111
|
+
}
|
|
24112
|
+
if (item.status === "in_progress" && ++active > 1) {
|
|
24113
|
+
throw new ExecutionPlanValidationError("at most one item may be in_progress");
|
|
24114
|
+
}
|
|
24115
|
+
return { id, title, status: item.status };
|
|
24116
|
+
});
|
|
24117
|
+
}
|
|
24118
|
+
function notify(dir, plan) {
|
|
24119
|
+
for (const listener of listeners) {
|
|
24120
|
+
try {
|
|
24121
|
+
listener(dir, clonePlan(plan));
|
|
24122
|
+
} catch {}
|
|
24123
|
+
}
|
|
24124
|
+
}
|
|
24125
|
+
function subscribeExecutionPlans(listener) {
|
|
24126
|
+
listeners.add(listener);
|
|
24127
|
+
return () => listeners.delete(listener);
|
|
24128
|
+
}
|
|
24129
|
+
async function getExecutionPlan(dir) {
|
|
24130
|
+
const plan = (await readSlate(dir))?.executionPlan;
|
|
24131
|
+
return plan === undefined ? undefined : clonePlan(plan);
|
|
24132
|
+
}
|
|
24133
|
+
async function setExecutionPlan(dir, input) {
|
|
24134
|
+
const items = validateItems(input.items);
|
|
24135
|
+
let result;
|
|
24136
|
+
await writeSlate(dir, (prev) => {
|
|
24137
|
+
if (prev === undefined)
|
|
24138
|
+
throw new ExecutionPlanValidationError("no open Slate");
|
|
24139
|
+
const actual = prev.executionPlan?.revision ?? 0;
|
|
24140
|
+
if (input.expectedRevision !== actual)
|
|
24141
|
+
throw new ExecutionPlanConflictError(input.expectedRevision, actual);
|
|
24142
|
+
result = { revision: actual + 1, items };
|
|
24143
|
+
return { ...prev, executionPlan: result };
|
|
24144
|
+
});
|
|
24145
|
+
const plan = clonePlan(result);
|
|
24146
|
+
notify(dir, plan);
|
|
24147
|
+
return plan;
|
|
24148
|
+
}
|
|
24149
|
+
async function updateExecutionPlan(dir, input) {
|
|
24150
|
+
let result;
|
|
24151
|
+
await writeSlate(dir, (prev) => {
|
|
24152
|
+
if (prev === undefined)
|
|
24153
|
+
throw new ExecutionPlanValidationError("no open Slate");
|
|
24154
|
+
const current = prev.executionPlan;
|
|
24155
|
+
const actual = current?.revision ?? 0;
|
|
24156
|
+
if (input.expectedRevision !== actual)
|
|
24157
|
+
throw new ExecutionPlanConflictError(input.expectedRevision, actual);
|
|
24158
|
+
if (current === undefined)
|
|
24159
|
+
throw new ExecutionPlanValidationError("no execution plan exists");
|
|
24160
|
+
if (!EXECUTION_PLAN_STATUSES.includes(input.status)) {
|
|
24161
|
+
throw new ExecutionPlanValidationError(`unknown status ${JSON.stringify(input.status)}`);
|
|
24162
|
+
}
|
|
24163
|
+
if (!current.items.some((item) => item.id === input.itemId)) {
|
|
24164
|
+
throw new ExecutionPlanValidationError(`unknown item id: ${input.itemId}`);
|
|
24165
|
+
}
|
|
24166
|
+
const items = validateItems(current.items.map((item) => item.id === input.itemId ? { ...item, status: input.status } : item));
|
|
24167
|
+
result = { revision: actual + 1, items };
|
|
24168
|
+
return { ...prev, executionPlan: result };
|
|
24169
|
+
});
|
|
24170
|
+
const plan = clonePlan(result);
|
|
24171
|
+
notify(dir, plan);
|
|
24172
|
+
return plan;
|
|
24173
|
+
}
|
|
24174
|
+
function hasActionableExecutionPlanItems(plan) {
|
|
24175
|
+
return plan?.items.some((item) => item.status === "pending" || item.status === "in_progress") ?? false;
|
|
24176
|
+
}
|
|
24177
|
+
function renderExecutionPlanSnapshot(plan, maxItems = 7) {
|
|
24178
|
+
if (plan === undefined || plan.items.length === 0)
|
|
24179
|
+
return;
|
|
24180
|
+
const rows = plan.items.slice(0, Math.max(0, maxItems)).map((item) => {
|
|
24181
|
+
const title = item.title.length > 120 ? `${item.title.slice(0, 119)}\u2026` : item.title;
|
|
24182
|
+
return `- ${item.id} [${item.status}]: ${title}`;
|
|
24183
|
+
});
|
|
24184
|
+
if (plan.items.length > rows.length)
|
|
24185
|
+
rows.push(`- \u2026 ${plan.items.length - rows.length} more item(s)`);
|
|
24186
|
+
return `Current execution plan (revision ${plan.revision}):
|
|
24187
|
+
${rows.join(`
|
|
24188
|
+
`)}`;
|
|
24189
|
+
}
|
|
24190
|
+
var EXECUTION_PLAN_STATUSES, listeners, ExecutionPlanConflictError, ExecutionPlanValidationError;
|
|
24191
|
+
var init_execution_plan = __esm(() => {
|
|
24192
|
+
init_slate();
|
|
24193
|
+
EXECUTION_PLAN_STATUSES = ["pending", "in_progress", "completed", "blocked", "skipped"];
|
|
24194
|
+
listeners = new Set;
|
|
24195
|
+
ExecutionPlanConflictError = class ExecutionPlanConflictError extends Error {
|
|
24196
|
+
constructor(expected, actual) {
|
|
24197
|
+
super(`execution plan revision conflict: expected ${expected}, current ${actual}`);
|
|
24198
|
+
this.name = "ExecutionPlanConflictError";
|
|
24199
|
+
}
|
|
24200
|
+
};
|
|
24201
|
+
ExecutionPlanValidationError = class ExecutionPlanValidationError extends Error {
|
|
24202
|
+
constructor(message) {
|
|
24203
|
+
super(`invalid execution plan: ${message}`);
|
|
24204
|
+
this.name = "ExecutionPlanValidationError";
|
|
24205
|
+
}
|
|
24206
|
+
};
|
|
24207
|
+
});
|
|
24208
|
+
|
|
24092
24209
|
// src/flow/store.ts
|
|
24093
24210
|
import { createHash as createHash16 } from "crypto";
|
|
24094
24211
|
import { appendFile as appendFile3, mkdir as mkdir32, readFile as readFile49, readdir as readdir12 } from "fs/promises";
|
|
@@ -28849,7 +28966,12 @@ async function openSlate(opts) {
|
|
|
28849
28966
|
cwd: opts.cwd,
|
|
28850
28967
|
...opts.runtime !== undefined ? { runtime: opts.runtime } : {}
|
|
28851
28968
|
});
|
|
28852
|
-
return openSlateAtomic(opts.dir, opts.mintAttemptId, () => ({
|
|
28969
|
+
return openSlateAtomic(opts.dir, opts.mintAttemptId, (existing) => ({
|
|
28970
|
+
anchors,
|
|
28971
|
+
course: {},
|
|
28972
|
+
seeds: [],
|
|
28973
|
+
...existing?.executionPlan === undefined ? {} : { executionPlan: existing.executionPlan }
|
|
28974
|
+
}));
|
|
28853
28975
|
}
|
|
28854
28976
|
async function closeSlate(dir, mintAttemptId) {
|
|
28855
28977
|
await archiveSlate(dir, mintAttemptId());
|
|
@@ -28891,7 +29013,12 @@ async function ensureSlateOpened(ref, mintAttemptId, runtime) {
|
|
|
28891
29013
|
if (isSlateSessionDetached(ref)) {
|
|
28892
29014
|
return;
|
|
28893
29015
|
}
|
|
28894
|
-
await openSlateAtomic(ref.dir, mintAttemptId, () => ({
|
|
29016
|
+
await openSlateAtomic(ref.dir, mintAttemptId, (existing) => ({
|
|
29017
|
+
anchors,
|
|
29018
|
+
course: {},
|
|
29019
|
+
seeds: [],
|
|
29020
|
+
...existing?.executionPlan === undefined ? {} : { executionPlan: existing.executionPlan }
|
|
29021
|
+
}));
|
|
28895
29022
|
ref.opened = true;
|
|
28896
29023
|
}
|
|
28897
29024
|
async function closeSlateSession(ref, mintAttemptId) {
|
|
@@ -29360,7 +29487,7 @@ function buildAgentSystemInstruction(orient, ctx = {}) {
|
|
|
29360
29487
|
` + locateRule + "- read_file returns a bounded window starting at `start_line` (default 1). A large file " + "is read by paging: its truncation notice names the start_line to continue from, and a " + `line number from ${hasGraph ? "search_code or graph_symbol" : "search_code"} can be read directly \u2014 do not re-read the ` + `same head hoping for more.
|
|
29361
29488
|
` + "- Prefer ONE correct shell_exec over many exploratory tool calls when the user asks " + `to run a known keryx workflow.
|
|
29362
29489
|
` + "- Tool-call budget: shell_exec, file-mutating shell, workspace_create/workspace_propose, and spawn_subagent " + "all share ONE small per-turn pool (distinct non-read actions), separate from the much larger read-tool pool. " + (hasGraph ? "search_code/graph_*/memory_search/read_wiki/wiki_*/test_related/health_status/flow_status/repomap/read_file/" : "search_code/read_file/") + "list_dir do NOT touch it. Conserve the small pool: batch multiple shell steps into ONE call with `&&` instead " + "of issuing them one at a time, get a command's arguments right the first time instead of trying variants, and " + "for any check covered by a read tool above, use that tool instead of shelling out to the equivalent `keryx \u2026` " + `CLI command.
|
|
29363
|
-
` + "- This session has its own Slate (working-set scratch, not project knowledge): " + "**slate_read** shows the Course (if a Flow is bound) and Seeds recorded so far \u2014 nothing " + "here is auto-injected, so call it if you want to see it. **slate_write_seed** with " + "`{ text, kind? }` records a draft hypothesis/decision/follow-up worth a later human " + "review. WRITE A SEED when you: found a root cause or a bug worth remembering; changed or " + "added code (summarize WHAT changed and WHY); took a design/architecture decision; " + "identified a risk; or discovered a constraint/lesson. Use the `kind` that fits: " + "`decision` (a choice made), `wiki-update` (something a wiki page should say), " + "`memory-entry` (a lesson/constraint), `follow-up` (a TODO for a later session), " + "`risk`, or `contract-change`. Keep each Seed to 2-3 sentences, concrete and specific. " + "Do NOT write Seeds for routine progress notes, one-shot operational requests (e.g. " + '"run git pull", "count files"), or trivia \u2014 those need no workspace and no proposal. ' + "Seeds are the ONLY input wrap-up proposes from: a session whose Slate has zero Seeds " + `produces zero proposals. A Seed is never accepted knowledge by itself.
|
|
29490
|
+
` + "- For multi-step work, use **plan_set** to publish a structured plan, **plan_update** after each real status " + "change, and **plan_get** before resolving a revision conflict. Keep stable item ids, at most one " + "`in_progress` item, and do not mark work complete before verification. These tools update session metadata " + "only; `/plan` remains the operator's separate read-only permission mode.\n" + "- This session has its own Slate (working-set scratch, not project knowledge): " + "**slate_read** shows the Course (if a Flow is bound) and Seeds recorded so far \u2014 nothing " + "here is auto-injected, so call it if you want to see it. **slate_write_seed** with " + "`{ text, kind? }` records a draft hypothesis/decision/follow-up worth a later human " + "review. WRITE A SEED when you: found a root cause or a bug worth remembering; changed or " + "added code (summarize WHAT changed and WHY); took a design/architecture decision; " + "identified a risk; or discovered a constraint/lesson. Use the `kind` that fits: " + "`decision` (a choice made), `wiki-update` (something a wiki page should say), " + "`memory-entry` (a lesson/constraint), `follow-up` (a TODO for a later session), " + "`risk`, or `contract-change`. Keep each Seed to 2-3 sentences, concrete and specific. " + "Do NOT write Seeds for routine progress notes, one-shot operational requests (e.g. " + '"run git pull", "count files"), or trivia \u2014 those need no workspace and no proposal. ' + "Seeds are the ONLY input wrap-up proposes from: a session whose Slate has zero Seeds " + `produces zero proposals. A Seed is never accepted knowledge by itself.
|
|
29364
29491
|
` + "- Shared Agent Context (SAC) workspaces hold accepted, evidence-backed project context " + "beyond this codebase. **workspace_list** with `{ includeArchived? }` shows every workspace " + "visible to you \u2014 call it first when the user references a shared team workspace or accepted " + "project context, or before creating a new workspace, to judge whether an existing one " + "already fits the current topic. **workspace_show** with `{ workspaceId }` shows one " + "workspace's manifest. **workspace_overview** with `{ workspaceId }`, then **workspace_read** " + "with `{ workspaceId, itemId }` for one specific item, reads its accepted Facts/Work/Know-how. " + "**workspace_create** with `{ title, component? }` creates a new workspace AND binds it to " + "this session's slate (wrap-up then proposes into it) \u2014 only when workspace_list found no " + "fitting one and the session has real, durable results worth persisting; a workspace is " + "meant to persist across sessions, so prefer an existing one over creating another for the " + "same topic, and do NOT create one for one-shot operational requests. **workspace_propose** with " + "`{ workspaceId, kind, sessionId?, note? }` (sessionId defaults to this session) proposes a decision/wiki-update/memory-entry/" + "follow-up/contract-change/risk from this session for later human review \u2014 it never accepts " + "anything by itself; accepting always requires a human running `keryx workspace review` at a " + `real terminal, never this tool.
|
|
29365
29492
|
` + "- When you need a decision, interview step, or clarification: use **ask_user** with " + "2\u20136 options `{ id, label, description, recommended? }` (mark one recommended). " + `Do not dump long prose questions without options.
|
|
29366
29493
|
` + "- For a focused independent subtask (investigate X, review Y, research Z): use " + "**spawn_subagent** with `{ task, mode?: 'read_only'|'general', label? }`. " + "Default mode is read_only (no shell). Prefer spawn for work that can finish " + `without your intermediate turns; do not spawn for trivial one-line answers.
|
|
@@ -29657,6 +29784,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
29657
29784
|
return "stop";
|
|
29658
29785
|
};
|
|
29659
29786
|
let toollessReprompts = 0;
|
|
29787
|
+
let planFollowThroughUsed = false;
|
|
29660
29788
|
let lastToollessText;
|
|
29661
29789
|
for (;; ) {
|
|
29662
29790
|
if (roundState.round >= roundState.maxRounds) {
|
|
@@ -29666,7 +29794,20 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
29666
29794
|
return { finishReason: "budget" };
|
|
29667
29795
|
}
|
|
29668
29796
|
roundState.round += 1;
|
|
29669
|
-
|
|
29797
|
+
let currentPlan;
|
|
29798
|
+
if (options.slateSession?.opened === true) {
|
|
29799
|
+
try {
|
|
29800
|
+
currentPlan = await getExecutionPlan(options.slateSession.dir);
|
|
29801
|
+
} catch (cause) {
|
|
29802
|
+
io.onSystem?.(`execution plan read failed (ignored): ${cause instanceof Error ? cause.message : String(cause)}
|
|
29803
|
+
`);
|
|
29804
|
+
}
|
|
29805
|
+
}
|
|
29806
|
+
const planSnapshot = renderExecutionPlanSnapshot(currentPlan);
|
|
29807
|
+
const roundSystemInstruction = planSnapshot === undefined ? deps.systemInstruction : `${deps.systemInstruction}
|
|
29808
|
+
|
|
29809
|
+
${planSnapshot}`;
|
|
29810
|
+
const preRequestEstimate = estimateRequestTokens(history, roundSystemInstruction, toolDefs);
|
|
29670
29811
|
if (needsCompaction(preRequestEstimate, deps.contextWindow)) {
|
|
29671
29812
|
const compacted = compactMessages(history, { keepLastUserTurns: 3 });
|
|
29672
29813
|
if (!compacted.noop) {
|
|
@@ -29681,7 +29822,7 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
29681
29822
|
const baseRequest = {
|
|
29682
29823
|
providerId: deps.providerId,
|
|
29683
29824
|
modelId: deps.modelId,
|
|
29684
|
-
systemInstruction:
|
|
29825
|
+
systemInstruction: roundSystemInstruction,
|
|
29685
29826
|
messages: [...history],
|
|
29686
29827
|
tools: toolDefs,
|
|
29687
29828
|
budget: { maxOutputTokens, runReservation: maxOutputTokens },
|
|
@@ -29911,6 +30052,32 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
29911
30052
|
}
|
|
29912
30053
|
continue;
|
|
29913
30054
|
}
|
|
30055
|
+
if (!planFollowThroughUsed && hasActionableExecutionPlanItems(currentPlan)) {
|
|
30056
|
+
planFollowThroughUsed = true;
|
|
30057
|
+
history.push({
|
|
30058
|
+
role: "user",
|
|
30059
|
+
content: "[system] The current execution plan still has actionable items remaining. Continue the work now. " + "Do not give another final reply until the plan is complete or genuinely blocked.",
|
|
30060
|
+
provenance: "project",
|
|
30061
|
+
ts: now()
|
|
30062
|
+
});
|
|
30063
|
+
io.onHistoryChange?.("tool");
|
|
30064
|
+
continue;
|
|
30065
|
+
}
|
|
30066
|
+
if (planFollowThroughUsed && hasActionableExecutionPlanItems(currentPlan)) {
|
|
30067
|
+
const actionable = currentPlan?.items.filter((item) => item.status === "pending" || item.status === "in_progress") ?? [];
|
|
30068
|
+
const shown = actionable.slice(0, 7).map((item) => {
|
|
30069
|
+
const title = item.title.length > 120 ? `${item.title.slice(0, 119)}\u2026` : item.title;
|
|
30070
|
+
return `- ${item.id} [${item.status}]: ${title}`;
|
|
30071
|
+
});
|
|
30072
|
+
if (actionable.length > shown.length) {
|
|
30073
|
+
shown.push(`- \u2026 ${actionable.length - shown.length} more actionable item(s)`);
|
|
30074
|
+
}
|
|
30075
|
+
system(`
|
|
30076
|
+
[plan] Actionable items remain after the single follow-through:
|
|
30077
|
+
${shown.join(`
|
|
30078
|
+
`)}
|
|
30079
|
+
`);
|
|
30080
|
+
}
|
|
29914
30081
|
return {};
|
|
29915
30082
|
}
|
|
29916
30083
|
if (isAborted()) {
|
|
@@ -30481,6 +30648,7 @@ var init_agent = __esm(() => {
|
|
|
30481
30648
|
init_peer_notification();
|
|
30482
30649
|
init_scheduler();
|
|
30483
30650
|
init_slate();
|
|
30651
|
+
init_execution_plan();
|
|
30484
30652
|
init_slate_course();
|
|
30485
30653
|
init_machine_wrap_up();
|
|
30486
30654
|
init_slate_lifecycle();
|
|
@@ -44782,7 +44950,7 @@ import { stdout } from "process";
|
|
|
44782
44950
|
// src/lib/md-blocks.ts
|
|
44783
44951
|
var FENCE_LINE = /^[ \t]{0,3}(```|~~~)(.*)\r?$/;
|
|
44784
44952
|
var HUNK_HEADER = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/;
|
|
44785
|
-
var MARKDOWN_LANGS = new Set(["md", "markdown", "prompt"
|
|
44953
|
+
var MARKDOWN_LANGS = new Set(["md", "markdown", "prompt"]);
|
|
44786
44954
|
var DIFF_LANGS = new Set(["diff", "patch"]);
|
|
44787
44955
|
function stripTrailingCr(line) {
|
|
44788
44956
|
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
@@ -44791,6 +44959,101 @@ function splitLines(text) {
|
|
|
44791
44959
|
return text.split(`
|
|
44792
44960
|
`).map(stripTrailingCr);
|
|
44793
44961
|
}
|
|
44962
|
+
function markdownTableCells(line) {
|
|
44963
|
+
const source = line.trim();
|
|
44964
|
+
if (!source.includes("|")) {
|
|
44965
|
+
return;
|
|
44966
|
+
}
|
|
44967
|
+
const cells = [];
|
|
44968
|
+
let cell = "";
|
|
44969
|
+
let inCode = false;
|
|
44970
|
+
for (let index = 0;index < source.length; index += 1) {
|
|
44971
|
+
const char = source[index] ?? "";
|
|
44972
|
+
if (char === "\\" && source[index + 1] === "|") {
|
|
44973
|
+
cell += "|";
|
|
44974
|
+
index += 1;
|
|
44975
|
+
continue;
|
|
44976
|
+
}
|
|
44977
|
+
if (char === "`") {
|
|
44978
|
+
inCode = !inCode;
|
|
44979
|
+
cell += char;
|
|
44980
|
+
continue;
|
|
44981
|
+
}
|
|
44982
|
+
if (char === "|" && !inCode) {
|
|
44983
|
+
cells.push(cell.trim());
|
|
44984
|
+
cell = "";
|
|
44985
|
+
continue;
|
|
44986
|
+
}
|
|
44987
|
+
cell += char;
|
|
44988
|
+
}
|
|
44989
|
+
cells.push(cell.trim());
|
|
44990
|
+
if (source.startsWith("|")) {
|
|
44991
|
+
cells.shift();
|
|
44992
|
+
}
|
|
44993
|
+
if (source.endsWith("|")) {
|
|
44994
|
+
cells.pop();
|
|
44995
|
+
}
|
|
44996
|
+
return cells.length >= 2 ? cells : undefined;
|
|
44997
|
+
}
|
|
44998
|
+
function tableAlignments(line, columns) {
|
|
44999
|
+
const cells = markdownTableCells(line);
|
|
45000
|
+
if (cells === undefined || cells.length !== columns) {
|
|
45001
|
+
return;
|
|
45002
|
+
}
|
|
45003
|
+
const alignments = [];
|
|
45004
|
+
for (const cell of cells) {
|
|
45005
|
+
const marker = cell.trim();
|
|
45006
|
+
if (!/^:?-{3,}:?$/.test(marker)) {
|
|
45007
|
+
return;
|
|
45008
|
+
}
|
|
45009
|
+
alignments.push(marker.startsWith(":") && marker.endsWith(":") ? "center" : marker.endsWith(":") ? "right" : "left");
|
|
45010
|
+
}
|
|
45011
|
+
return alignments;
|
|
45012
|
+
}
|
|
45013
|
+
function parseMarkdownTables(text) {
|
|
45014
|
+
if (text.length === 0) {
|
|
45015
|
+
return [];
|
|
45016
|
+
}
|
|
45017
|
+
const lines = splitLines(text);
|
|
45018
|
+
const parts = [];
|
|
45019
|
+
let proseStart = 0;
|
|
45020
|
+
const flushProse = (end) => {
|
|
45021
|
+
if (end <= proseStart) {
|
|
45022
|
+
return;
|
|
45023
|
+
}
|
|
45024
|
+
const prose = lines.slice(proseStart, end).join(`
|
|
45025
|
+
`);
|
|
45026
|
+
if (prose.length > 0) {
|
|
45027
|
+
parts.push({ kind: "prose", text: prose });
|
|
45028
|
+
}
|
|
45029
|
+
};
|
|
45030
|
+
for (let index = 0;index + 1 < lines.length; index += 1) {
|
|
45031
|
+
const headers = markdownTableCells(lines[index] ?? "");
|
|
45032
|
+
if (headers === undefined) {
|
|
45033
|
+
continue;
|
|
45034
|
+
}
|
|
45035
|
+
const alignments = tableAlignments(lines[index + 1] ?? "", headers.length);
|
|
45036
|
+
if (alignments === undefined) {
|
|
45037
|
+
continue;
|
|
45038
|
+
}
|
|
45039
|
+
flushProse(index);
|
|
45040
|
+
const rows = [];
|
|
45041
|
+
let cursor = index + 2;
|
|
45042
|
+
while (cursor < lines.length) {
|
|
45043
|
+
const row = markdownTableCells(lines[cursor] ?? "");
|
|
45044
|
+
if (row === undefined || row.length !== headers.length) {
|
|
45045
|
+
break;
|
|
45046
|
+
}
|
|
45047
|
+
rows.push(row);
|
|
45048
|
+
cursor += 1;
|
|
45049
|
+
}
|
|
45050
|
+
parts.push({ kind: "table", table: { headers, alignments, rows } });
|
|
45051
|
+
proseStart = cursor;
|
|
45052
|
+
index = cursor - 1;
|
|
45053
|
+
}
|
|
45054
|
+
flushProse(lines.length);
|
|
45055
|
+
return parts.length > 0 ? parts : [{ kind: "prose", text }];
|
|
45056
|
+
}
|
|
44794
45057
|
function fenceInfo(line) {
|
|
44795
45058
|
const match = FENCE_LINE.exec(line);
|
|
44796
45059
|
if (match === null) {
|
|
@@ -51141,7 +51404,7 @@ import path47 from "path";
|
|
|
51141
51404
|
// package.json
|
|
51142
51405
|
var package_default = {
|
|
51143
51406
|
name: "@mrciphersmith/keryx",
|
|
51144
|
-
version: "0.2.
|
|
51407
|
+
version: "0.2.143",
|
|
51145
51408
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
51146
51409
|
private: false,
|
|
51147
51410
|
publishConfig: {
|
|
@@ -86842,6 +87105,107 @@ shell_exec: no output for ${info.idleTimeoutMs}ms, so the command was killed (ra
|
|
|
86842
87105
|
};
|
|
86843
87106
|
}
|
|
86844
87107
|
|
|
87108
|
+
// src/harness/tool/builtin/execution-plan-tool.ts
|
|
87109
|
+
init_execution_plan();
|
|
87110
|
+
var statusSchema = { type: "string", enum: [...EXECUTION_PLAN_STATUSES] };
|
|
87111
|
+
function noSession(name) {
|
|
87112
|
+
return { output: `${name}: no active session in this run`, isError: true };
|
|
87113
|
+
}
|
|
87114
|
+
function failed(name, cause) {
|
|
87115
|
+
return { output: `${name} failed: ${cause instanceof Error ? cause.message : String(cause)}`, isError: true };
|
|
87116
|
+
}
|
|
87117
|
+
function executionPlanTools(getSessionDir) {
|
|
87118
|
+
const get = {
|
|
87119
|
+
definition: {
|
|
87120
|
+
name: "plan_get",
|
|
87121
|
+
description: "Read the current structured execution plan. Input: {}.",
|
|
87122
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
87123
|
+
risk: "read"
|
|
87124
|
+
},
|
|
87125
|
+
invoke: async () => {
|
|
87126
|
+
const dir = getSessionDir();
|
|
87127
|
+
if (dir === undefined)
|
|
87128
|
+
return noSession("plan_get");
|
|
87129
|
+
try {
|
|
87130
|
+
return { output: JSON.stringify(await getExecutionPlan(dir) ?? null, null, 2), isError: false };
|
|
87131
|
+
} catch (cause) {
|
|
87132
|
+
return failed("plan_get", cause);
|
|
87133
|
+
}
|
|
87134
|
+
}
|
|
87135
|
+
};
|
|
87136
|
+
const set = {
|
|
87137
|
+
definition: {
|
|
87138
|
+
name: "plan_set",
|
|
87139
|
+
description: "Replace the structured execution plan using optimistic revision control.",
|
|
87140
|
+
inputSchema: {
|
|
87141
|
+
type: "object",
|
|
87142
|
+
properties: {
|
|
87143
|
+
expectedRevision: { type: "integer", minimum: 0 },
|
|
87144
|
+
items: {
|
|
87145
|
+
type: "array",
|
|
87146
|
+
items: {
|
|
87147
|
+
type: "object",
|
|
87148
|
+
properties: { id: { type: "string", minLength: 1 }, title: { type: "string", minLength: 1 }, status: statusSchema },
|
|
87149
|
+
required: ["id", "title", "status"],
|
|
87150
|
+
additionalProperties: false
|
|
87151
|
+
}
|
|
87152
|
+
}
|
|
87153
|
+
},
|
|
87154
|
+
required: ["expectedRevision", "items"],
|
|
87155
|
+
additionalProperties: false
|
|
87156
|
+
},
|
|
87157
|
+
risk: "read"
|
|
87158
|
+
},
|
|
87159
|
+
invoke: async (input) => {
|
|
87160
|
+
const dir = getSessionDir();
|
|
87161
|
+
if (dir === undefined)
|
|
87162
|
+
return noSession("plan_set");
|
|
87163
|
+
try {
|
|
87164
|
+
const plan = await setExecutionPlan(dir, {
|
|
87165
|
+
expectedRevision: input.expectedRevision,
|
|
87166
|
+
items: input.items
|
|
87167
|
+
});
|
|
87168
|
+
return { output: JSON.stringify(plan, null, 2), isError: false };
|
|
87169
|
+
} catch (cause) {
|
|
87170
|
+
return failed("plan_set", cause);
|
|
87171
|
+
}
|
|
87172
|
+
}
|
|
87173
|
+
};
|
|
87174
|
+
const update = {
|
|
87175
|
+
definition: {
|
|
87176
|
+
name: "plan_update",
|
|
87177
|
+
description: "Update one execution-plan item's status using optimistic revision control.",
|
|
87178
|
+
inputSchema: {
|
|
87179
|
+
type: "object",
|
|
87180
|
+
properties: {
|
|
87181
|
+
expectedRevision: { type: "integer", minimum: 0 },
|
|
87182
|
+
itemId: { type: "string", minLength: 1 },
|
|
87183
|
+
status: statusSchema
|
|
87184
|
+
},
|
|
87185
|
+
required: ["expectedRevision", "itemId", "status"],
|
|
87186
|
+
additionalProperties: false
|
|
87187
|
+
},
|
|
87188
|
+
risk: "read"
|
|
87189
|
+
},
|
|
87190
|
+
invoke: async (input) => {
|
|
87191
|
+
const dir = getSessionDir();
|
|
87192
|
+
if (dir === undefined)
|
|
87193
|
+
return noSession("plan_update");
|
|
87194
|
+
try {
|
|
87195
|
+
const plan = await updateExecutionPlan(dir, {
|
|
87196
|
+
expectedRevision: input.expectedRevision,
|
|
87197
|
+
itemId: input.itemId,
|
|
87198
|
+
status: input.status
|
|
87199
|
+
});
|
|
87200
|
+
return { output: JSON.stringify(plan, null, 2), isError: false };
|
|
87201
|
+
} catch (cause) {
|
|
87202
|
+
return failed("plan_update", cause);
|
|
87203
|
+
}
|
|
87204
|
+
}
|
|
87205
|
+
};
|
|
87206
|
+
return [set, update, get];
|
|
87207
|
+
}
|
|
87208
|
+
|
|
86845
87209
|
// src/harness/tool/builtin/slate-tool.ts
|
|
86846
87210
|
init_slate();
|
|
86847
87211
|
init_slate_course();
|
|
@@ -87760,6 +88124,7 @@ function buildInteractiveAgentTools(input) {
|
|
|
87760
88124
|
createAskUserTool(invokeAskUserHost),
|
|
87761
88125
|
slateReadTool(input.cwd, getSessionDir),
|
|
87762
88126
|
slateWriteSeedTool(getSessionDir, idSeq, clock),
|
|
88127
|
+
...executionPlanTools(getSessionDir),
|
|
87763
88128
|
...input.bus?.client() === undefined ? [] : buildBusTools(input.bus.client),
|
|
87764
88129
|
...input.mcp === undefined ? [] : createMcpInteractiveTools({
|
|
87765
88130
|
catalog: input.mcp.catalog,
|
|
@@ -91315,9 +91680,81 @@ class LiveMarkdownBlock {
|
|
|
91315
91680
|
init_config_dir();
|
|
91316
91681
|
import { existsSync as existsSync33 } from "fs";
|
|
91317
91682
|
import path192 from "path";
|
|
91318
|
-
|
|
91683
|
+
function hexChannels(hex) {
|
|
91684
|
+
return [1, 3, 5].map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16));
|
|
91685
|
+
}
|
|
91686
|
+
function mixHex(base, accent, accentWeight) {
|
|
91687
|
+
const a = hexChannels(base);
|
|
91688
|
+
const b = hexChannels(accent);
|
|
91689
|
+
const channel = (index) => Math.round((a[index] ?? 0) * (1 - accentWeight) + (b[index] ?? 0) * accentWeight).toString(16).padStart(2, "0");
|
|
91690
|
+
return `#${channel(0)}${channel(1)}${channel(2)}`;
|
|
91691
|
+
}
|
|
91692
|
+
function relativeLuminance(hex) {
|
|
91693
|
+
const linear = hexChannels(hex).map((channel) => {
|
|
91694
|
+
const value = channel / 255;
|
|
91695
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
91696
|
+
});
|
|
91697
|
+
return 0.2126 * (linear[0] ?? 0) + 0.7152 * (linear[1] ?? 0) + 0.0722 * (linear[2] ?? 0);
|
|
91698
|
+
}
|
|
91699
|
+
function contrastRatio(a, b) {
|
|
91700
|
+
const [light, dark] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x);
|
|
91701
|
+
return ((light ?? 0) + 0.05) / ((dark ?? 0) + 0.05);
|
|
91702
|
+
}
|
|
91703
|
+
function transcriptAccent(theme, accent) {
|
|
91704
|
+
for (let weight = 0.5;weight >= 0; weight -= 0.05) {
|
|
91705
|
+
const candidate = mixHex(theme.text, accent, weight);
|
|
91706
|
+
if (contrastRatio(candidate, theme.bg) >= 3 && contrastRatio(candidate, theme.panel) >= 3) {
|
|
91707
|
+
return candidate;
|
|
91708
|
+
}
|
|
91709
|
+
}
|
|
91710
|
+
return theme.text;
|
|
91711
|
+
}
|
|
91712
|
+
function deriveTranscriptPalette(theme) {
|
|
91713
|
+
const heading = transcriptAccent(theme, theme.assistant);
|
|
91714
|
+
const muted = transcriptAccent(theme, theme.muted);
|
|
91715
|
+
const positive = transcriptAccent(theme, theme.ok);
|
|
91716
|
+
const negative = transcriptAccent(theme, theme.error);
|
|
91717
|
+
const focus = transcriptAccent(theme, theme.focus);
|
|
91718
|
+
return {
|
|
91719
|
+
prose: theme.text,
|
|
91720
|
+
heading,
|
|
91721
|
+
emphasis: theme.text,
|
|
91722
|
+
inlineCode: focus,
|
|
91723
|
+
inlineCodeBackground: theme.highlight,
|
|
91724
|
+
blockMeta: theme.muted,
|
|
91725
|
+
tableHeader: heading,
|
|
91726
|
+
tableBorder: theme.border,
|
|
91727
|
+
codePlain: theme.text,
|
|
91728
|
+
codeComment: muted,
|
|
91729
|
+
codeString: positive,
|
|
91730
|
+
codeNumber: focus,
|
|
91731
|
+
codeKeyword: heading,
|
|
91732
|
+
diffContext: theme.text,
|
|
91733
|
+
diffMeta: muted,
|
|
91734
|
+
diffHunk: heading,
|
|
91735
|
+
diffAdd: positive,
|
|
91736
|
+
diffDelete: negative,
|
|
91737
|
+
diffAddBackground: mixHex(theme.panel, theme.ok, 0.12),
|
|
91738
|
+
diffDeleteBackground: mixHex(theme.panel, theme.error, 0.12)
|
|
91739
|
+
};
|
|
91740
|
+
}
|
|
91741
|
+
var THEME_NAMES = [
|
|
91742
|
+
"groknight",
|
|
91743
|
+
"tokyonight",
|
|
91744
|
+
"keryx",
|
|
91745
|
+
"midnight",
|
|
91746
|
+
"nord",
|
|
91747
|
+
"ember",
|
|
91748
|
+
"violet",
|
|
91749
|
+
"grokday",
|
|
91750
|
+
"paper",
|
|
91751
|
+
"frost",
|
|
91752
|
+
"sand",
|
|
91753
|
+
"mint"
|
|
91754
|
+
];
|
|
91319
91755
|
var THEME_IDS = ["auto", ...THEME_NAMES];
|
|
91320
91756
|
var DEFAULT_THEME_ID = "groknight";
|
|
91757
|
+
var LIGHT_THEME_NAMES = new Set(["grokday", "paper", "frost", "sand", "mint"]);
|
|
91321
91758
|
var THEMES = {
|
|
91322
91759
|
groknight: {
|
|
91323
91760
|
name: "groknight",
|
|
@@ -91358,7 +91795,7 @@ var THEMES = {
|
|
|
91358
91795
|
highlight: "#292e42",
|
|
91359
91796
|
border: "#3b4261",
|
|
91360
91797
|
text: "#c0caf5",
|
|
91361
|
-
muted: "#
|
|
91798
|
+
muted: "#737da8",
|
|
91362
91799
|
user: "#a9b1d6",
|
|
91363
91800
|
assistant: "#bb9af7",
|
|
91364
91801
|
tool: "#7aa2f7",
|
|
@@ -91382,11 +91819,139 @@ var THEMES = {
|
|
|
91382
91819
|
focus: "#ffd166",
|
|
91383
91820
|
error: "#e05a5a",
|
|
91384
91821
|
ok: "#6bcf6b"
|
|
91822
|
+
},
|
|
91823
|
+
midnight: {
|
|
91824
|
+
name: "midnight",
|
|
91825
|
+
bg: "#080d18",
|
|
91826
|
+
panel: "#101827",
|
|
91827
|
+
highlight: "#17233a",
|
|
91828
|
+
border: "#40577a",
|
|
91829
|
+
text: "#dbe7ff",
|
|
91830
|
+
muted: "#8292ad",
|
|
91831
|
+
user: "#a9c7ff",
|
|
91832
|
+
assistant: "#8bd5ca",
|
|
91833
|
+
tool: "#7aa2f7",
|
|
91834
|
+
side: "#c6a0f6",
|
|
91835
|
+
focus: "#f5c26b",
|
|
91836
|
+
error: "#ff7a90",
|
|
91837
|
+
ok: "#83d17f"
|
|
91838
|
+
},
|
|
91839
|
+
nord: {
|
|
91840
|
+
name: "nord",
|
|
91841
|
+
bg: "#242933",
|
|
91842
|
+
panel: "#2e3440",
|
|
91843
|
+
highlight: "#3b4252",
|
|
91844
|
+
border: "#5e6b80",
|
|
91845
|
+
text: "#eceff4",
|
|
91846
|
+
muted: "#929db0",
|
|
91847
|
+
user: "#d8dee9",
|
|
91848
|
+
assistant: "#88c0d0",
|
|
91849
|
+
tool: "#81a1c1",
|
|
91850
|
+
side: "#b48ead",
|
|
91851
|
+
focus: "#ebcb8b",
|
|
91852
|
+
error: "#f0808b",
|
|
91853
|
+
ok: "#a3be8c"
|
|
91854
|
+
},
|
|
91855
|
+
ember: {
|
|
91856
|
+
name: "ember",
|
|
91857
|
+
bg: "#17100d",
|
|
91858
|
+
panel: "#211713",
|
|
91859
|
+
highlight: "#30201a",
|
|
91860
|
+
border: "#6a4d3d",
|
|
91861
|
+
text: "#f2e3d5",
|
|
91862
|
+
muted: "#a48672",
|
|
91863
|
+
user: "#ffd2ad",
|
|
91864
|
+
assistant: "#ff9f68",
|
|
91865
|
+
tool: "#e1b274",
|
|
91866
|
+
side: "#d89b72",
|
|
91867
|
+
focus: "#ffd166",
|
|
91868
|
+
error: "#ff7676",
|
|
91869
|
+
ok: "#a6d27a"
|
|
91870
|
+
},
|
|
91871
|
+
violet: {
|
|
91872
|
+
name: "violet",
|
|
91873
|
+
bg: "#100b1b",
|
|
91874
|
+
panel: "#1a1227",
|
|
91875
|
+
highlight: "#281b3c",
|
|
91876
|
+
border: "#61497d",
|
|
91877
|
+
text: "#eee8ff",
|
|
91878
|
+
muted: "#9f8db8",
|
|
91879
|
+
user: "#d6c4ff",
|
|
91880
|
+
assistant: "#c099ff",
|
|
91881
|
+
tool: "#76b7ff",
|
|
91882
|
+
side: "#ff8cc6",
|
|
91883
|
+
focus: "#ffd166",
|
|
91884
|
+
error: "#ff7699",
|
|
91885
|
+
ok: "#7bdba5"
|
|
91886
|
+
},
|
|
91887
|
+
paper: {
|
|
91888
|
+
name: "paper",
|
|
91889
|
+
bg: "#faf7f0",
|
|
91890
|
+
panel: "#f1ece1",
|
|
91891
|
+
highlight: "#e5ddcf",
|
|
91892
|
+
border: "#9d9280",
|
|
91893
|
+
text: "#29251f",
|
|
91894
|
+
muted: "#746d62",
|
|
91895
|
+
user: "#4b4033",
|
|
91896
|
+
assistant: "#6b4bbc",
|
|
91897
|
+
tool: "#1f62a8",
|
|
91898
|
+
side: "#8a4f72",
|
|
91899
|
+
focus: "#815500",
|
|
91900
|
+
error: "#b4233c",
|
|
91901
|
+
ok: "#2e7d32"
|
|
91902
|
+
},
|
|
91903
|
+
frost: {
|
|
91904
|
+
name: "frost",
|
|
91905
|
+
bg: "#f4f8fb",
|
|
91906
|
+
panel: "#eaf1f6",
|
|
91907
|
+
highlight: "#dbe8f0",
|
|
91908
|
+
border: "#93a8b7",
|
|
91909
|
+
text: "#1f2d3a",
|
|
91910
|
+
muted: "#627483",
|
|
91911
|
+
user: "#314c65",
|
|
91912
|
+
assistant: "#5b4bb7",
|
|
91913
|
+
tool: "#1769aa",
|
|
91914
|
+
side: "#794ca8",
|
|
91915
|
+
focus: "#805400",
|
|
91916
|
+
error: "#b32642",
|
|
91917
|
+
ok: "#2d7544"
|
|
91918
|
+
},
|
|
91919
|
+
sand: {
|
|
91920
|
+
name: "sand",
|
|
91921
|
+
bg: "#fff7e8",
|
|
91922
|
+
panel: "#f6ead5",
|
|
91923
|
+
highlight: "#ead9bb",
|
|
91924
|
+
border: "#ad936d",
|
|
91925
|
+
text: "#33291d",
|
|
91926
|
+
muted: "#776751",
|
|
91927
|
+
user: "#5b4630",
|
|
91928
|
+
assistant: "#744494",
|
|
91929
|
+
tool: "#176b75",
|
|
91930
|
+
side: "#91483b",
|
|
91931
|
+
focus: "#805400",
|
|
91932
|
+
error: "#b3263b",
|
|
91933
|
+
ok: "#347536"
|
|
91934
|
+
},
|
|
91935
|
+
mint: {
|
|
91936
|
+
name: "mint",
|
|
91937
|
+
bg: "#f2faf7",
|
|
91938
|
+
panel: "#e4f3ed",
|
|
91939
|
+
highlight: "#d3e9e0",
|
|
91940
|
+
border: "#8fafa3",
|
|
91941
|
+
text: "#19332a",
|
|
91942
|
+
muted: "#5d776d",
|
|
91943
|
+
user: "#285548",
|
|
91944
|
+
assistant: "#6745a6",
|
|
91945
|
+
tool: "#176b72",
|
|
91946
|
+
side: "#86506b",
|
|
91947
|
+
focus: "#805400",
|
|
91948
|
+
error: "#b32643",
|
|
91949
|
+
ok: "#217343"
|
|
91385
91950
|
}
|
|
91386
91951
|
};
|
|
91387
91952
|
var kind = DEFAULT_THEME_ID;
|
|
91388
91953
|
var resolved = THEMES.groknight;
|
|
91389
|
-
var
|
|
91954
|
+
var listeners2 = new Set;
|
|
91390
91955
|
function parseThemeId(raw) {
|
|
91391
91956
|
const key = raw.trim().toLowerCase();
|
|
91392
91957
|
if (key === "auto" || key === "system") {
|
|
@@ -91419,28 +91984,22 @@ function themeLabel(id) {
|
|
|
91419
91984
|
switch (id) {
|
|
91420
91985
|
case "auto":
|
|
91421
91986
|
return "auto (follow terminal)";
|
|
91422
|
-
|
|
91423
|
-
return
|
|
91424
|
-
case "grokday":
|
|
91425
|
-
return "grokday";
|
|
91426
|
-
case "tokyonight":
|
|
91427
|
-
return "tokyonight";
|
|
91428
|
-
case "keryx":
|
|
91429
|
-
return "keryx";
|
|
91987
|
+
default:
|
|
91988
|
+
return LIGHT_THEME_NAMES.has(id) ? `${id} \u2600` : id;
|
|
91430
91989
|
}
|
|
91431
91990
|
}
|
|
91432
91991
|
function applyThemeId(id, mode = null) {
|
|
91433
91992
|
kind = id;
|
|
91434
91993
|
resolved = resolveTheme(id, mode);
|
|
91435
|
-
for (const listener of
|
|
91994
|
+
for (const listener of listeners2) {
|
|
91436
91995
|
listener(resolved, kind);
|
|
91437
91996
|
}
|
|
91438
91997
|
return resolved;
|
|
91439
91998
|
}
|
|
91440
91999
|
function onThemeChange(listener) {
|
|
91441
|
-
|
|
92000
|
+
listeners2.add(listener);
|
|
91442
92001
|
return () => {
|
|
91443
|
-
|
|
92002
|
+
listeners2.delete(listener);
|
|
91444
92003
|
};
|
|
91445
92004
|
}
|
|
91446
92005
|
function tuiConfigPath(dir) {
|
|
@@ -92169,11 +92728,15 @@ function clipBody(text, maxLines = MAX_BODY_LINES) {
|
|
|
92169
92728
|
return [...lines.slice(0, maxLines), `\u2026 (${hidden} more line${hidden === 1 ? "" : "s"} not shown)`].join(`
|
|
92170
92729
|
`);
|
|
92171
92730
|
}
|
|
92172
|
-
function
|
|
92731
|
+
function themedChunk(otui, text, fg, bg) {
|
|
92732
|
+
const foreground = otui.fg(fg)(text);
|
|
92733
|
+
return bg === undefined ? foreground : otui.bg(bg)(foreground);
|
|
92734
|
+
}
|
|
92735
|
+
function markdownToChunks(otui, md, palette = deriveTranscriptPalette(getTheme())) {
|
|
92173
92736
|
const out = [];
|
|
92174
92737
|
const plain = (s) => {
|
|
92175
92738
|
if (s.length > 0) {
|
|
92176
|
-
out.push(
|
|
92739
|
+
out.push(themedChunk(otui, s, palette.prose));
|
|
92177
92740
|
}
|
|
92178
92741
|
};
|
|
92179
92742
|
const inline = (text) => {
|
|
@@ -92183,9 +92746,9 @@ function markdownToChunks(otui, md) {
|
|
|
92183
92746
|
while ((m = re.exec(text)) !== null) {
|
|
92184
92747
|
plain(text.slice(last, m.index));
|
|
92185
92748
|
if (m[1] !== undefined) {
|
|
92186
|
-
out.push(otui
|
|
92749
|
+
out.push(themedChunk(otui, m[1].slice(1, -1), palette.inlineCode, palette.inlineCodeBackground));
|
|
92187
92750
|
} else if (m[2] !== undefined) {
|
|
92188
|
-
out.push(otui.bold(m[2].slice(2, -2)));
|
|
92751
|
+
out.push(otui.bold(themedChunk(otui, m[2].slice(2, -2), palette.emphasis)));
|
|
92189
92752
|
}
|
|
92190
92753
|
last = m.index + m[0].length;
|
|
92191
92754
|
}
|
|
@@ -92204,12 +92767,12 @@ function markdownToChunks(otui, md) {
|
|
|
92204
92767
|
`);
|
|
92205
92768
|
}
|
|
92206
92769
|
if (inCode) {
|
|
92207
|
-
out.push(otui.
|
|
92770
|
+
out.push(themedChunk(otui, line, palette.codePlain));
|
|
92208
92771
|
continue;
|
|
92209
92772
|
}
|
|
92210
92773
|
const heading = /^#{1,6}\s+(.*)$/.exec(line);
|
|
92211
92774
|
if (heading !== null) {
|
|
92212
|
-
out.push(otui.
|
|
92775
|
+
out.push(otui.bold(themedChunk(otui, heading[1] ?? "", palette.heading)));
|
|
92213
92776
|
continue;
|
|
92214
92777
|
}
|
|
92215
92778
|
const bullet = /^(\s*)[-*]\s+(.*)$/.exec(line);
|
|
@@ -92222,109 +92785,150 @@ function markdownToChunks(otui, md) {
|
|
|
92222
92785
|
}
|
|
92223
92786
|
return out;
|
|
92224
92787
|
}
|
|
92225
|
-
function diffChunks(otui, text) {
|
|
92788
|
+
function diffChunks(otui, text, palette = deriveTranscriptPalette(getTheme())) {
|
|
92226
92789
|
const out = [];
|
|
92227
92790
|
const lines = splitLines(text);
|
|
92228
92791
|
for (const [index, line] of lines.entries()) {
|
|
92229
92792
|
if (index > 0) {
|
|
92230
|
-
out.push(
|
|
92231
|
-
|
|
92793
|
+
out.push(themedChunk(otui, `
|
|
92794
|
+
`, palette.diffContext));
|
|
92232
92795
|
}
|
|
92233
92796
|
switch (classifyDiffLine(line)) {
|
|
92234
92797
|
case "add":
|
|
92235
|
-
out.push(otui.
|
|
92798
|
+
out.push(themedChunk(otui, line, palette.diffAdd, palette.diffAddBackground));
|
|
92236
92799
|
break;
|
|
92237
92800
|
case "del":
|
|
92238
|
-
out.push(otui.
|
|
92801
|
+
out.push(themedChunk(otui, line, palette.diffDelete, palette.diffDeleteBackground));
|
|
92239
92802
|
break;
|
|
92240
92803
|
case "hunk":
|
|
92241
|
-
out.push(otui.
|
|
92804
|
+
out.push(themedChunk(otui, line, palette.diffHunk));
|
|
92242
92805
|
break;
|
|
92243
92806
|
case "meta":
|
|
92244
|
-
out.push(otui.
|
|
92807
|
+
out.push(themedChunk(otui, line, palette.diffMeta));
|
|
92245
92808
|
break;
|
|
92246
92809
|
default:
|
|
92247
|
-
out.push(
|
|
92810
|
+
out.push(themedChunk(otui, line, palette.diffContext));
|
|
92248
92811
|
}
|
|
92249
92812
|
}
|
|
92250
92813
|
return out;
|
|
92251
92814
|
}
|
|
92252
|
-
function flatDimChunks(otui, text) {
|
|
92815
|
+
function flatDimChunks(otui, text, palette = deriveTranscriptPalette(getTheme())) {
|
|
92253
92816
|
const out = [];
|
|
92254
92817
|
for (const [index, line] of splitLines(text).entries()) {
|
|
92255
92818
|
if (index > 0) {
|
|
92256
|
-
out.push(
|
|
92257
|
-
|
|
92819
|
+
out.push(otui.dim(themedChunk(otui, `
|
|
92820
|
+
`, palette.blockMeta)));
|
|
92258
92821
|
}
|
|
92259
|
-
out.push(otui.dim(line));
|
|
92822
|
+
out.push(otui.dim(themedChunk(otui, line, palette.blockMeta)));
|
|
92260
92823
|
}
|
|
92261
92824
|
return out;
|
|
92262
92825
|
}
|
|
92263
|
-
function codeChunks(otui, text, lang) {
|
|
92826
|
+
function codeChunks(otui, text, lang, palette) {
|
|
92264
92827
|
const out = [];
|
|
92265
92828
|
for (const [index, line] of splitLines(text).entries()) {
|
|
92266
92829
|
if (index > 0) {
|
|
92267
|
-
out.push(
|
|
92268
|
-
|
|
92830
|
+
out.push(themedChunk(otui, `
|
|
92831
|
+
`, palette.codePlain));
|
|
92269
92832
|
}
|
|
92270
92833
|
for (const token of tokenizeCodeLine(line, lang)) {
|
|
92271
92834
|
switch (token.kind) {
|
|
92272
92835
|
case "comment":
|
|
92273
|
-
out.push(otui
|
|
92836
|
+
out.push(themedChunk(otui, token.text, palette.codeComment));
|
|
92274
92837
|
break;
|
|
92275
92838
|
case "string":
|
|
92276
|
-
out.push(otui
|
|
92839
|
+
out.push(themedChunk(otui, token.text, palette.codeString));
|
|
92277
92840
|
break;
|
|
92278
92841
|
case "number":
|
|
92279
|
-
out.push(otui
|
|
92842
|
+
out.push(themedChunk(otui, token.text, palette.codeNumber));
|
|
92280
92843
|
break;
|
|
92281
92844
|
case "keyword":
|
|
92282
|
-
out.push(otui
|
|
92845
|
+
out.push(themedChunk(otui, token.text, palette.codeKeyword));
|
|
92283
92846
|
break;
|
|
92284
92847
|
default:
|
|
92285
|
-
out.push(
|
|
92848
|
+
out.push(themedChunk(otui, token.text, palette.codePlain));
|
|
92286
92849
|
}
|
|
92287
92850
|
}
|
|
92288
92851
|
}
|
|
92289
92852
|
return out;
|
|
92290
92853
|
}
|
|
92291
|
-
function payloadChunks(otui, text, lang = "") {
|
|
92854
|
+
function payloadChunks(otui, text, lang = "", palette = deriveTranscriptPalette(getTheme())) {
|
|
92292
92855
|
const kind = payloadKind(lang, lineCountOf(text));
|
|
92293
92856
|
if (kind === "diff" || looksLikeUnifiedDiff(text)) {
|
|
92294
|
-
return diffChunks(otui, text);
|
|
92857
|
+
return diffChunks(otui, text, palette);
|
|
92295
92858
|
}
|
|
92296
92859
|
if (kind === "markdown" || lang.length === 0) {
|
|
92297
|
-
return markdownToChunks(otui, text);
|
|
92860
|
+
return markdownToChunks(otui, text, palette);
|
|
92298
92861
|
}
|
|
92299
|
-
return codeChunks(otui, text, lang);
|
|
92862
|
+
return codeChunks(otui, text, lang, palette);
|
|
92300
92863
|
}
|
|
92301
92864
|
function createSegmentView(otui, renderer, parent, segment) {
|
|
92302
92865
|
viewSeq += 1;
|
|
92303
92866
|
const id = `seg${viewSeq}`;
|
|
92304
92867
|
if (segment.kind === "text") {
|
|
92305
|
-
|
|
92306
|
-
|
|
92307
|
-
|
|
92308
|
-
|
|
92309
|
-
|
|
92868
|
+
let mounted = [];
|
|
92869
|
+
const paint = (source) => {
|
|
92870
|
+
for (const child of mounted) {
|
|
92871
|
+
try {
|
|
92872
|
+
parent.remove(child);
|
|
92873
|
+
child.destroyRecursively();
|
|
92874
|
+
} catch {}
|
|
92875
|
+
}
|
|
92876
|
+
mounted = [];
|
|
92877
|
+
const palette = deriveTranscriptPalette(getTheme());
|
|
92878
|
+
for (const [index, part] of parseMarkdownTables(source).entries()) {
|
|
92879
|
+
if (part.kind === "prose") {
|
|
92880
|
+
const text = new otui.TextRenderable(renderer, {
|
|
92881
|
+
id: `${id}-prose-${index}`,
|
|
92882
|
+
content: new otui.StyledText(markdownToChunks(otui, part.text, palette))
|
|
92883
|
+
});
|
|
92884
|
+
parent.add(text);
|
|
92885
|
+
mounted.push(text);
|
|
92886
|
+
continue;
|
|
92887
|
+
}
|
|
92888
|
+
const header = part.table.headers.map((cell) => [otui.bold(themedChunk(otui, cell, palette.tableHeader))]);
|
|
92889
|
+
const rows = part.table.rows.map((row) => row.map((cell) => markdownToChunks(otui, cell, palette)));
|
|
92890
|
+
const table = new otui.TextTableRenderable(renderer, {
|
|
92891
|
+
id: `${id}-table-${index}`,
|
|
92892
|
+
content: [header, ...rows],
|
|
92893
|
+
width: "100%",
|
|
92894
|
+
wrapMode: "word",
|
|
92895
|
+
columnWidthMode: "full",
|
|
92896
|
+
columnFitter: "balanced",
|
|
92897
|
+
cellPaddingX: 1,
|
|
92898
|
+
cellPaddingY: 0,
|
|
92899
|
+
showBorders: true,
|
|
92900
|
+
outerBorder: true,
|
|
92901
|
+
borderStyle: "rounded",
|
|
92902
|
+
borderColor: palette.tableBorder,
|
|
92903
|
+
fg: palette.prose,
|
|
92904
|
+
backgroundColor: getTheme().panel
|
|
92905
|
+
});
|
|
92906
|
+
parent.add(table);
|
|
92907
|
+
mounted.push(table);
|
|
92908
|
+
}
|
|
92909
|
+
};
|
|
92910
|
+
paint(segment.text);
|
|
92310
92911
|
return {
|
|
92311
92912
|
kind: "text",
|
|
92312
92913
|
update: (next) => {
|
|
92313
92914
|
if (next.kind === "text") {
|
|
92314
|
-
|
|
92915
|
+
paint(next.text);
|
|
92315
92916
|
}
|
|
92316
92917
|
},
|
|
92317
92918
|
destroy: () => {
|
|
92318
|
-
|
|
92319
|
-
|
|
92320
|
-
|
|
92321
|
-
|
|
92919
|
+
for (const child of mounted) {
|
|
92920
|
+
try {
|
|
92921
|
+
parent.remove(child);
|
|
92922
|
+
child.destroyRecursively();
|
|
92923
|
+
} catch {}
|
|
92924
|
+
}
|
|
92925
|
+
mounted = [];
|
|
92322
92926
|
}
|
|
92323
92927
|
};
|
|
92324
92928
|
}
|
|
92325
92929
|
const tag = (lang, body) => {
|
|
92326
92930
|
const n = lineCountOf(body);
|
|
92327
|
-
return `${lang.length > 0 ? lang : "text"} \xB7 ${n} ${n === 1 ? "line" : "lines"} \xB7 y copy`;
|
|
92931
|
+
return `${lang.length > 0 ? lang : "text"} \xB7 ${n} ${n === 1 ? "line" : "lines"} \xB7 ctrl+o then y copy`;
|
|
92328
92932
|
};
|
|
92329
92933
|
const frameWidth = (lang, body) => Math.max(hugWidth(body, FRAME_CHROME), hugWidth(tag(lang, body), FRAME_CHROME));
|
|
92330
92934
|
const frame = new otui.BoxRenderable(renderer, {
|
|
@@ -92335,12 +92939,15 @@ function createSegmentView(otui, renderer, parent, segment) {
|
|
|
92335
92939
|
borderStyle: "rounded",
|
|
92336
92940
|
border: true,
|
|
92337
92941
|
borderColor: frameColor(),
|
|
92942
|
+
backgroundColor: getTheme().panel,
|
|
92338
92943
|
paddingLeft: 1,
|
|
92339
92944
|
paddingRight: 1
|
|
92340
92945
|
});
|
|
92341
92946
|
const header = new otui.TextRenderable(renderer, {
|
|
92342
92947
|
id: `${id}-tag`,
|
|
92343
|
-
content: otui.
|
|
92948
|
+
content: new otui.StyledText([
|
|
92949
|
+
themedChunk(otui, tag(segment.lang, segment.body), deriveTranscriptPalette(getTheme()).blockMeta)
|
|
92950
|
+
])
|
|
92344
92951
|
});
|
|
92345
92952
|
const body = new otui.TextRenderable(renderer, {
|
|
92346
92953
|
id: `${id}-body`,
|
|
@@ -92355,7 +92962,9 @@ function createSegmentView(otui, renderer, parent, segment) {
|
|
|
92355
92962
|
if (next.kind !== "code") {
|
|
92356
92963
|
return;
|
|
92357
92964
|
}
|
|
92358
|
-
header.content = otui.
|
|
92965
|
+
header.content = new otui.StyledText([
|
|
92966
|
+
themedChunk(otui, tag(next.lang, next.body), deriveTranscriptPalette(getTheme()).blockMeta)
|
|
92967
|
+
]);
|
|
92359
92968
|
body.content = new otui.StyledText(payloadChunks(otui, next.body, next.lang));
|
|
92360
92969
|
frame.maxWidth = frameWidth(next.lang, next.body);
|
|
92361
92970
|
},
|
|
@@ -92902,6 +93511,7 @@ function innerWidthOf(state) {
|
|
|
92902
93511
|
}
|
|
92903
93512
|
function paintTabs(state) {
|
|
92904
93513
|
clearTranscriptChildren(state.tabStrip);
|
|
93514
|
+
const theme = getTheme();
|
|
92905
93515
|
for (const [index, tab] of state.tabs.entries()) {
|
|
92906
93516
|
const active = tab.id === state.active;
|
|
92907
93517
|
const label = active ? `[${tab.label}]` : ` ${tab.label} `;
|
|
@@ -92909,6 +93519,7 @@ function paintTabs(state) {
|
|
|
92909
93519
|
state.tabStrip.add(new state.otui.TextRenderable(state.chrome.renderer, {
|
|
92910
93520
|
id: `modal-tab-${tab.id}`,
|
|
92911
93521
|
content: active ? state.otui.t`${prefix}${state.otui.bold(label)}` : state.otui.t`${prefix}${state.otui.dim(label)}`,
|
|
93522
|
+
fg: active ? theme.focus : theme.muted,
|
|
92912
93523
|
onMouseDown: () => {
|
|
92913
93524
|
if (!state.open || state.input === undefined || tab.id === state.active) {
|
|
92914
93525
|
return;
|
|
@@ -93017,12 +93628,14 @@ function ensureHost(otui, chrome) {
|
|
|
93017
93628
|
const titleText = new otui.TextRenderable(r, {
|
|
93018
93629
|
id: "modal-title",
|
|
93019
93630
|
content: "",
|
|
93020
|
-
flexGrow: 1
|
|
93631
|
+
flexGrow: 1,
|
|
93632
|
+
fg: getTheme().text
|
|
93021
93633
|
});
|
|
93022
93634
|
const closeText = new otui.TextRenderable(r, {
|
|
93023
93635
|
id: "modal-close",
|
|
93024
93636
|
content: "",
|
|
93025
93637
|
flexShrink: 0,
|
|
93638
|
+
fg: getTheme().muted,
|
|
93026
93639
|
onMouseDown: () => {
|
|
93027
93640
|
const current = hosts.get(r);
|
|
93028
93641
|
if (current !== undefined && current.open) {
|
|
@@ -93059,7 +93672,8 @@ function ensureHost(otui, chrome) {
|
|
|
93059
93672
|
});
|
|
93060
93673
|
const footerText = new otui.TextRenderable(r, {
|
|
93061
93674
|
id: "modal-footer-text",
|
|
93062
|
-
content: ""
|
|
93675
|
+
content: "",
|
|
93676
|
+
fg: getTheme().text
|
|
93063
93677
|
});
|
|
93064
93678
|
footer.add(footerText);
|
|
93065
93679
|
panel.add(header);
|
|
@@ -93161,6 +93775,10 @@ function ensureHost(otui, chrome) {
|
|
|
93161
93775
|
state.backdrop.backgroundColor = theme.bg;
|
|
93162
93776
|
state.panel.backgroundColor = theme.panel;
|
|
93163
93777
|
state.panel.borderColor = theme.border;
|
|
93778
|
+
state.titleText.fg = theme.text;
|
|
93779
|
+
state.closeText.fg = theme.muted;
|
|
93780
|
+
state.footerText.fg = theme.text;
|
|
93781
|
+
paintTabs(state);
|
|
93164
93782
|
});
|
|
93165
93783
|
return state;
|
|
93166
93784
|
}
|
|
@@ -94962,12 +95580,50 @@ function themeColorRemap(from, to) {
|
|
|
94962
95580
|
remap.set(oldColor, newColor);
|
|
94963
95581
|
}
|
|
94964
95582
|
}
|
|
95583
|
+
const fromTranscript = deriveTranscriptPalette(from);
|
|
95584
|
+
const toTranscript = deriveTranscriptPalette(to);
|
|
95585
|
+
for (const slot of Object.keys(fromTranscript)) {
|
|
95586
|
+
const oldColor = fromTranscript[slot];
|
|
95587
|
+
const newColor = toTranscript[slot];
|
|
95588
|
+
if (oldColor !== newColor) {
|
|
95589
|
+
remap.set(oldColor, newColor);
|
|
95590
|
+
}
|
|
95591
|
+
}
|
|
94965
95592
|
return remap;
|
|
94966
95593
|
}
|
|
94967
|
-
function
|
|
95594
|
+
function recolorContent(value, remap, color) {
|
|
95595
|
+
if (Array.isArray(value)) {
|
|
95596
|
+
let changed = false;
|
|
95597
|
+
for (const item of value) {
|
|
95598
|
+
changed = recolorContent(item, remap, color) || changed;
|
|
95599
|
+
}
|
|
95600
|
+
return changed;
|
|
95601
|
+
}
|
|
95602
|
+
if (value === null || typeof value !== "object") {
|
|
95603
|
+
return false;
|
|
95604
|
+
}
|
|
95605
|
+
const target = value;
|
|
95606
|
+
let changed = false;
|
|
95607
|
+
if (typeof target.text === "string") {
|
|
95608
|
+
for (const prop of ["fg", "bg"]) {
|
|
95609
|
+
const hex = themeColorToHex(target[prop]);
|
|
95610
|
+
const next = hex === undefined ? undefined : remap.get(hex);
|
|
95611
|
+
if (next !== undefined) {
|
|
95612
|
+
target[prop] = color(next);
|
|
95613
|
+
changed = true;
|
|
95614
|
+
}
|
|
95615
|
+
}
|
|
95616
|
+
}
|
|
95617
|
+
if (target.chunks !== undefined) {
|
|
95618
|
+
changed = recolorContent(target.chunks, remap, color) || changed;
|
|
95619
|
+
}
|
|
95620
|
+
return changed;
|
|
95621
|
+
}
|
|
95622
|
+
function recolorThemeTree(node, fromTheme, toTheme, color = (hex) => hex) {
|
|
94968
95623
|
if (node === null || node === undefined) {
|
|
94969
95624
|
return;
|
|
94970
95625
|
}
|
|
95626
|
+
const remap = themeColorRemap(fromTheme, toTheme);
|
|
94971
95627
|
const target = node;
|
|
94972
95628
|
for (const prop of ["borderColor", "backgroundColor", "fg"]) {
|
|
94973
95629
|
const hex = themeColorToHex(target[prop]);
|
|
@@ -94978,9 +95634,13 @@ function recolorThemeTree(node, remap) {
|
|
|
94978
95634
|
}
|
|
94979
95635
|
}
|
|
94980
95636
|
}
|
|
95637
|
+
const content = target.content;
|
|
95638
|
+
if (content !== undefined && recolorContent(content, remap, color)) {
|
|
95639
|
+
target.content = content;
|
|
95640
|
+
}
|
|
94981
95641
|
if (typeof target.getChildren === "function") {
|
|
94982
95642
|
for (const child of target.getChildren()) {
|
|
94983
|
-
recolorThemeTree(child,
|
|
95643
|
+
recolorThemeTree(child, fromTheme, toTheme, color);
|
|
94984
95644
|
}
|
|
94985
95645
|
}
|
|
94986
95646
|
}
|
|
@@ -95600,7 +96260,6 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
95600
96260
|
}
|
|
95601
96261
|
});
|
|
95602
96262
|
const applyTheme = (theme = getTheme()) => {
|
|
95603
|
-
const remap = themeColorRemap(appliedTheme, theme);
|
|
95604
96263
|
try {
|
|
95605
96264
|
r.setBackgroundColor(theme.bg);
|
|
95606
96265
|
} catch {}
|
|
@@ -95625,14 +96284,15 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
95625
96284
|
menu.selectedTextColor = theme.focus;
|
|
95626
96285
|
menu.descriptionColor = theme.muted;
|
|
95627
96286
|
menu.selectedDescriptionColor = theme.muted;
|
|
95628
|
-
|
|
95629
|
-
recolorThemeTree(
|
|
95630
|
-
recolorThemeTree(
|
|
95631
|
-
recolorThemeTree(
|
|
95632
|
-
recolorThemeTree(
|
|
95633
|
-
recolorThemeTree(
|
|
95634
|
-
recolorThemeTree(
|
|
95635
|
-
recolorThemeTree(
|
|
96287
|
+
const chunkColor = (hex) => otui.RGBA.fromHex(hex);
|
|
96288
|
+
recolorThemeTree(transcript, appliedTheme, theme, chunkColor);
|
|
96289
|
+
recolorThemeTree(dock, appliedTheme, theme, chunkColor);
|
|
96290
|
+
recolorThemeTree(queueDock, appliedTheme, theme, chunkColor);
|
|
96291
|
+
recolorThemeTree(sidebarTop, appliedTheme, theme, chunkColor);
|
|
96292
|
+
recolorThemeTree(menu, appliedTheme, theme, chunkColor);
|
|
96293
|
+
recolorThemeTree(composer, appliedTheme, theme, chunkColor);
|
|
96294
|
+
recolorThemeTree(header, appliedTheme, theme, chunkColor);
|
|
96295
|
+
recolorThemeTree(footer, appliedTheme, theme, chunkColor);
|
|
95636
96296
|
appliedTheme = theme;
|
|
95637
96297
|
};
|
|
95638
96298
|
const unsubTheme = onThemeChange((theme) => applyTheme(theme));
|
|
@@ -102218,6 +102878,96 @@ function paintBackgroundJobSidebar(otui, renderer, parent, entries, options) {
|
|
|
102218
102878
|
}
|
|
102219
102879
|
}
|
|
102220
102880
|
|
|
102881
|
+
// src/tui/execution-plan-panel.ts
|
|
102882
|
+
init_execution_plan();
|
|
102883
|
+
var GLYPHS = {
|
|
102884
|
+
completed: "\u2713",
|
|
102885
|
+
in_progress: "\u25B6",
|
|
102886
|
+
pending: "\u25CB",
|
|
102887
|
+
blocked: "!",
|
|
102888
|
+
skipped: "\u2212"
|
|
102889
|
+
};
|
|
102890
|
+
function truncate7(text, width) {
|
|
102891
|
+
if (width <= 0)
|
|
102892
|
+
return "";
|
|
102893
|
+
if (displayWidth(text) <= width)
|
|
102894
|
+
return text;
|
|
102895
|
+
if (width === 1)
|
|
102896
|
+
return "\u2026";
|
|
102897
|
+
let prefix = "";
|
|
102898
|
+
for (const character of text) {
|
|
102899
|
+
if (displayWidth(prefix + character) > width - 1)
|
|
102900
|
+
break;
|
|
102901
|
+
prefix += character;
|
|
102902
|
+
}
|
|
102903
|
+
return `${prefix}\u2026`;
|
|
102904
|
+
}
|
|
102905
|
+
function projectExecutionPlanPanel(plan, options) {
|
|
102906
|
+
if (plan === undefined || plan.items.length === 0) {
|
|
102907
|
+
return { visible: false, rows: [], revision: undefined };
|
|
102908
|
+
}
|
|
102909
|
+
const size = Math.max(1, Math.min(options.maxRows ?? 7, 7, plan.items.length));
|
|
102910
|
+
const active = Math.max(0, plan.items.findIndex((item) => item.status === "in_progress"));
|
|
102911
|
+
const start = Math.max(0, Math.min(active - Math.floor(size / 2), plan.items.length - size));
|
|
102912
|
+
const rows = plan.items.slice(start, start + size).map((item) => {
|
|
102913
|
+
const glyph = GLYPHS[item.status];
|
|
102914
|
+
return { id: item.id, status: item.status, glyph, text: truncate7(`${glyph} ${item.title}`, options.width) };
|
|
102915
|
+
});
|
|
102916
|
+
return { visible: true, rows, revision: plan.revision };
|
|
102917
|
+
}
|
|
102918
|
+
function mountExecutionPlanPanel(otui, renderer, parent, options) {
|
|
102919
|
+
const box = parent;
|
|
102920
|
+
let paintedDir;
|
|
102921
|
+
let paintedRevision;
|
|
102922
|
+
let refreshGeneration = 0;
|
|
102923
|
+
let disposed = false;
|
|
102924
|
+
const paint = (plan, dir) => {
|
|
102925
|
+
const projected = projectExecutionPlanPanel(plan, {
|
|
102926
|
+
width: options.width,
|
|
102927
|
+
...options.maxRows === undefined ? {} : { maxRows: options.maxRows }
|
|
102928
|
+
});
|
|
102929
|
+
if (paintedDir === dir && paintedRevision === projected.revision)
|
|
102930
|
+
return;
|
|
102931
|
+
paintedDir = dir;
|
|
102932
|
+
paintedRevision = projected.revision;
|
|
102933
|
+
if (box.getChildren !== undefined && box.remove !== undefined) {
|
|
102934
|
+
for (const child of [...box.getChildren()])
|
|
102935
|
+
box.remove(child);
|
|
102936
|
+
}
|
|
102937
|
+
const Ctor = otui.TextRenderable;
|
|
102938
|
+
if (!projected.visible || box.add === undefined || Ctor === undefined)
|
|
102939
|
+
return;
|
|
102940
|
+
box.add(new Ctor(renderer, { id: "sb-plan-h", content: "Plan", marginTop: 1 }));
|
|
102941
|
+
for (const row of projected.rows)
|
|
102942
|
+
box.add(new Ctor(renderer, { id: `sb-plan-${row.id}`, content: row.text }));
|
|
102943
|
+
};
|
|
102944
|
+
const refresh = async () => {
|
|
102945
|
+
const dir = options.getSessionDir();
|
|
102946
|
+
const generation = ++refreshGeneration;
|
|
102947
|
+
const plan = dir === undefined ? undefined : await getExecutionPlan(dir).catch(() => {
|
|
102948
|
+
return;
|
|
102949
|
+
});
|
|
102950
|
+
if (disposed || generation !== refreshGeneration || dir !== options.getSessionDir())
|
|
102951
|
+
return;
|
|
102952
|
+
paint(plan, dir);
|
|
102953
|
+
};
|
|
102954
|
+
const unsubscribe = subscribeExecutionPlans((dir, plan) => {
|
|
102955
|
+
if (dir === options.getSessionDir()) {
|
|
102956
|
+
refreshGeneration += 1;
|
|
102957
|
+
paint(plan, dir);
|
|
102958
|
+
}
|
|
102959
|
+
});
|
|
102960
|
+
refresh();
|
|
102961
|
+
return {
|
|
102962
|
+
refresh,
|
|
102963
|
+
dispose: () => {
|
|
102964
|
+
disposed = true;
|
|
102965
|
+
refreshGeneration += 1;
|
|
102966
|
+
unsubscribe();
|
|
102967
|
+
}
|
|
102968
|
+
};
|
|
102969
|
+
}
|
|
102970
|
+
|
|
102221
102971
|
// src/tui/bus-command.ts
|
|
102222
102972
|
var SEND_USAGE = "usage: /bus send @<name> <text> (or /bus @<name> <text>)";
|
|
102223
102973
|
var ASK_USAGE = "usage: /bus ask @<name> <text>";
|
|
@@ -104201,6 +104951,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
104201
104951
|
let mountedChrome;
|
|
104202
104952
|
let liveDeps;
|
|
104203
104953
|
let liveJobs;
|
|
104954
|
+
let disposeExecutionPlanPanel;
|
|
104204
104955
|
let detachExternal;
|
|
104205
104956
|
let liveBus;
|
|
104206
104957
|
let destroyed = false;
|
|
@@ -104223,6 +104974,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
104223
104974
|
try {
|
|
104224
104975
|
const r = renderer = await createShellRenderer(otui, {
|
|
104225
104976
|
onDestroy: () => {
|
|
104977
|
+
disposeExecutionPlanPanel?.();
|
|
104226
104978
|
destroyed = true;
|
|
104227
104979
|
foregroundOperation.cancel("renderer destroyed");
|
|
104228
104980
|
foregroundOperation.dispose();
|
|
@@ -104262,6 +105014,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
104262
105014
|
saveShellConfig(sel.baseUrl === undefined ? { provider: sel.provider, model: sel.model } : { provider: sel.provider, model: sel.model, baseUrl: sel.baseUrl });
|
|
104263
105015
|
let currentSel = sel;
|
|
104264
105016
|
const suggestionGate = new NextStepSuggestionGate;
|
|
105017
|
+
let slateSession;
|
|
104265
105018
|
const liveSlateSession = () => whilePersisting(slateSession, () => sessionLease.canPersist());
|
|
104266
105019
|
let deps = await opts.makeAgentDeps(sel, liveSlateSession, busClientRef);
|
|
104267
105020
|
liveDeps = deps;
|
|
@@ -104405,6 +105158,18 @@ async function launchTuiAgentShell(opts) {
|
|
|
104405
105158
|
marginTop: 1
|
|
104406
105159
|
});
|
|
104407
105160
|
sidebar.add(sbSubagents);
|
|
105161
|
+
const sbPlan = new otui.BoxRenderable(r, {
|
|
105162
|
+
id: "sb-plan",
|
|
105163
|
+
flexDirection: "column",
|
|
105164
|
+
flexShrink: 0
|
|
105165
|
+
});
|
|
105166
|
+
sidebar.add(sbPlan);
|
|
105167
|
+
const executionPlanPanel = mountExecutionPlanPanel(otui, r, sbPlan, {
|
|
105168
|
+
getSessionDir: () => liveSlateSession()?.dir,
|
|
105169
|
+
width: SIDEBAR_TEXT_WIDTH,
|
|
105170
|
+
maxRows: 7
|
|
105171
|
+
});
|
|
105172
|
+
disposeExecutionPlanPanel = executionPlanPanel.dispose;
|
|
104408
105173
|
const sbJobs = new otui.BoxRenderable(r, {
|
|
104409
105174
|
id: "sb-jobs",
|
|
104410
105175
|
flexDirection: "column",
|
|
@@ -104989,9 +105754,9 @@ async function launchTuiAgentShell(opts) {
|
|
|
104989
105754
|
let archive = [];
|
|
104990
105755
|
let nextArchiveIndex = 0;
|
|
104991
105756
|
let sessionPersistTimer;
|
|
104992
|
-
let slateSession;
|
|
104993
105757
|
const bindSlateToLiveSession = () => {
|
|
104994
105758
|
slateSession = freshSlateSessionRef(liveSession.dir, sessionCwd);
|
|
105759
|
+
executionPlanPanel.refresh();
|
|
104995
105760
|
};
|
|
104996
105761
|
const onContextCompaction = (r) => {
|
|
104997
105762
|
if (!sessionLease.canPersist()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.143",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|