@jwilger/pi-development-system 0.81.0 → 0.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -22,8 +22,9 @@ what the current phase expects. When you say a slice is finished with no review
|
|
|
22
22
|
it tells you to start one. The slash commands (`/devsys-start`, `/devsys-plan`, `/devsys-review`)
|
|
23
23
|
are shortcuts to the same tools.
|
|
24
24
|
|
|
25
|
-
With `codemode` enabled (`"defaultTools": ["+codemode"]` in pi settings), rarely used tools
|
|
26
|
-
the `judge_*` Jev wrappers
|
|
25
|
+
With `codemode` enabled (`"defaultTools": ["+codemode"]` in pi settings), rarely used tools are
|
|
26
|
+
reached through scripts and the `judge_*` Jev wrappers exist for scripts only; without codemode
|
|
27
|
+
the rarely used tools are declared directly and the wrappers are absent.
|
|
27
28
|
|
|
28
29
|
## Development
|
|
29
30
|
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import { phaseGuide } from "./phase-guide.ts";
|
|
|
9
9
|
* system prompt's state section; calling the tool returns the guidance for it.
|
|
10
10
|
*/
|
|
11
11
|
export const DEVSYS_TOOL_DESCRIPTION =
|
|
12
|
-
"What the development system expects in the current workflow phase (
|
|
12
|
+
"What the development system expects in the current workflow phase (the system prompt names it as of the start of the prompt; this call is live), and which tools to use. Call it when unsure what comes next.";
|
|
13
13
|
|
|
14
14
|
/** `devsys`: a model-only tool that returns the current phase's guidance. */
|
|
15
15
|
export function createPhaseTool(deps: { state: SessionState }): ToolDefinition {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DevsysState } from "../core/types.ts";
|
|
2
2
|
import type { Jev } from "../jev/client.ts";
|
|
3
|
-
import { type Intent, judgeIntent } from "../jev/questions/intent.ts";
|
|
3
|
+
import { type Intent, judgeIntent, requestText } from "../jev/questions/intent.ts";
|
|
4
4
|
|
|
5
5
|
/** Probability at or above which an intent is acted on. */
|
|
6
6
|
export const INTENT_AT = 0.7;
|
|
@@ -35,7 +35,7 @@ export async function intentLineFor(
|
|
|
35
35
|
state: DevsysState,
|
|
36
36
|
jev: Jev,
|
|
37
37
|
): Promise<string | undefined> {
|
|
38
|
-
if (state.phase !== "idle" || prompt
|
|
38
|
+
if (state.phase !== "idle" || requestText(prompt) === "" || jev.availability() === "offline") {
|
|
39
39
|
return undefined;
|
|
40
40
|
}
|
|
41
41
|
const judged = await judgeIntent(jev, { prompt, phase: state.phase });
|
|
@@ -28,7 +28,7 @@ export function buildPrinciplesSection(nonNegotiables: string): string {
|
|
|
28
28
|
*/
|
|
29
29
|
export function buildStateSection(state: DevsysState): string {
|
|
30
30
|
const lines = [
|
|
31
|
-
"##
|
|
31
|
+
"## Workflow state at the start of this prompt (call the devsys tool for the live phase)",
|
|
32
32
|
`- phase: ${state.phase}`,
|
|
33
33
|
`- sizing: ${state.sizing ?? "none"}`,
|
|
34
34
|
`- active slice: ${state.activeSlice ?? "none"}`,
|
|
@@ -79,7 +79,7 @@ export async function judgeTestChange(
|
|
|
79
79
|
? { before: input.before ?? "", after: "" }
|
|
80
80
|
: changeWindow(input.before ?? "", input.after);
|
|
81
81
|
const state = {
|
|
82
|
-
path: input.path,
|
|
82
|
+
path: clip(input.path).slice(0, 400),
|
|
83
83
|
before: clip(windowed.before),
|
|
84
84
|
after: input.after === undefined ? "" : clip(windowed.after),
|
|
85
85
|
deleted: input.after === undefined,
|
package/src/subagents/index.ts
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
|
-
//
|
|
1
|
+
// biome-ignore-all lint/suspicious/noUnnecessaryConditions: vendored upstream TUI/config code with no tests; rewriting it risks behaviour (see src/subagents/VENDORED.md)
|
|
2
|
+
// biome-ignore-all lint/suspicious/useAwait: vendored upstream TUI/config code with no tests; rewriting it risks behaviour (see src/subagents/VENDORED.md)
|
|
3
|
+
// biome-ignore-all lint/complexity/noVoid: vendored upstream TUI/config code with no tests; rewriting it risks behaviour (see src/subagents/VENDORED.md)
|
|
4
|
+
// biome-ignore-all lint/style/noNestedTernary: vendored upstream TUI/config code with no tests; rewriting it risks behaviour (see src/subagents/VENDORED.md)
|
|
5
|
+
// biome-ignore-all lint/style/noNonNullAssertion: vendored upstream TUI/config code with no tests; rewriting it risks behaviour (see src/subagents/VENDORED.md)
|
|
6
|
+
// biome-ignore-all lint/complexity/noExcessiveCognitiveComplexity: vendored upstream TUI/config code with no tests; rewriting it risks behaviour (see src/subagents/VENDORED.md)
|
|
2
7
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { buildSessionContext, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
4
8
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { buildSessionContext, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
5
10
|
import { fuzzyFilter } from "@earendil-works/pi-tui";
|
|
6
|
-
import {
|
|
7
|
-
import { IMPORT_REQUEST_PREFIX, markImportOffered, offerAgentImport } from "./prefs/agent-import.ts";
|
|
11
|
+
import { createInheritedToolSource } from "./orch/inherited-tools.ts";
|
|
8
12
|
import { ThreadManager } from "./orch/manager.ts";
|
|
13
|
+
import { subagentPrompt } from "./orch/prompt.ts";
|
|
14
|
+
import { createDriverFactory } from "./orch/runtime.ts";
|
|
15
|
+
import { agentTools } from "./orch/tools.ts";
|
|
16
|
+
import {
|
|
17
|
+
IMPORT_REQUEST_PREFIX,
|
|
18
|
+
markImportOffered,
|
|
19
|
+
offerAgentImport,
|
|
20
|
+
} from "./prefs/agent-import.ts";
|
|
21
|
+
import { ConfigStore } from "./prefs/config.ts";
|
|
9
22
|
import { loadManagerSettings } from "./prefs/settings.ts";
|
|
23
|
+
import type { SavedThread, ThreadEvent } from "./types.ts";
|
|
24
|
+
import { AgentNavigationEditor } from "./ui/agent-navigation-editor.ts";
|
|
10
25
|
import { configureAgents } from "./ui/settings-ui.ts";
|
|
11
26
|
import { AgentNavigationController, showAgentStatus } from "./ui/status-ui.ts";
|
|
12
|
-
import { AgentNavigationEditor } from "./ui/agent-navigation-editor.ts";
|
|
13
|
-
import { createDriverFactory } from "./orch/runtime.ts";
|
|
14
|
-
import { createInheritedToolSource } from "./orch/inherited-tools.ts";
|
|
15
|
-
import { subagentPrompt } from "./orch/prompt.ts";
|
|
16
|
-
import { agentTools } from "./orch/tools.ts";
|
|
17
27
|
import { editAgentTypes, updateWidget } from "./ui/ui.ts";
|
|
18
|
-
import type { SavedThread, ThreadEvent } from "./types.ts";
|
|
19
28
|
|
|
20
29
|
const REGISTRY_ENTRY = "pi-subagent:registry:v1";
|
|
21
30
|
const ROOT_MAILBOX_ENTRY = "pi-subagent:root-mailbox:v1";
|
|
@@ -81,32 +90,55 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
81
90
|
if (existingFactory && existingFactory !== navigationEditorFactory) return;
|
|
82
91
|
// The host only transfers raw text, not cursor/undo/expanded-paste state.
|
|
83
92
|
if (ctx.ui.getEditorText?.()) return;
|
|
84
|
-
const history = buildSessionContext(ctx.sessionManager.getBranch()).messages.flatMap(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
const history = buildSessionContext(ctx.sessionManager.getBranch()).messages.flatMap(
|
|
94
|
+
(message) => {
|
|
95
|
+
if (message.role !== "user") return [];
|
|
96
|
+
const content = message.content;
|
|
97
|
+
return [
|
|
98
|
+
typeof content === "string"
|
|
99
|
+
? content
|
|
100
|
+
: content
|
|
101
|
+
.filter((block) => block.type === "text")
|
|
102
|
+
.map((block) => block.text)
|
|
103
|
+
.join(""),
|
|
104
|
+
];
|
|
105
|
+
},
|
|
106
|
+
);
|
|
90
107
|
navigationEditorFactory = (tui, theme, keys) => {
|
|
91
108
|
const editor = new AgentNavigationEditor(tui, theme, keys, {
|
|
92
109
|
// Initial startup hydrates after session_start; replacement editors do not.
|
|
93
|
-
|
|
110
|
+
...(seedHistory ? { initialHistory: history } : {}),
|
|
94
111
|
generation: () => navigationGeneration,
|
|
95
|
-
canOpen: () =>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
canOpen: () =>
|
|
113
|
+
manager !== undefined &&
|
|
114
|
+
context?.mode === "tui" &&
|
|
115
|
+
limits.subagentMode !== "off" &&
|
|
116
|
+
!navigation.isOpen &&
|
|
117
|
+
manager
|
|
118
|
+
.list()
|
|
119
|
+
.some(
|
|
120
|
+
(thread) =>
|
|
121
|
+
thread.path !== "/root" &&
|
|
122
|
+
(thread.state === "starting" || thread.state === "running"),
|
|
123
|
+
),
|
|
124
|
+
openTree: () =>
|
|
125
|
+
manager && context
|
|
126
|
+
? navigation.open(context, manager.scope("/root"), undefined, limits.nerdFontIcons)
|
|
127
|
+
: undefined,
|
|
128
|
+
canCollapse: () =>
|
|
129
|
+
manager !== undefined &&
|
|
130
|
+
context?.mode === "tui" &&
|
|
131
|
+
limits.subagentMode !== "off" &&
|
|
132
|
+
!navigation.isOpen &&
|
|
133
|
+
!widgetCollapsed &&
|
|
134
|
+
limits.widgetMode === "full" &&
|
|
135
|
+
widgetThreads().some((thread) => thread.path !== "/root"),
|
|
104
136
|
collapseWidget: () => {
|
|
105
137
|
widgetCollapsed = true;
|
|
106
138
|
if (context) refreshWidget(context);
|
|
107
139
|
},
|
|
108
|
-
onError: (error) =>
|
|
109
|
-
error instanceof Error ? error.message : String(error), "error"),
|
|
140
|
+
onError: (error) =>
|
|
141
|
+
context?.ui.notify(error instanceof Error ? error.message : String(error), "error"),
|
|
110
142
|
});
|
|
111
143
|
navigationEditor = editor;
|
|
112
144
|
return editor;
|
|
@@ -120,7 +152,10 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
120
152
|
// Migration requires the root's file tools and must not delegate source prompts.
|
|
121
153
|
if (limits.subagentMode === "orchestration") {
|
|
122
154
|
if (!firstRun)
|
|
123
|
-
ctx.ui.notify(
|
|
155
|
+
ctx.ui.notify(
|
|
156
|
+
"Agent import requires Opportunistic mode. Change Subagent Mode in /agents settings.",
|
|
157
|
+
"info",
|
|
158
|
+
);
|
|
124
159
|
return;
|
|
125
160
|
}
|
|
126
161
|
if (importInProgress) return;
|
|
@@ -140,12 +175,15 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
140
175
|
if (!manager) throw new Error("Subagent threads are not initialized; start a Pi session first");
|
|
141
176
|
return manager;
|
|
142
177
|
};
|
|
143
|
-
const widgetThreads = () =>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
178
|
+
const widgetThreads = () =>
|
|
179
|
+
requireManager()
|
|
180
|
+
.list()
|
|
181
|
+
.filter((thread) => {
|
|
182
|
+
// Resuming a retained session makes it visible for this task, even after it settles again.
|
|
183
|
+
if (thread.state === "starting" || thread.state === "running")
|
|
184
|
+
hiddenWidgetThreads.delete(thread.path);
|
|
185
|
+
return !hiddenWidgetThreads.has(thread.path);
|
|
186
|
+
});
|
|
149
187
|
const requireContext = () => {
|
|
150
188
|
if (!context) throw new Error("No active Pi session");
|
|
151
189
|
return context;
|
|
@@ -187,44 +225,72 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
187
225
|
};
|
|
188
226
|
const pendingSummaries = (ctx: ExtensionContext) => {
|
|
189
227
|
const entries = ctx.sessionManager.getBranch();
|
|
190
|
-
const summarized = new Set(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
228
|
+
const summarized = new Set(
|
|
229
|
+
entries.flatMap((entry) => {
|
|
230
|
+
if (entry.type !== "custom_message" || entry.customType !== ROOT_SUMMARY_MESSAGE) return [];
|
|
231
|
+
const ids = (entry.details as { mailboxIds?: unknown } | undefined)?.mailboxIds;
|
|
232
|
+
return Array.isArray(ids) ? ids.filter((id): id is string => typeof id === "string") : [];
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
195
235
|
return entries.flatMap((entry) => {
|
|
196
236
|
if (entry.type !== "custom" || entry.customType !== ROOT_MAILBOX_ENTRY) return [];
|
|
197
237
|
const notification = entry.data as RootNotification | undefined;
|
|
198
238
|
return notification?.rootSessionId === ctx.sessionManager.getSessionId() &&
|
|
199
|
-
notification.finalRecap === true &&
|
|
239
|
+
notification.finalRecap === true &&
|
|
240
|
+
typeof notification.content === "string" &&
|
|
200
241
|
typeof notification.details?.mailboxId === "string" &&
|
|
201
|
-
!summarized.has(notification.details.mailboxId)
|
|
242
|
+
!summarized.has(notification.details.mailboxId)
|
|
243
|
+
? [notification]
|
|
244
|
+
: [];
|
|
202
245
|
});
|
|
203
246
|
};
|
|
204
247
|
const scheduleSummary = (ctx: ExtensionContext) => {
|
|
205
|
-
if (
|
|
206
|
-
|
|
248
|
+
if (
|
|
249
|
+
!limits.finalRecap ||
|
|
250
|
+
limits.subagentMode === "off" ||
|
|
251
|
+
summaryRunning ||
|
|
252
|
+
suspendingSummaries ||
|
|
253
|
+
summaryTimer !== undefined ||
|
|
254
|
+
ctx.isIdle() === false ||
|
|
255
|
+
pendingSummaries(ctx).length === 0
|
|
256
|
+
)
|
|
257
|
+
return;
|
|
207
258
|
const currentGeneration = generation;
|
|
208
259
|
// Coalesce siblings finishing together and leave the settlement boundary before
|
|
209
260
|
// starting a new turn. Recheck settings/session/idle state after the deferral.
|
|
210
261
|
summaryTimer = setTimeout(() => {
|
|
211
262
|
summaryTimer = undefined;
|
|
212
|
-
if (
|
|
213
|
-
|
|
263
|
+
if (
|
|
264
|
+
currentGeneration !== generation ||
|
|
265
|
+
context?.sessionManager !== ctx.sessionManager ||
|
|
266
|
+
!limits.finalRecap ||
|
|
267
|
+
limits.subagentMode === "off" ||
|
|
268
|
+
suspendingSummaries ||
|
|
269
|
+
ctx.isIdle() === false
|
|
270
|
+
)
|
|
271
|
+
return;
|
|
214
272
|
const notifications = pendingSummaries(ctx);
|
|
215
|
-
if (
|
|
273
|
+
if (notifications.length === 0) return;
|
|
216
274
|
summaryRunning = true;
|
|
217
275
|
try {
|
|
218
|
-
pi.sendMessage(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
276
|
+
pi.sendMessage(
|
|
277
|
+
{
|
|
278
|
+
customType: ROOT_SUMMARY_MESSAGE,
|
|
279
|
+
content:
|
|
280
|
+
"Summarize the newly finished asynchronous subagent results for the user. " +
|
|
281
|
+
"Their results are in the preceding subagent notifications; use agent_output if more detail is needed. " +
|
|
282
|
+
"Give a concise main-thread answer covering results and any failures. " +
|
|
283
|
+
"Do not spawn or resume agents solely to summarize.\nAgents: " +
|
|
284
|
+
[...new Set(notifications.map((notification) => notification.details.path))].join(
|
|
285
|
+
", ",
|
|
286
|
+
),
|
|
287
|
+
display: false,
|
|
288
|
+
details: {
|
|
289
|
+
mailboxIds: notifications.map((notification) => notification.details.mailboxId),
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{ triggerTurn: true },
|
|
293
|
+
);
|
|
228
294
|
} catch (error) {
|
|
229
295
|
summaryRunning = false;
|
|
230
296
|
warnDelivery(error);
|
|
@@ -278,7 +344,10 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
278
344
|
content: message,
|
|
279
345
|
// Foreground results already handled by a busy main thread do not need
|
|
280
346
|
// another turn. Completions during a summary do need a follow-up summary.
|
|
281
|
-
finalRecap:
|
|
347
|
+
finalRecap:
|
|
348
|
+
limits.finalRecap &&
|
|
349
|
+
!suspendingSummaries &&
|
|
350
|
+
event.kind === "settled" &&
|
|
282
351
|
(thread.state === "completed" || thread.state === "failed") &&
|
|
283
352
|
(requireContext().isIdle() || summaryRunning),
|
|
284
353
|
details: {
|
|
@@ -335,8 +404,14 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
335
404
|
const refreshWidget = (ctx: ExtensionContext) => {
|
|
336
405
|
if (limits.subagentMode === "off") {
|
|
337
406
|
if (ctx.hasUI) ctx.ui.setWidget("pi-subagent", undefined);
|
|
338
|
-
} else
|
|
339
|
-
|
|
407
|
+
} else
|
|
408
|
+
updateWidget(
|
|
409
|
+
ctx,
|
|
410
|
+
widgetThreads(),
|
|
411
|
+
widgetCollapsed ? "minimal" : limits.widgetMode,
|
|
412
|
+
limits.nerdFontIcons,
|
|
413
|
+
rootTurnEnded,
|
|
414
|
+
);
|
|
340
415
|
};
|
|
341
416
|
pi.on("agent_start", async (_event, ctx) => {
|
|
342
417
|
// A new task must not resurrect the previous task's settled agents. Automatic
|
|
@@ -356,7 +431,7 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
356
431
|
// A migration can span clarification turns. Reload after each root turn, without converting files.
|
|
357
432
|
if (limits.subagentMode === "off" || !migrationRequested) return;
|
|
358
433
|
store.reload();
|
|
359
|
-
if (store.diagnostics.length) context?.ui.notify(store.diagnostics.join("\n"), "warning");
|
|
434
|
+
if (store.diagnostics.length > 0) context?.ui.notify(store.diagnostics.join("\n"), "warning");
|
|
360
435
|
});
|
|
361
436
|
syncTools();
|
|
362
437
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
@@ -369,12 +444,17 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
369
444
|
ctx.ui.notify(`Could not save import onboarding state: ${String(error)}`, "warning");
|
|
370
445
|
}
|
|
371
446
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
447
|
+
// A section, not a returned `systemPrompt`: returning one makes pi force the whole prompt as
|
|
448
|
+
// the head of every request, so any change to our state section would miss the prompt cache.
|
|
449
|
+
event.systemPromptOptions.sections["pi-subagent"] = prompt;
|
|
450
|
+
return undefined;
|
|
375
451
|
});
|
|
376
452
|
|
|
377
|
-
const attachSession = async (
|
|
453
|
+
const attachSession = async (
|
|
454
|
+
ctx: ExtensionContext,
|
|
455
|
+
installEditor = false,
|
|
456
|
+
seedHistory = true,
|
|
457
|
+
) => {
|
|
378
458
|
cancelSummary();
|
|
379
459
|
const token = ++generation;
|
|
380
460
|
closeNavigation(installEditor);
|
|
@@ -437,7 +517,7 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
437
517
|
restoreRootMailbox(ctx);
|
|
438
518
|
refreshWidget(ctx);
|
|
439
519
|
const diagnostics = [...store.diagnostics, ...settingsDiagnostics];
|
|
440
|
-
if (limits.subagentMode !== "off" && diagnostics.length)
|
|
520
|
+
if (limits.subagentMode !== "off" && diagnostics.length > 0)
|
|
441
521
|
ctx.ui.notify(diagnostics.join("\n"), "warning");
|
|
442
522
|
};
|
|
443
523
|
pi.on("session_start", async (event, ctx) => {
|
|
@@ -511,10 +591,11 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
511
591
|
refreshWidget(ctx);
|
|
512
592
|
restoreRootMailbox(ctx);
|
|
513
593
|
ctx.ui.notify(
|
|
514
|
-
diagnostics.length
|
|
594
|
+
diagnostics.length > 0
|
|
515
595
|
? diagnostics.join("\n")
|
|
516
596
|
: `Loaded ${store.list().length} agent types; maximum ${limits.maxLevels} levels`,
|
|
517
|
-
|
|
597
|
+
|
|
598
|
+
diagnostics.length > 0 ? "warning" : "info",
|
|
518
599
|
);
|
|
519
600
|
} else if (!command || command === "settings") {
|
|
520
601
|
await configureAgents(ctx, {
|
|
@@ -527,13 +608,23 @@ export default function piSubagent(pi: ExtensionAPI): void {
|
|
|
527
608
|
syncTools();
|
|
528
609
|
refreshWidget(ctx);
|
|
529
610
|
restoreRootMailbox(ctx);
|
|
530
|
-
if (diagnostics.length) ctx.ui.notify(diagnostics.join("\n"), "warning");
|
|
611
|
+
if (diagnostics.length > 0) ctx.ui.notify(diagnostics.join("\n"), "warning");
|
|
531
612
|
},
|
|
532
613
|
});
|
|
533
614
|
} else if (command === "status") {
|
|
534
|
-
await showAgentStatus(
|
|
615
|
+
await showAgentStatus(
|
|
616
|
+
ctx,
|
|
617
|
+
requireManager().scope("/root"),
|
|
618
|
+
undefined,
|
|
619
|
+
limits.nerdFontIcons,
|
|
620
|
+
);
|
|
535
621
|
} else if (command === "tree") {
|
|
536
|
-
await navigation.open(
|
|
622
|
+
await navigation.open(
|
|
623
|
+
ctx,
|
|
624
|
+
requireManager().scope("/root"),
|
|
625
|
+
rest.join(" ") || undefined,
|
|
626
|
+
limits.nerdFontIcons,
|
|
627
|
+
);
|
|
537
628
|
} else
|
|
538
629
|
ctx.ui.notify(
|
|
539
630
|
"Usage: /agents [tree [path] | status | settings | types | import | reload]",
|