@warren-dean/code-kit 0.7.0 → 0.8.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +33 -0
- package/README.md +15 -3
- package/bin/code-kit.mjs +171 -3
- package/hooks/guard-bash.mjs +29 -2
- package/hooks/lib/adapters/context-graph.mjs +18 -19
- package/hooks/lib/journal.mjs +224 -0
- package/hooks/lib/loop-state.mjs +26 -0
- package/hooks/lib/next.mjs +13 -3
- package/hooks/lib/plan.mjs +8 -1
- package/hooks/lib/storymap.mjs +155 -0
- package/hooks/mod/codemap.mjs +328 -0
- package/hooks/mod/decisions.mjs +146 -0
- package/hooks/mod/loop.mjs +7 -3
- package/hooks/mod/mission.mjs +116 -0
- package/hooks/mod/panes.mjs +469 -34
- package/hooks/mod/register.mjs +196 -2
- package/hooks/mod/register.test.ts +456 -25
- package/hooks/mod/trail.mjs +152 -0
- package/hooks/mod/view.mjs +324 -116
- package/package.json +1 -1
- package/skills/init/SKILL.md +3 -3
package/hooks/mod/register.mjs
CHANGED
|
@@ -46,6 +46,9 @@ import {
|
|
|
46
46
|
toolLine,
|
|
47
47
|
} from './panes.mjs';
|
|
48
48
|
import { leadPrompt, nudgeText, restartPrompt, stallDue, stepKey, stepLabel } from './loop.mjs';
|
|
49
|
+
import { goalOf, nextText } from './mission.mjs';
|
|
50
|
+
import { contextOf, decisionCards } from './decisions.mjs';
|
|
51
|
+
import { glowOf, mapStoryOf, touchOf } from './codemap.mjs';
|
|
49
52
|
import { harnessSettings } from '../lib/harness.mjs';
|
|
50
53
|
import {
|
|
51
54
|
REVIEW_WAIT_MS,
|
|
@@ -82,6 +85,7 @@ const loop = {
|
|
|
82
85
|
asked: { key: null, times: 0 }, // the step the lead was last prompted with, and how often in a row
|
|
83
86
|
proposal: null, // under autonomy propose, or a step asked too often: { prompt, label, kind, target }
|
|
84
87
|
pending: [], // restarts waiting for the lead to be idle: { story, prompt }
|
|
88
|
+
nextStep: null, // what `code-kit next` last said, for the band's next row
|
|
85
89
|
done: null, // { count } once every story is done
|
|
86
90
|
doneDismissed: false,
|
|
87
91
|
};
|
|
@@ -91,6 +95,19 @@ let leadTurn = null; // the lead's running turn, if any
|
|
|
91
95
|
const activity = new Map(); // agent id → { at, inFlight, nudged, stopped, lane, story }
|
|
92
96
|
const restarts = new Map(); // story → times the loop restarted it
|
|
93
97
|
const flagged = new Map(); // story → { lane, story, count }: stalled past its restarts
|
|
98
|
+
/**
|
|
99
|
+
* LOOP-3: whether an agent's work is done, so it isn't stalled however quiet: it handed back, or its
|
|
100
|
+
* story is in review, waiting in the merge queue or merged.
|
|
101
|
+
*/
|
|
102
|
+
const finishedWork = (a) => {
|
|
103
|
+
if (a.handedBack) return true;
|
|
104
|
+
const story = (model.state?.stories ?? []).find((s) => s.id === a.story);
|
|
105
|
+
if (!story) return false;
|
|
106
|
+
if (['review', 'done'].includes(story.state)) return true;
|
|
107
|
+
return (model.queue ?? []).some(
|
|
108
|
+
(q) => q.branch === story.branch && ['waiting', 'merging'].includes(q.state),
|
|
109
|
+
);
|
|
110
|
+
};
|
|
94
111
|
/** An agent's activity record, made on first sight. */
|
|
95
112
|
const activityOf = (agentId, now) => {
|
|
96
113
|
let a = activity.get(agentId);
|
|
@@ -110,6 +127,11 @@ let frame = 0;
|
|
|
110
127
|
let animation = null; // the timer repainting working characters, while one works
|
|
111
128
|
let paneSurface = null;
|
|
112
129
|
let paneColumns = 80;
|
|
130
|
+
let codeMap = null; // MAP-1: `code-kit map <story> --json` for the Code map tab's story
|
|
131
|
+
let trailData = null; // TRAIL-1: `code-kit trail --json` while the Trail tab shows
|
|
132
|
+
let journalData = null; // JOURNAL-1: `code-kit journal --json`, for the Journal tab and the decisions
|
|
133
|
+
const touches = []; // MAP-2: { path, verb, lane, at }, the files agents read and edited lately
|
|
134
|
+
const TOUCHES_KEPT = 200;
|
|
113
135
|
// The reviewer (spec 07): each branch's review at its head, and the agent type registered for it.
|
|
114
136
|
const reviews = new Map(); // branch → { branch, head, story, state, startedAt, findings, why?, error? }
|
|
115
137
|
let reviewerAgent = null; // the registered agent's full name
|
|
@@ -162,6 +184,9 @@ export function register(on) {
|
|
|
162
184
|
const ran = await $.process.run(['node', cli, ...args, '--json'], { cwd });
|
|
163
185
|
return ran.exitCode === 0 ? parseJson(ran.stdout) : null;
|
|
164
186
|
};
|
|
187
|
+
// A session starts with the loop running: a pause recorded by an earlier one no longer holds.
|
|
188
|
+
if ((await json('loop').catch(() => null))?.paused)
|
|
189
|
+
await $.process.run(['node', cli, 'loop', 'resume'], { cwd }).catch(() => {});
|
|
165
190
|
const stamp = async (path) => {
|
|
166
191
|
const s = await $.fs.stat(`${cwd}/${path}`).catch(() => null);
|
|
167
192
|
return s ? `${path}@${s.mtimeMs}` : `${path}-`;
|
|
@@ -210,8 +235,22 @@ export function register(on) {
|
|
|
210
235
|
base: status?.base ?? null,
|
|
211
236
|
};
|
|
212
237
|
if (model.state.kind === 'ok') await act.registerReviewer();
|
|
238
|
+
await act.readMaps();
|
|
213
239
|
$.ui.invalidate('ui.render');
|
|
214
240
|
},
|
|
241
|
+
// MAP-1, TRAIL-1: what the Code map and Trail tabs draw, read while they show.
|
|
242
|
+
readMaps: async () => {
|
|
243
|
+
if (model.state?.kind !== 'ok') return;
|
|
244
|
+
if (paneUi.tab === 'code') {
|
|
245
|
+
const id = mapStoryOf(stateNow(), paneUi.selected, paneUi.mapStory);
|
|
246
|
+
codeMap = id ? ((await json('map', id)) ?? codeMap) : null;
|
|
247
|
+
}
|
|
248
|
+
if (paneUi.tab === 'map') trailData = (await json('trail')) ?? trailData;
|
|
249
|
+
// JOURNAL-1, DECIDE-2: the lineage, for the Journal tab and the decisions' cards.
|
|
250
|
+
const deciding = held.size > 0 || (model.requests?.open ?? []).length > 0;
|
|
251
|
+
if (model.check?.docs?.plan && (['journal', 'decisions'].includes(paneUi.tab) || deciding))
|
|
252
|
+
journalData = (await json('journal')) ?? journalData;
|
|
253
|
+
},
|
|
215
254
|
// REVW-2: the reviewer, read-only by the hooks' rule for agents outside the lanes. Registered
|
|
216
255
|
// again when its model setting changes.
|
|
217
256
|
registerReviewer: async () => {
|
|
@@ -374,6 +413,21 @@ export function register(on) {
|
|
|
374
413
|
if (!placed.isPlaced) return { text: `The Lanes pane is waiting: ${placed.reason}` };
|
|
375
414
|
return {};
|
|
376
415
|
},
|
|
416
|
+
// DECIDE-3: the Decisions tab, on the decision the person asked about.
|
|
417
|
+
decide: async (line) => {
|
|
418
|
+
const cards = cardsNow(await $.clock.now());
|
|
419
|
+
const at = Math.max(
|
|
420
|
+
0,
|
|
421
|
+
cards.findIndex((c) => c.line.kind === line?.kind),
|
|
422
|
+
);
|
|
423
|
+
paneUi = { ...paneUi, tab: 'decisions', decision: at, story: null };
|
|
424
|
+
if (!(await $.ui.panes()).some((p) => p.id === PANE_ID))
|
|
425
|
+
await $.ui.open({ id: PANE_ID, title: 'Lanes', focus: true, closeOnEscape: true });
|
|
426
|
+
$.ui.invalidate('ui.render');
|
|
427
|
+
await act.readMaps();
|
|
428
|
+
$.ui.invalidate('ui.render');
|
|
429
|
+
return {};
|
|
430
|
+
},
|
|
377
431
|
// ACT-1: the confirmation, prefilled from the request.
|
|
378
432
|
approve: async (line) => {
|
|
379
433
|
approving = {
|
|
@@ -594,10 +648,26 @@ export function register(on) {
|
|
|
594
648
|
if (box?.text?.trim()) return;
|
|
595
649
|
if (loop.pending.length) {
|
|
596
650
|
const { story, prompt } = loop.pending.shift();
|
|
651
|
+
// Until the person has chosen how hands-off the loop is, or under propose, a restart is offered
|
|
652
|
+
// like any other step, not taken.
|
|
653
|
+
const chosenYet = (model.check?.harnessSet ?? []).includes('autonomy');
|
|
654
|
+
if (!chosenYet || h.autonomy === 'propose') {
|
|
655
|
+
loop.proposal = {
|
|
656
|
+
prompt,
|
|
657
|
+
label: `Restart ${story}`,
|
|
658
|
+
kind: 'restart',
|
|
659
|
+
target: story,
|
|
660
|
+
first: !chosenYet,
|
|
661
|
+
};
|
|
662
|
+
$.ui.invalidate('ui.render');
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
597
665
|
return act.submit(prompt, 'restart', story);
|
|
598
666
|
}
|
|
599
667
|
const ran = await $.process.run(['node', cli, 'next', '--json'], { cwd });
|
|
600
668
|
const step = ran.exitCode === 0 ? parseJson(ran.stdout) : null;
|
|
669
|
+
// MISSION-1: the band shows what the loop takes next.
|
|
670
|
+
loop.nextStep = step;
|
|
601
671
|
if (!step) {
|
|
602
672
|
notice = `The loop is waiting: code-kit next failed (${(ran.stderr || ran.stdout).trim().split('\n')[0]})`;
|
|
603
673
|
$.ui.invalidate('ui.render');
|
|
@@ -701,11 +771,14 @@ export function register(on) {
|
|
|
701
771
|
pause: async () => {
|
|
702
772
|
loop.paused = true;
|
|
703
773
|
loop.proposal = null;
|
|
774
|
+
// Recorded, so other tools' background work in this project pauses with the loop.
|
|
775
|
+
await $.process.run(['node', cli, 'loop', 'pause'], { cwd }).catch(() => {});
|
|
704
776
|
await act.record('pause', 'the loop');
|
|
705
777
|
$.ui.invalidate('ui.render');
|
|
706
778
|
},
|
|
707
779
|
resume: async () => {
|
|
708
780
|
loop.paused = false;
|
|
781
|
+
await $.process.run(['node', cli, 'loop', 'resume'], { cwd }).catch(() => {});
|
|
709
782
|
loop.lastKey = null;
|
|
710
783
|
await act.record('resume', 'the loop');
|
|
711
784
|
$.ui.invalidate('ui.render');
|
|
@@ -744,6 +817,12 @@ export function register(on) {
|
|
|
744
817
|
a.at = now;
|
|
745
818
|
continue;
|
|
746
819
|
}
|
|
820
|
+
// LOOP-3: an agent whose work is done isn't stalled: it handed back, or its story is in review,
|
|
821
|
+
// in the merge queue or merged. Nothing to nudge or restart.
|
|
822
|
+
if (finishedWork(a)) {
|
|
823
|
+
a.at = now;
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
747
826
|
const due = stallDue(now - a.at, a, restarts.get(a.story) ?? 0, h.stall);
|
|
748
827
|
if (due === 'nudge') {
|
|
749
828
|
a.nudged = true;
|
|
@@ -990,10 +1069,26 @@ export function register(on) {
|
|
|
990
1069
|
busy.inFlight += 1;
|
|
991
1070
|
busy.nudged = false;
|
|
992
1071
|
busy.stopped = false;
|
|
1072
|
+
busy.handedBack = false;
|
|
993
1073
|
// VIEW-4: the agent's current or last tool call.
|
|
994
1074
|
busy.tool = toolLine(e);
|
|
995
1075
|
busy.at = calledAt;
|
|
996
1076
|
} else if (!e.agentId) leadLast = { tool: toolLine(e), at: calledAt };
|
|
1077
|
+
// MAP-2: the file the call reads or edits glows in the code map, in its lane's colour. An agent
|
|
1078
|
+
// first seen on this call has its lane found by its type.
|
|
1079
|
+
const touch = touchOf(e, where?.cwd);
|
|
1080
|
+
if (touch && busy && !busy.lane) {
|
|
1081
|
+
const agent = ((await $.agent.list().catch(() => [])) ?? []).find((a) => a.id === e.agentId);
|
|
1082
|
+
busy.lane =
|
|
1083
|
+
Object.entries(model.check?.lanes ?? {}).find(([, l]) => l.agent === agent?.type)?.[0] ??
|
|
1084
|
+
busy.lane;
|
|
1085
|
+
}
|
|
1086
|
+
const touchLane = busy ? busy.lane : e.agentId ? null : 'lead';
|
|
1087
|
+
if (touch && touchLane) {
|
|
1088
|
+
touches.push({ ...touch, lane: touchLane, at: calledAt });
|
|
1089
|
+
if (touches.length > TOUCHES_KEPT) touches.shift();
|
|
1090
|
+
if (paneUi.tab === 'code') $.ui.invalidate('ui.render');
|
|
1091
|
+
}
|
|
997
1092
|
try {
|
|
998
1093
|
const res = await next(e);
|
|
999
1094
|
const text = res?.deny ?? (res?.isError ? (res.text ?? String(res.result ?? '')) : null);
|
|
@@ -1007,6 +1102,7 @@ export function register(on) {
|
|
|
1007
1102
|
// HOLD-6: nobody to ask (claude -p, the SDK), or hold switched off: refused as today.
|
|
1008
1103
|
if (!ask || !where?.interactive || minutes <= 0 || model.state?.kind !== 'ok') return res;
|
|
1009
1104
|
const id = e.tool_use_id;
|
|
1105
|
+
let outsideLimits = null;
|
|
1010
1106
|
// HOLD-5: under autonomy, the project's delegated rules decide first, without asking.
|
|
1011
1107
|
if ((settings?.autonomy ?? 'autonomous') === 'autonomous') {
|
|
1012
1108
|
const delegated = await $.process.run(
|
|
@@ -1023,6 +1119,8 @@ export function register(on) {
|
|
|
1023
1119
|
{ cwd: where.cwd, timeoutMs: 60000 },
|
|
1024
1120
|
);
|
|
1025
1121
|
if (delegated.exitCode === 0) return next(e);
|
|
1122
|
+
// DECIDE-2: why it's outside the limits the person set, for the decision's card.
|
|
1123
|
+
outsideLimits = (delegated.stderr || delegated.stdout || '').trim();
|
|
1026
1124
|
}
|
|
1027
1125
|
const agent = e.agentId ? agentsSeen.get(e.agentId) : null;
|
|
1028
1126
|
held.set(id, {
|
|
@@ -1035,6 +1133,10 @@ export function register(on) {
|
|
|
1035
1133
|
what: ask.what,
|
|
1036
1134
|
since: await $.clock.now(),
|
|
1037
1135
|
minutes,
|
|
1136
|
+
// DECIDE-2: the call itself, the rule that refused it, and why the lead couldn't approve it.
|
|
1137
|
+
command: e.command ?? e.file_path ?? null,
|
|
1138
|
+
refusal: text.split('\n')[0],
|
|
1139
|
+
outside: outsideLimits,
|
|
1038
1140
|
});
|
|
1039
1141
|
$.ui.invalidate('ui.render');
|
|
1040
1142
|
// The wait is a process the band's answer ends (`code-kit hold`), so it isn't the hook's own time.
|
|
@@ -1123,6 +1225,9 @@ export function register(on) {
|
|
|
1123
1225
|
$.ui.invalidate('ui.render');
|
|
1124
1226
|
}
|
|
1125
1227
|
const res = await next(e);
|
|
1228
|
+
// LOOP-3: a lane agent that has handed back its work isn't stalled while it waits; a message that
|
|
1229
|
+
// starts it again shows as its next tool call.
|
|
1230
|
+
if (e.agentId) activityOf(e.agentId, await $.clock.now()).handedBack = true;
|
|
1126
1231
|
// REVW-3: a subagent's last turn may be the reviewer's report.
|
|
1127
1232
|
if (e.agentId && act) await act.reviewed(e.agentId, e.answer).catch(() => {});
|
|
1128
1233
|
// LOOP-1: the lead is idle; the loop's next step, if one is due (submitted from turn.complete,
|
|
@@ -1166,6 +1271,9 @@ export function register(on) {
|
|
|
1166
1271
|
flagged: [...flagged.values()],
|
|
1167
1272
|
doneCount: loop.done?.count ?? 0,
|
|
1168
1273
|
steps: loop.steps,
|
|
1274
|
+
// LANES-1: the step the lead is taking now (the loop's last prompt, while its turn runs), and the next.
|
|
1275
|
+
current: leadTurn && loop.started ? (loop.steps.at(-1) ?? null) : null,
|
|
1276
|
+
nextText: nextText(loop.nextStep),
|
|
1169
1277
|
laneNotes,
|
|
1170
1278
|
now,
|
|
1171
1279
|
reviews: [...reviews.values()].map((r) => ({
|
|
@@ -1284,7 +1392,58 @@ export function register(on) {
|
|
|
1284
1392
|
};
|
|
1285
1393
|
};
|
|
1286
1394
|
// The band's lines, which the pane pins as what needs the person (VIEW-5).
|
|
1287
|
-
|
|
1395
|
+
// JOURNAL-1: the lineage from the project's records, with this session's loop steps and your own acts
|
|
1396
|
+
// in it, in time order.
|
|
1397
|
+
const STEP_WORDS = {
|
|
1398
|
+
dispatch: (t) => `The loop asked the lead to hand out ${t}.`,
|
|
1399
|
+
review: (t) => `The loop asked the lead to review ${t}.`,
|
|
1400
|
+
lead: (t) => `The loop asked the lead to build ${t} itself.`,
|
|
1401
|
+
merge: (t) => `The loop asked the lead to merge ${t}.`,
|
|
1402
|
+
restart: (t) => `The loop asked the lead to restart ${t}, which had gone quiet.`,
|
|
1403
|
+
nudge: (t) => `The loop nudged ${t}, which had gone quiet.`,
|
|
1404
|
+
flag: (t) => `${t} stalled again after its restarts, so it came to you.`,
|
|
1405
|
+
pause: () => 'You paused the loop.',
|
|
1406
|
+
resume: () => 'You resumed the loop.',
|
|
1407
|
+
done: (t) => `Every story was done (${t}).`,
|
|
1408
|
+
};
|
|
1409
|
+
const journalNow = () => {
|
|
1410
|
+
const recorded = journalData?.events ?? [];
|
|
1411
|
+
const steps = loop.steps
|
|
1412
|
+
.filter((x) => STEP_WORDS[x.kind])
|
|
1413
|
+
.map((x) => ({
|
|
1414
|
+
at: x.at,
|
|
1415
|
+
kind: 'loop',
|
|
1416
|
+
who: ['pause', 'resume'].includes(x.kind) ? 'person' : 'lead',
|
|
1417
|
+
text: STEP_WORDS[x.kind](x.target),
|
|
1418
|
+
}));
|
|
1419
|
+
return [...recorded, ...steps].sort((a, b) => a.at - b.at);
|
|
1420
|
+
};
|
|
1421
|
+
// DECIDE-2: the decisions that need the person, each with its lineage.
|
|
1422
|
+
const cardsNow = (now, lines = linesNowRaw(now)) => {
|
|
1423
|
+
const events = journalData?.events ?? [];
|
|
1424
|
+
const goal = goalOf(model.state?.stories ?? []);
|
|
1425
|
+
const asked =
|
|
1426
|
+
events.find((e) => e.kind === 'request')?.text.replace(/^You asked for /, '') ?? null;
|
|
1427
|
+
return decisionCards(lines, {
|
|
1428
|
+
state: model.state,
|
|
1429
|
+
status: model.status,
|
|
1430
|
+
check: model.check,
|
|
1431
|
+
goal: goal ? goal.name : null,
|
|
1432
|
+
request: asked,
|
|
1433
|
+
journal: events,
|
|
1434
|
+
inForce: model.requests?.inForce ?? [],
|
|
1435
|
+
});
|
|
1436
|
+
};
|
|
1437
|
+
// The band's lines, each that needs the person carrying which story it's for.
|
|
1438
|
+
const linesNow = (now) => {
|
|
1439
|
+
const lines = linesNowRaw(now);
|
|
1440
|
+
const cards = cardsNow(now, lines);
|
|
1441
|
+
return lines.map((l) => {
|
|
1442
|
+
const card = cards.find((c) => c.line === l);
|
|
1443
|
+
return card ? { ...l, context: contextOf(card) } : l;
|
|
1444
|
+
});
|
|
1445
|
+
};
|
|
1446
|
+
const linesNowRaw = (now) =>
|
|
1288
1447
|
bandLines({
|
|
1289
1448
|
...model,
|
|
1290
1449
|
reviewing,
|
|
@@ -1293,6 +1452,10 @@ export function register(on) {
|
|
|
1293
1452
|
held: [...held.values()],
|
|
1294
1453
|
now,
|
|
1295
1454
|
loop: loopNow(now),
|
|
1455
|
+
leadBusy: Boolean(leadTurn),
|
|
1456
|
+
live: liveNow(now),
|
|
1457
|
+
nextStep: loop.nextStep,
|
|
1458
|
+
combined: combinedNow(),
|
|
1296
1459
|
});
|
|
1297
1460
|
|
|
1298
1461
|
on('ui.render', { component: 'Pane', requestId: PANE_ID }, async ($, e) => {
|
|
@@ -1307,7 +1470,10 @@ export function register(on) {
|
|
|
1307
1470
|
usage,
|
|
1308
1471
|
plan,
|
|
1309
1472
|
loop: { ...loopNow(now), queue: model.queue ?? [] },
|
|
1310
|
-
needs: linesNow(now),
|
|
1473
|
+
needs: linesNow(now).filter((l) => !l.mission),
|
|
1474
|
+
cards: cardsNow(now),
|
|
1475
|
+
journal: journalNow(),
|
|
1476
|
+
now,
|
|
1311
1477
|
live: liveNow(now),
|
|
1312
1478
|
held: [...held.values()],
|
|
1313
1479
|
requests: model.requests?.open ?? [],
|
|
@@ -1322,6 +1488,11 @@ export function register(on) {
|
|
|
1322
1488
|
},
|
|
1323
1489
|
art: artNow(now, liveNow(now)),
|
|
1324
1490
|
combined: combinedNow(),
|
|
1491
|
+
codeMap,
|
|
1492
|
+
trail: trailData,
|
|
1493
|
+
glow: glowOf(touches, now),
|
|
1494
|
+
surface: paneSurface,
|
|
1495
|
+
columns: paneColumns,
|
|
1325
1496
|
placement: e.props.placement,
|
|
1326
1497
|
story: storyOpen ? storyNow(storyOpen, now, usage) : null,
|
|
1327
1498
|
},
|
|
@@ -1330,6 +1501,29 @@ export function register(on) {
|
|
|
1330
1501
|
onTab: (tab) => {
|
|
1331
1502
|
paneUi = { ...paneUi, tab };
|
|
1332
1503
|
$.ui.invalidate('ui.render');
|
|
1504
|
+
act?.readMaps().then(() => $.ui.invalidate('ui.render'));
|
|
1505
|
+
},
|
|
1506
|
+
onDecision: (i) => {
|
|
1507
|
+
paneUi = { ...paneUi, decision: i };
|
|
1508
|
+
$.ui.invalidate('ui.render');
|
|
1509
|
+
},
|
|
1510
|
+
onMapStory: (id) => {
|
|
1511
|
+
paneUi = { ...paneUi, mapStory: id, mapFile: null };
|
|
1512
|
+
$.ui.invalidate('ui.render');
|
|
1513
|
+
act?.readMaps().then(() => $.ui.invalidate('ui.render'));
|
|
1514
|
+
},
|
|
1515
|
+
onMapFile: (path) => {
|
|
1516
|
+
paneUi = { ...paneUi, mapFile: path };
|
|
1517
|
+
$.ui.invalidate('ui.render');
|
|
1518
|
+
},
|
|
1519
|
+
// MAP-3: the file in Context Graph's own view, through the command its adapter names.
|
|
1520
|
+
onOpenFile: (path) => {
|
|
1521
|
+
const command = codeMap?.open?.command;
|
|
1522
|
+
if (!command) return;
|
|
1523
|
+
$.command.run({ command, args: path }).catch((err) => {
|
|
1524
|
+
notice = `Couldn't open ${path}: ${err?.message ?? err}`;
|
|
1525
|
+
$.ui.invalidate('ui.render');
|
|
1526
|
+
});
|
|
1333
1527
|
},
|
|
1334
1528
|
onFilter: (state) => {
|
|
1335
1529
|
paneUi = { ...paneUi, filter: paneUi.filter === state ? null : state };
|