@rse/ase 2.0.1 → 2.0.3
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/dst/ase-agent-definition-core.js +111 -87
- package/dst/ase-agent-fleet.js +1 -1
- package/dst/ase-agent-runtime-claim.js +5 -3
- package/dst/ase-agent-runtime-engine.js +1 -1
- package/dst/ase-agent-runtime-phase.js +1 -1
- package/dst/ase-agent-runtime-request.js +1 -1
- package/dst/ase-agent-runtime.js +3 -5
- package/dst/ase-artifact.js +3 -3
- package/dst/ase-config-core.js +2 -16
- package/dst/ase-config-schema.js +1 -3
- package/dst/ase-config.js +4 -1
- package/dst/ase-hook.js +14 -19
- package/dst/ase-lib-daemon-cli.js +117 -4
- package/dst/ase-lib-server-cli.js +10 -10
- package/dst/ase-mcp.js +4 -4
- package/dst/ase-service-getopt.js +2 -2
- package/dst/ase-service-otel.js +4 -4
- package/dst/ase-service-skills.js +5 -15
- package/dst/ase-service.js +87 -210
- package/dst/ase-setup-common.js +1 -2
- package/dst/ase-setup-settings.js +6 -9
- package/dst/ase-setup.js +2 -3
- package/dst/ase-statusline.js +170 -170
- package/dst/ase-task-board-core.js +1 -1
- package/dst/ase-task-board-flow.js +7 -6
- package/dst/ase-task-board-graph.js +5 -4
- package/dst/ase-task-board-tui-control.js +51 -45
- package/dst/ase-task-board-tui-model.js +195 -181
- package/dst/ase-task-board-tui-popup.js +2 -3
- package/dst/ase-task-board-tui-view.js +88 -79
- package/dst/ase-task-board-web-client.css +1 -1
- package/dst/ase-task-board-web-client.js +2 -2
- package/dst/ase-task-board-web-server.js +4 -27
- package/dst/ase-task-board.js +1 -1
- package/dst/ase-task-format.js +1 -1
- package/dst/ase-task-store-client.js +17 -5
- package/dst/ase-task-store-core.js +5 -7
- package/dst/ase-task-store-issues.js +91 -0
- package/dst/ase-task-store-plugin-ase.js +7 -3
- package/dst/ase-task-store-plugin-gitea.js +11 -97
- package/dst/ase-task-store-plugin-github.js +5 -77
- package/dst/ase-task-store-plugin-gitlab.js +4 -83
- package/dst/ase-task.js +51 -23
- package/dst/ase-util-compat.js +1 -1
- package/dst/ase-util-diagram.js +16 -42
- package/dst/ase.js +5 -5
- package/dst/man/ase-agent-adhoc.1 +1 -1
- package/dst/man/ase-agent-condenser.1 +1 -1
- package/dst/man/ase-agent-editor.1 +1 -1
- package/dst/man/ase-agent-estimator.1 +1 -1
- package/dst/man/ase-agent-fleet.1 +1 -1
- package/dst/man/ase-agent-implementer.1 +1 -1
- package/dst/man/ase-agent-integrator.1 +1 -1
- package/dst/man/ase-agent-qualifier.1 +1 -1
- package/dst/man/ase-agent-rebooter.1 +1 -1
- package/dst/man/ase-agent-relay.1 +1 -1
- package/dst/man/ase-agent-researcher.1 +1 -1
- package/dst/man/ase-agent.1 +1 -1
- package/dst/man/ase-artifact-list.1 +1 -1
- package/dst/man/ase-artifact-name.1 +1 -1
- package/dst/man/ase-artifact.1 +1 -1
- package/dst/man/ase-config-delete.1 +1 -1
- package/dst/man/ase-config-edit.1 +1 -1
- package/dst/man/ase-config-get.1 +1 -1
- package/dst/man/ase-config-init.1 +1 -1
- package/dst/man/ase-config-list.1 +1 -1
- package/dst/man/ase-config-set.1 +1 -1
- package/dst/man/ase-config.1 +1 -1
- package/dst/man/ase-hook-permission-request.1 +1 -1
- package/dst/man/ase-hook-pre-tool-use.1 +1 -1
- package/dst/man/ase-hook-session-end.1 +1 -1
- package/dst/man/ase-hook-session-persona.1 +1 -1
- package/dst/man/ase-hook-session-start.1 +1 -1
- package/dst/man/ase-hook-stop.1 +1 -1
- package/dst/man/ase-hook-user-prompt-submit.1 +1 -1
- package/dst/man/ase-hook.1 +1 -1
- package/dst/man/ase-man.1 +1 -1
- package/dst/man/ase-mcp.1 +1 -1
- package/dst/man/ase-service-log.1 +1 -1
- package/dst/man/ase-service-send.1 +1 -1
- package/dst/man/ase-service-start.1 +1 -1
- package/dst/man/ase-service-status.1 +1 -1
- package/dst/man/ase-service-stop.1 +1 -1
- package/dst/man/ase-service.1 +1 -1
- package/dst/man/ase-setup-disable.1 +1 -1
- package/dst/man/ase-setup-enable.1 +1 -1
- package/dst/man/ase-setup-install.1 +1 -1
- package/dst/man/ase-setup-mcp.1 +1 -1
- package/dst/man/ase-setup-status.1 +1 -1
- package/dst/man/ase-setup-statusline.1 +1 -1
- package/dst/man/ase-setup-telemetry.1 +1 -1
- package/dst/man/ase-setup-uninstall.1 +1 -1
- package/dst/man/ase-setup-update.1 +1 -1
- package/dst/man/ase-setup.1 +1 -1
- package/dst/man/ase-spec-export.1 +1 -1
- package/dst/man/ase-spec-lint.1 +1 -1
- package/dst/man/ase-spec-preview.1 +1 -1
- package/dst/man/ase-spec.1 +1 -1
- package/dst/man/ase-statusline.1 +1 -1
- package/dst/man/ase-task-assign.1 +1 -1
- package/dst/man/ase-task-board.1 +3 -3
- package/dst/man/ase-task-delete.1 +1 -1
- package/dst/man/ase-task-edit.1 +1 -1
- package/dst/man/ase-task-idscheme.1 +1 -1
- package/dst/man/ase-task-lifecycle.1 +1 -1
- package/dst/man/ase-task-list.1 +1 -1
- package/dst/man/ase-task-load.1 +1 -1
- package/dst/man/ase-task-newid.1 +1 -1
- package/dst/man/ase-task-purge.1 +1 -1
- package/dst/man/ase-task-rename.1 +1 -1
- package/dst/man/ase-task-request.1 +1 -1
- package/dst/man/ase-task-save.1 +1 -1
- package/dst/man/ase-task-status.1 +1 -1
- package/dst/man/ase-task-store.1 +1 -1
- package/dst/man/ase-task-view.1 +1 -1
- package/dst/man/ase-task.1 +1 -1
- package/dst/man/ase-util-compat.1 +1 -1
- package/dst/man/ase-util-diagram.1 +1 -1
- package/dst/man/ase-util-meta.1 +1 -1
- package/dst/man/ase-util-metric.1 +1 -1
- package/dst/man/ase-util-mint.1 +1 -1
- package/dst/man/ase-util-worktree.1 +1 -1
- package/dst/man/ase-util.1 +1 -1
- package/dst/man/ase.1 +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/meta/ase-common-resolve.md +2 -2
- package/plugin/meta/ase-skill.md +7 -0
- package/plugin/mods/ase-mods.ts +16 -12
- package/plugin/skills/ase-arch-analyze/SKILL.md +3 -1
- package/plugin/skills/ase-code-analyze/SKILL.md +26 -10
- package/plugin/skills/ase-code-analyze/help.md +11 -6
- package/plugin/skills/ase-code-edit/SKILL.md +5 -4
- package/plugin/skills/ase-code-edit/help.md +1 -1
- package/plugin/skills/ase-code-lint/SKILL.md +52 -27
- package/plugin/skills/ase-code-lint/help.md +10 -5
- package/plugin/skills/ase-help-intent/data.md +60 -30
- package/plugin/skills/ase-help-skill/help.md +3 -4
- package/plugin/skills/ase-meta-quorum/SKILL.md +9 -0
- package/plugin/skills/ase-meta-quorum/help.md +5 -3
- package/plugin/skills/ase-meta-workflow/sample.md +5 -3
- package/plugin/skills/ase-repo-changelog/SKILL.md +8 -9
- package/plugin/skills/ase-repo-changelog/help.md +2 -2
- package/plugin/skills/ase-repo-review/SKILL.md +5 -3
- package/plugin/skills/ase-repo-review/help.md +3 -1
- package/plugin/skills/ase-spec-edit/SKILL.md +2 -1
- package/plugin/skills/ase-spec-edit/help.md +2 -1
- package/plugin/skills/ase-sync-reconcile/help.md +4 -4
- package/plugin/skills/ase-task-dissect/SKILL.md +10 -6
- package/plugin/skills/ase-task-grill/help.md +1 -1
- package/plugin/skills/ase-task-implement/SKILL.md +58 -1
- package/plugin/skills/ase-task-implement/help.md +16 -0
- package/plugin/skills/ase-task-list/help.md +2 -2
package/dst/ase-statusline.js
CHANGED
|
@@ -14,10 +14,11 @@ import { Config, sessionState } from "./ase-config-core.js";
|
|
|
14
14
|
import { configSchema } from "./ase-config-schema.js";
|
|
15
15
|
import { parseScope, userStateDir, projectIdOf } from "./ase-config-scope.js";
|
|
16
16
|
import { readStdin, writeStdout } from "./ase-lib-stdio.js";
|
|
17
|
+
import { parseTool } from "./ase-setup-common.js";
|
|
17
18
|
import pkg from "../package.json" with { type: "json" };
|
|
18
19
|
/* forced-color chalk instance: stdout is a pipe under Anthropic Claude Code CLI,
|
|
19
20
|
so chalk auto-detection would yield level 0; force level 1 to keep
|
|
20
|
-
emitting ANSI sequences
|
|
21
|
+
emitting ANSI sequences */
|
|
21
22
|
const c = new Chalk({ level: 1 });
|
|
22
23
|
/* set of valid <color>...</color> markup names (chalk basic foreground colors plus "default") */
|
|
23
24
|
const COLORS = new Set([
|
|
@@ -25,10 +26,9 @@ const COLORS = new Set([
|
|
|
25
26
|
]);
|
|
26
27
|
/* custom argument parser for Commander: non-negative integer */
|
|
27
28
|
const parseInteger = (name) => (value) => {
|
|
28
|
-
|
|
29
|
-
if (!Number.isFinite(n) || n < 0)
|
|
29
|
+
if (!/^\d+$/.test(value))
|
|
30
30
|
throw new InvalidArgumentError(`${name} must be a non-negative integer`);
|
|
31
|
-
return
|
|
31
|
+
return Number.parseInt(value, 10);
|
|
32
32
|
};
|
|
33
33
|
/* detect terminal column width via /dev/tty (stdout is a pipe under Anthropic Claude Code CLI) */
|
|
34
34
|
const detectTermWidth = () => {
|
|
@@ -39,7 +39,7 @@ const detectTermWidth = () => {
|
|
|
39
39
|
const out = execFileSync("tput", ["cols"], { stdio: [tty, "pipe", "ignore"] });
|
|
40
40
|
width = Number.parseInt(out.toString("utf8").trim(), 10) || 0;
|
|
41
41
|
}
|
|
42
|
-
catch
|
|
42
|
+
catch {
|
|
43
43
|
/* no controlling terminal */
|
|
44
44
|
}
|
|
45
45
|
finally {
|
|
@@ -47,7 +47,7 @@ const detectTermWidth = () => {
|
|
|
47
47
|
try {
|
|
48
48
|
fs.closeSync(tty);
|
|
49
49
|
}
|
|
50
|
-
catch
|
|
50
|
+
catch {
|
|
51
51
|
/* best-effort */
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -141,12 +141,12 @@ const probeGit = (cwd) => {
|
|
|
141
141
|
if (mDel !== null)
|
|
142
142
|
removed = Number.parseInt(mDel[1], 10);
|
|
143
143
|
}
|
|
144
|
-
catch
|
|
144
|
+
catch {
|
|
145
145
|
/* no HEAD yet or git failure; leave counts at 0 */
|
|
146
146
|
}
|
|
147
147
|
return { branch, untracked, added, removed };
|
|
148
148
|
}
|
|
149
|
-
catch
|
|
149
|
+
catch {
|
|
150
150
|
return { branch: "", untracked: 0, added: 0, removed: 0 };
|
|
151
151
|
}
|
|
152
152
|
};
|
|
@@ -157,7 +157,7 @@ const probeMemory = () => {
|
|
|
157
157
|
const free = os.freemem();
|
|
158
158
|
return { used: total - free, total };
|
|
159
159
|
}
|
|
160
|
-
catch
|
|
160
|
+
catch {
|
|
161
161
|
return { used: 0, total: 0 };
|
|
162
162
|
}
|
|
163
163
|
};
|
|
@@ -197,7 +197,7 @@ const fetchCodexLimits = async () => {
|
|
|
197
197
|
try {
|
|
198
198
|
cache = (JSON.parse(fs.readFileSync(cacheFile, "utf8")) ?? {});
|
|
199
199
|
}
|
|
200
|
-
catch
|
|
200
|
+
catch {
|
|
201
201
|
/* no (valid) cache yet */
|
|
202
202
|
}
|
|
203
203
|
if (typeof cache.time === "number" && Math.abs(Date.now() - cache.time) < CODEX_USAGE_TTL)
|
|
@@ -205,7 +205,8 @@ const fetchCodexLimits = async () => {
|
|
|
205
205
|
/* query the usage endpoint (keeping the stale result on any failure) */
|
|
206
206
|
let limits = cache.limits;
|
|
207
207
|
try {
|
|
208
|
-
const
|
|
208
|
+
const dir = process.env.CODEX_HOME ?? "";
|
|
209
|
+
const home = dir !== "" ? dir : path.join(os.homedir(), ".codex");
|
|
209
210
|
const auth = JSON.parse(fs.readFileSync(path.join(home, "auth.json"), "utf8"));
|
|
210
211
|
const token = auth?.tokens?.access_token;
|
|
211
212
|
if (typeof token === "string" && token !== "") {
|
|
@@ -233,7 +234,7 @@ const fetchCodexLimits = async () => {
|
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
}
|
|
236
|
-
catch
|
|
237
|
+
catch {
|
|
237
238
|
/* credential unreadable or endpoint unreachable */
|
|
238
239
|
}
|
|
239
240
|
/* update the cache */
|
|
@@ -241,23 +242,166 @@ const fetchCodexLimits = async () => {
|
|
|
241
242
|
fs.mkdirSync(path.dirname(cacheFile), { recursive: true });
|
|
242
243
|
fs.writeFileSync(cacheFile, JSON.stringify({ time: Date.now(), limits }) + "\n", "utf8");
|
|
243
244
|
}
|
|
244
|
-
catch
|
|
245
|
+
catch {
|
|
245
246
|
/* best-effort */
|
|
246
247
|
}
|
|
247
248
|
return limits;
|
|
248
249
|
};
|
|
250
|
+
/* create the identifier to renderer map: each callback fetches its own information
|
|
251
|
+
directly from data (or via the lazy probes for shared values) */
|
|
252
|
+
const createRenderers = (ctx) => {
|
|
253
|
+
const { data, tool, limits, emit, prefix, getSession, getCfg, getModel, getGit, getMem } = ctx;
|
|
254
|
+
/* render a configuration value (if non-empty) with its icon and label */
|
|
255
|
+
const cfgValue = (key, icon, label) => () => {
|
|
256
|
+
const value = getCfg()[key];
|
|
257
|
+
if (value !== "")
|
|
258
|
+
emit(`${prefix(icon, label)}${c.bold(value)}`);
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
/* ==== SCOPE ==== */
|
|
262
|
+
u: () => {
|
|
263
|
+
const user = process.env.USER ?? process.env.LOGNAME ?? "unknown";
|
|
264
|
+
emit(`${prefix("※", "user")}${c.bold(user)}`);
|
|
265
|
+
},
|
|
266
|
+
p: () => {
|
|
267
|
+
const id = projectIdOf(data.workspace?.current_dir ?? "");
|
|
268
|
+
emit(`${prefix("⚑", "project")}${c.bold(id)}`);
|
|
269
|
+
},
|
|
270
|
+
T: cfgValue("taskId", "◉", "task"),
|
|
271
|
+
s: () => emit(`${prefix("⏻", "session")}${c.bold(getSession())}`),
|
|
272
|
+
/* ==== MODEL ==== */
|
|
273
|
+
m: () => {
|
|
274
|
+
const { name } = getModel();
|
|
275
|
+
emit(`${prefix("⚙", "model")}${c.bold(name)}`);
|
|
276
|
+
},
|
|
277
|
+
e: () => {
|
|
278
|
+
const { effort } = getModel();
|
|
279
|
+
emit(`${prefix("⚒", "effort")}${c.bold(effort !== "" ? effort : "unknown")}`);
|
|
280
|
+
},
|
|
281
|
+
t: () => {
|
|
282
|
+
const thinking = data.thinking?.enabled === true ? "yes" : "no";
|
|
283
|
+
emit(`${prefix("⚛", "thinking")}${c.bold(thinking)}`);
|
|
284
|
+
},
|
|
285
|
+
/* ==== OUTPUT ==== */
|
|
286
|
+
O: () => {
|
|
287
|
+
const styleName = data.output_style?.name ?? "";
|
|
288
|
+
if (styleName !== "")
|
|
289
|
+
emit(`${prefix("≡", "style")}${c.bold(styleName)}`);
|
|
290
|
+
},
|
|
291
|
+
P: cfgValue("persona", "☯", "persona"),
|
|
292
|
+
h: cfgValue("guidance", "▶", "guidance"),
|
|
293
|
+
B: cfgValue("boxing", "▢", "boxing"),
|
|
294
|
+
L: cfgValue("lifecycle", "⟳", "lifecycle"),
|
|
295
|
+
/* ==== CONTEXT ==== */
|
|
296
|
+
c: () => {
|
|
297
|
+
const pct = Math.min(100, Math.max(0, Math.floor(data.context_window?.used_percentage ?? 0)));
|
|
298
|
+
const barSize = 20;
|
|
299
|
+
const filled = Math.round(pct / 100 * barSize);
|
|
300
|
+
const bar = "█".repeat(filled) + "░".repeat(barSize - filled);
|
|
301
|
+
emit(`${prefix("◔", "context")}${bar} ${pct}%`);
|
|
302
|
+
},
|
|
303
|
+
C: () => {
|
|
304
|
+
/* probe the live-context fields first and fall back to the
|
|
305
|
+
cumulative ones, so each harness contributes its own view:
|
|
306
|
+
GitHub Copilot CLI reports the live window separately (its
|
|
307
|
+
"total_*" are session sums), whereas Anthropic Claude Code CLI
|
|
308
|
+
folds it into "total_input_tokens". Output tokens stay out --
|
|
309
|
+
"used_percentage" is input-side only. */
|
|
310
|
+
const cw = data.context_window;
|
|
311
|
+
const pct = cw?.used_percentage ?? 0;
|
|
312
|
+
const tokensCur = cw?.current_context_tokens ??
|
|
313
|
+
cw?.last_call_input_tokens ?? cw?.total_input_tokens ?? 0;
|
|
314
|
+
const tokensLim = cw?.displayed_context_limit ?? cw?.context_window_size ??
|
|
315
|
+
(pct > 0 && tokensCur > 0 ? Math.round(tokensCur * 100 / pct) : 0);
|
|
316
|
+
if (tokensLim > 0)
|
|
317
|
+
emit(`${prefix("◆", "tokens")}${c.bold(formatTokens(tokensCur) + "/" + formatTokens(tokensLim))}`);
|
|
318
|
+
},
|
|
319
|
+
/* ==== RATE LIMITS ==== */
|
|
320
|
+
S: () => {
|
|
321
|
+
const pct5h = limits?.five_hour?.used_percentage;
|
|
322
|
+
const s = pct5h !== undefined ? `${pct5h.toFixed(1)}%` : "-";
|
|
323
|
+
emit(`${prefix("⏲", "5h-usage")}${c.bold(s)}`);
|
|
324
|
+
},
|
|
325
|
+
D: () => {
|
|
326
|
+
const until5h = limits?.five_hour?.resets_at;
|
|
327
|
+
const s = formatTimeUntil(until5h);
|
|
328
|
+
emit(`${prefix("⏱", "5h-resets")}${c.bold(s !== "" ? s : "-")}`);
|
|
329
|
+
},
|
|
330
|
+
W: () => {
|
|
331
|
+
const pctWk = limits?.seven_day?.used_percentage;
|
|
332
|
+
const s = pctWk !== undefined ? `${pctWk.toFixed(1)}%` : "-";
|
|
333
|
+
emit(`${prefix("⏲", "7d-usage")}${c.bold(s)}`);
|
|
334
|
+
},
|
|
335
|
+
Q: () => {
|
|
336
|
+
const untilWk = limits?.seven_day?.resets_at;
|
|
337
|
+
const s = formatTimeUntil(untilWk);
|
|
338
|
+
emit(`${prefix("⏱", "7d-resets")}${c.bold(s !== "" ? s : "-")}`);
|
|
339
|
+
},
|
|
340
|
+
/* ==== COSTS ==== */
|
|
341
|
+
H: () => {
|
|
342
|
+
const sessDurMs = data.cost?.total_duration_ms ?? 0;
|
|
343
|
+
if (sessDurMs > 0)
|
|
344
|
+
emit(`${prefix("⏱", "elapsed")}${c.bold(formatHoursMinutes(sessDurMs))}`);
|
|
345
|
+
},
|
|
346
|
+
X: () => {
|
|
347
|
+
const sessCost = data.cost?.total_cost_usd;
|
|
348
|
+
if (sessCost !== undefined)
|
|
349
|
+
emit(`${prefix("$", "cost")}${c.bold(formatCostUsd(sessCost))}`);
|
|
350
|
+
},
|
|
351
|
+
/* ==== VERSION CONTROL ==== */
|
|
352
|
+
a: () => {
|
|
353
|
+
const linesAdded = data.cost?.total_lines_added ?? 0;
|
|
354
|
+
emit(`${prefix("⊕", "added")}${c.bold(String(linesAdded))}`);
|
|
355
|
+
},
|
|
356
|
+
r: () => {
|
|
357
|
+
const linesRemoved = data.cost?.total_lines_removed ?? 0;
|
|
358
|
+
emit(`${prefix("⊖", "removed")}${c.bold(String(linesRemoved))}`);
|
|
359
|
+
},
|
|
360
|
+
b: () => {
|
|
361
|
+
const g = getGit();
|
|
362
|
+
const label = g.branch !== "" ? g.branch : "no git";
|
|
363
|
+
emit(`${prefix("⎇", "branch")}${c.bold(label)}`);
|
|
364
|
+
},
|
|
365
|
+
g: () => {
|
|
366
|
+
const g = getGit();
|
|
367
|
+
if (g.branch !== "")
|
|
368
|
+
emit(`${prefix("±", "changed")}${c.bold(`+${g.added}/-${g.removed}`)}`);
|
|
369
|
+
},
|
|
370
|
+
G: () => {
|
|
371
|
+
const g = getGit();
|
|
372
|
+
if (g.branch !== "")
|
|
373
|
+
emit(`${prefix("⁈", "untracked")}${c.bold(String(g.untracked))}`);
|
|
374
|
+
},
|
|
375
|
+
d: () => {
|
|
376
|
+
const cwd = data.workspace?.current_dir ?? "";
|
|
377
|
+
if (cwd !== "")
|
|
378
|
+
emit(`${prefix("▶", "cwd")}${c.bold(cwd)}`);
|
|
379
|
+
},
|
|
380
|
+
/* ==== RESOURCES ==== */
|
|
381
|
+
M: () => {
|
|
382
|
+
const m = getMem();
|
|
383
|
+
if (m.total > 0)
|
|
384
|
+
emit(`${prefix("⛁", "mem")}${c.bold(`${formatBytes(m.used)}/${formatBytes(m.total)}`)}`);
|
|
385
|
+
},
|
|
386
|
+
/* ==== VERSIONS ==== */
|
|
387
|
+
V: () => {
|
|
388
|
+
const toolVersion = data.version ?? "";
|
|
389
|
+
const aseVersion = pkg.version ?? "";
|
|
390
|
+
let version = "";
|
|
391
|
+
if (toolVersion !== "")
|
|
392
|
+
version += `${tool}/${toolVersion}`;
|
|
393
|
+
if (aseVersion !== "")
|
|
394
|
+
version += `${version !== "" ? " " : ""}ase/${aseVersion}`;
|
|
395
|
+
emit(`${prefix("⎈", "version")}${c.bold(version)}`);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
};
|
|
249
399
|
/* command-line handling */
|
|
250
400
|
export default class StatuslineCommand {
|
|
251
401
|
log;
|
|
252
402
|
constructor(log) {
|
|
253
403
|
this.log = log;
|
|
254
404
|
}
|
|
255
|
-
/* parse and validate the --tool option */
|
|
256
|
-
parseTool(value) {
|
|
257
|
-
if (value !== "claude" && value !== "copilot" && value !== "codex")
|
|
258
|
-
throw new InvalidArgumentError(`invalid --tool value: "${value}" (expected "claude", "copilot", or "codex")`);
|
|
259
|
-
return value;
|
|
260
|
-
}
|
|
261
405
|
/* register commands */
|
|
262
406
|
register(program) {
|
|
263
407
|
/* default for --tool derived from ASE_TOOL environment variable
|
|
@@ -280,7 +424,7 @@ export default class StatuslineCommand {
|
|
|
280
424
|
"(default: single line \"%m %e %t\")")
|
|
281
425
|
.action(async (lines, opts) => {
|
|
282
426
|
/* validate target tool */
|
|
283
|
-
const tool =
|
|
427
|
+
const tool = parseTool(opts.tool);
|
|
284
428
|
/* OpenAI Codex CLI has no scriptable custom statusline
|
|
285
429
|
mechanism, so reject the request explicitly */
|
|
286
430
|
if (tool === "codex")
|
|
@@ -361,7 +505,7 @@ export default class StatuslineCommand {
|
|
|
361
505
|
boxing = get("project.boxing", boxing);
|
|
362
506
|
lifecycle = get("project.task.lifecycle", lifecycle);
|
|
363
507
|
}
|
|
364
|
-
catch
|
|
508
|
+
catch {
|
|
365
509
|
/* cascade unavailable; keep env-var fallbacks */
|
|
366
510
|
}
|
|
367
511
|
try {
|
|
@@ -369,7 +513,7 @@ export default class StatuslineCommand {
|
|
|
369
513
|
state.read("lenient");
|
|
370
514
|
taskId = String(state.get("task") ?? "").trim() || taskId;
|
|
371
515
|
}
|
|
372
|
-
catch
|
|
516
|
+
catch {
|
|
373
517
|
/* session state unavailable; keep env-var fallback */
|
|
374
518
|
}
|
|
375
519
|
return { taskId, persona, guidance, boxing, lifecycle };
|
|
@@ -392,152 +536,8 @@ export default class StatuslineCommand {
|
|
|
392
536
|
&& /^gpt-.+/i.test(getModel().name)
|
|
393
537
|
&& tmpl.some((line) => /%[SDWQ]/.test(line)))
|
|
394
538
|
limits = await fetchCodexLimits();
|
|
395
|
-
/*
|
|
396
|
-
const
|
|
397
|
-
const value = getCfg()[key];
|
|
398
|
-
if (value !== "")
|
|
399
|
-
emit(`${prefix(icon, label)}${c.bold(value)}`);
|
|
400
|
-
};
|
|
401
|
-
/* identifier to renderer map: each callback fetches its own information
|
|
402
|
-
directly from data (or via the lazy helpers above for shared values) */
|
|
403
|
-
const renderers = {
|
|
404
|
-
/* ==== SCOPE ==== */
|
|
405
|
-
u: () => {
|
|
406
|
-
const user = process.env.USER ?? process.env.LOGNAME ?? "unknown";
|
|
407
|
-
emit(`${prefix("※", "user")}${c.bold(user)}`);
|
|
408
|
-
},
|
|
409
|
-
p: () => {
|
|
410
|
-
const id = projectIdOf(data.workspace?.current_dir ?? "");
|
|
411
|
-
emit(`${prefix("⚑", "project")}${c.bold(id)}`);
|
|
412
|
-
},
|
|
413
|
-
T: cfgValue("taskId", "◉", "task"),
|
|
414
|
-
s: () => emit(`${prefix("⏻", "session")}${c.bold(getSession())}`),
|
|
415
|
-
/* ==== MODEL ==== */
|
|
416
|
-
m: () => {
|
|
417
|
-
const { name } = getModel();
|
|
418
|
-
emit(`${prefix("⚙", "model")}${c.bold(name)}`);
|
|
419
|
-
},
|
|
420
|
-
e: () => {
|
|
421
|
-
const { effort } = getModel();
|
|
422
|
-
emit(`${prefix("⚒", "effort")}${c.bold(effort !== "" ? effort : "unknown")}`);
|
|
423
|
-
},
|
|
424
|
-
t: () => {
|
|
425
|
-
const thinking = data.thinking?.enabled === true ? "yes" : "no";
|
|
426
|
-
emit(`${prefix("⚛", "thinking")}${c.bold(thinking)}`);
|
|
427
|
-
},
|
|
428
|
-
/* ==== OUTPUT ==== */
|
|
429
|
-
O: () => {
|
|
430
|
-
const styleName = data.output_style?.name ?? "";
|
|
431
|
-
if (styleName !== "")
|
|
432
|
-
emit(`${prefix("≡", "style")}${c.bold(styleName)}`);
|
|
433
|
-
},
|
|
434
|
-
P: cfgValue("persona", "☯", "persona"),
|
|
435
|
-
h: cfgValue("guidance", "▶", "guidance"),
|
|
436
|
-
B: cfgValue("boxing", "▢", "boxing"),
|
|
437
|
-
L: cfgValue("lifecycle", "⟳", "lifecycle"),
|
|
438
|
-
/* ==== CONTEXT ==== */
|
|
439
|
-
c: () => {
|
|
440
|
-
const pct = Math.min(100, Math.max(0, Math.floor(data.context_window?.used_percentage ?? 0)));
|
|
441
|
-
const barSize = 20;
|
|
442
|
-
const filled = Math.round(pct / 100 * barSize);
|
|
443
|
-
const bar = "█".repeat(filled) + "░".repeat(barSize - filled);
|
|
444
|
-
emit(`${prefix("◔", "context")}${bar} ${pct}%`);
|
|
445
|
-
},
|
|
446
|
-
C: () => {
|
|
447
|
-
/* probe the live-context fields first and fall back to the
|
|
448
|
-
cumulative ones, so each harness contributes its own view:
|
|
449
|
-
GitHub Copilot CLI reports the live window separately (its
|
|
450
|
-
"total_*" are session sums), whereas Anthropic Claude Code CLI
|
|
451
|
-
folds it into "total_input_tokens". Output tokens stay out --
|
|
452
|
-
"used_percentage" is input-side only. */
|
|
453
|
-
const cw = data.context_window;
|
|
454
|
-
const pct = cw?.used_percentage ?? 0;
|
|
455
|
-
const tokensCur = cw?.current_context_tokens ??
|
|
456
|
-
cw?.last_call_input_tokens ?? cw?.total_input_tokens ?? 0;
|
|
457
|
-
const tokensLim = cw?.displayed_context_limit ?? cw?.context_window_size ??
|
|
458
|
-
(pct > 0 && tokensCur > 0 ? Math.round(tokensCur * 100 / pct) : 0);
|
|
459
|
-
if (tokensLim > 0)
|
|
460
|
-
emit(`${prefix("◆", "tokens")}${c.bold(formatTokens(tokensCur) + "/" + formatTokens(tokensLim))}`);
|
|
461
|
-
},
|
|
462
|
-
/* ==== RATE LIMITS ==== */
|
|
463
|
-
S: () => {
|
|
464
|
-
const pct5h = limits?.five_hour?.used_percentage;
|
|
465
|
-
const s = pct5h !== undefined ? `${pct5h.toFixed(1)}%` : "-";
|
|
466
|
-
emit(`${prefix("⏲", "5h-usage")}${c.bold(s)}`);
|
|
467
|
-
},
|
|
468
|
-
D: () => {
|
|
469
|
-
const until5h = limits?.five_hour?.resets_at;
|
|
470
|
-
const s = formatTimeUntil(until5h);
|
|
471
|
-
emit(`${prefix("⏱", "5h-resets")}${c.bold(s !== "" ? s : "-")}`);
|
|
472
|
-
},
|
|
473
|
-
W: () => {
|
|
474
|
-
const pctWk = limits?.seven_day?.used_percentage;
|
|
475
|
-
const s = pctWk !== undefined ? `${pctWk.toFixed(1)}%` : "-";
|
|
476
|
-
emit(`${prefix("⏲", "7d-usage")}${c.bold(s)}`);
|
|
477
|
-
},
|
|
478
|
-
Q: () => {
|
|
479
|
-
const untilWk = limits?.seven_day?.resets_at;
|
|
480
|
-
const s = formatTimeUntil(untilWk);
|
|
481
|
-
emit(`${prefix("⏱", "7d-resets")}${c.bold(s !== "" ? s : "-")}`);
|
|
482
|
-
},
|
|
483
|
-
/* ==== COSTS ==== */
|
|
484
|
-
H: () => {
|
|
485
|
-
const sessDurMs = data.cost?.total_duration_ms ?? 0;
|
|
486
|
-
if (sessDurMs > 0)
|
|
487
|
-
emit(`${prefix("⏱", "elapsed")}${c.bold(formatHoursMinutes(sessDurMs))}`);
|
|
488
|
-
},
|
|
489
|
-
X: () => {
|
|
490
|
-
const sessCost = data.cost?.total_cost_usd;
|
|
491
|
-
if (sessCost !== undefined)
|
|
492
|
-
emit(`${prefix("$", "cost")}${c.bold(formatCostUsd(sessCost))}`);
|
|
493
|
-
},
|
|
494
|
-
/* ==== VERSION CONTROL ==== */
|
|
495
|
-
a: () => {
|
|
496
|
-
const linesAdded = data.cost?.total_lines_added ?? 0;
|
|
497
|
-
emit(`${prefix("⊕", "added")}${c.bold(String(linesAdded))}`);
|
|
498
|
-
},
|
|
499
|
-
r: () => {
|
|
500
|
-
const linesRemoved = data.cost?.total_lines_removed ?? 0;
|
|
501
|
-
emit(`${prefix("⊖", "removed")}${c.bold(String(linesRemoved))}`);
|
|
502
|
-
},
|
|
503
|
-
b: () => {
|
|
504
|
-
const g = getGit();
|
|
505
|
-
const label = g.branch !== "" ? g.branch : "no git";
|
|
506
|
-
emit(`${prefix("⎇", "branch")}${c.bold(label)}`);
|
|
507
|
-
},
|
|
508
|
-
g: () => {
|
|
509
|
-
const g = getGit();
|
|
510
|
-
if (g.branch !== "")
|
|
511
|
-
emit(`${prefix("±", "changed")}${c.bold(`+${g.added}/-${g.removed}`)}`);
|
|
512
|
-
},
|
|
513
|
-
G: () => {
|
|
514
|
-
const g = getGit();
|
|
515
|
-
if (g.branch !== "")
|
|
516
|
-
emit(`${prefix("⁈", "untracked")}${c.bold(String(g.untracked))}`);
|
|
517
|
-
},
|
|
518
|
-
d: () => {
|
|
519
|
-
const cwd = data.workspace?.current_dir ?? "";
|
|
520
|
-
if (cwd !== "")
|
|
521
|
-
emit(`${prefix("▶", "cwd")}${c.bold(cwd)}`);
|
|
522
|
-
},
|
|
523
|
-
/* ==== RESOURCES ==== */
|
|
524
|
-
M: () => {
|
|
525
|
-
const m = getMem();
|
|
526
|
-
if (m.total > 0)
|
|
527
|
-
emit(`${prefix("⛁", "mem")}${c.bold(`${formatBytes(m.used)}/${formatBytes(m.total)}`)}`);
|
|
528
|
-
},
|
|
529
|
-
/* ==== VERSIONS ==== */
|
|
530
|
-
V: () => {
|
|
531
|
-
const toolVersion = data.version ?? "";
|
|
532
|
-
const aseVersion = pkg.version ?? "";
|
|
533
|
-
let version = "";
|
|
534
|
-
if (toolVersion !== "")
|
|
535
|
-
version += `${tool}/${toolVersion}`;
|
|
536
|
-
if (aseVersion !== "")
|
|
537
|
-
version += `${version !== "" ? " " : ""}ase/${aseVersion}`;
|
|
538
|
-
emit(`${prefix("⎈", "version")}${c.bold(version)}`);
|
|
539
|
-
}
|
|
540
|
-
};
|
|
539
|
+
/* identifier to renderer map (see "createRenderers") */
|
|
540
|
+
const renderers = createRenderers({ data, tool, limits, emit, prefix, getSession, getCfg, getModel, getGit, getMem });
|
|
541
541
|
/* walk each template line and render */
|
|
542
542
|
const closeSpan = () => {
|
|
543
543
|
span = null;
|
|
@@ -579,8 +579,8 @@ export default class StatuslineCommand {
|
|
|
579
579
|
if (opts.padding > 0) {
|
|
580
580
|
const pad = " ".repeat(opts.padding);
|
|
581
581
|
out = out.split("\n").map((line) => line !== "" ? `${pad}${line}${pad}` : line).join("\n");
|
|
582
|
-
/* for Copilot, prefix with a zero-width
|
|
583
|
-
to prevent the first line
|
|
582
|
+
/* for Copilot, prefix with a zero-width space
|
|
583
|
+
to prevent the first line from being unindented */
|
|
584
584
|
if (tool === "copilot")
|
|
585
585
|
out = "\u200B" + out;
|
|
586
586
|
}
|
|
@@ -187,7 +187,7 @@ export class BoardState {
|
|
|
187
187
|
state[surface][flag] = !state[surface][flag];
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
|
-
/* set the shown view (lanes, graph, stats, or
|
|
190
|
+
/* set the shown view (lanes, graph, stats, agents, or flow) */
|
|
191
191
|
static setView(surface, view) {
|
|
192
192
|
return BoardState.update((state) => {
|
|
193
193
|
state[surface].view = view;
|
|
@@ -133,12 +133,13 @@ const cardName = (card) => card.agents.length > 1 ? `${card.name} ×${card.agent
|
|
|
133
133
|
/* remove control characters from a text of the agent relay */
|
|
134
134
|
const clean = (text) => text.replace(/\p{Cc}/gu, "");
|
|
135
135
|
/* draw a cell-unit layout of the agent graph onto a character grid, returning per cell the
|
|
136
|
-
character and a tone for coloring (like the task graph): the state nodes in tone "done"
|
|
137
|
-
agent cards in tone "active" (busy), "idle" (waiting), or "sel" (selected,
|
|
138
|
-
a selected state), with the name of
|
|
139
|
-
|
|
140
|
-
"-
|
|
141
|
-
circle) of a busy card, resp. an empty circle of a waiting one, in the
|
|
136
|
+
character and a tone for coloring (like the task graph): the state nodes in tone "done"
|
|
137
|
+
(or "sel"), the agent cards in tone "active" (busy), "idle" (waiting), or "sel" (selected,
|
|
138
|
+
with a heavy border, like a selected state), with the name of the agent inverse on their
|
|
139
|
+
first line, followed by their field lines (with the tone suffix "-title" on the labels,
|
|
140
|
+
"-value" on their bold values, and the busy task ids inverse), and the given pulse glyph
|
|
141
|
+
(else a filled circle) of a busy card, resp. an empty circle of a waiting one, in the
|
|
142
|
+
right part of their top border */
|
|
142
143
|
export const drawFlowText = (flow, layout, selected, pulse = "", beat = () => false) => {
|
|
143
144
|
const g = textGrid(layout);
|
|
144
145
|
const { W, H, chars, tones } = g;
|
|
@@ -20,10 +20,11 @@ export const units = {
|
|
|
20
20
|
/* the id prefix of the compound node of a group (task ids never contain a colon) */
|
|
21
21
|
const GROUP = "group:";
|
|
22
22
|
/* the label lines of a node (in cell units only) of a given box width: the
|
|
23
|
-
task id (behind "≡", and of an epic also behind "♛") and title, cut onto a
|
|
24
|
-
onto at most three lines, with a
|
|
25
|
-
the task id to reserve the extra column
|
|
26
|
-
the "≡", and two more for the "♛" of
|
|
23
|
+
task id (behind "≡", and of an epic also behind "♛") and title, cut onto a
|
|
24
|
+
single line, or with titles shown, wrapped onto at most three lines, with a
|
|
25
|
+
placeholder column glued in front of the task id to reserve the extra column
|
|
26
|
+
of its inverse rendering (plus two for the "≡", and two more for the "♛" of
|
|
27
|
+
an epic), and the arrow in front of the title replaced by a placeholder
|
|
27
28
|
column glued to the title */
|
|
28
29
|
export const nodeLines = (board, id, titles, width) => {
|
|
29
30
|
const card = board.cards.get(id);
|
|
@@ -281,14 +281,13 @@ export const handleMouse = (ctx, btn, mx, my) => {
|
|
|
281
281
|
setDialog({ id: hit, tab: 0, first: 0, scrolls: {} });
|
|
282
282
|
}
|
|
283
283
|
};
|
|
284
|
-
/* handle a key press onto the
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
const { dialog, setFilter, typing, setTyping, setNotice, confirm, setConfirm, transfer, setTransfer, transferList, remove, transferTo, assign, setAssign, assignTo, prompt, setPrompt, cancelPrompt, submitPrompt } = ctx;
|
|
284
|
+
/* handle a key press onto the prompt popup */
|
|
285
|
+
const handlePromptKey = (ctx, prompt, input, key) => {
|
|
286
|
+
const { setPrompt, cancelPrompt, submitPrompt } = ctx;
|
|
288
287
|
/* operate the prompt popup: TAB resp. Shift+TAB switch the focus to the next resp.
|
|
289
288
|
previous field (the request name of a request by prompt, the prompt unless ignored, and
|
|
290
289
|
the request and cancel buttons), and ESC cancels in every field */
|
|
291
|
-
if (
|
|
290
|
+
if (key.tab || key.escape) {
|
|
292
291
|
if (key.escape)
|
|
293
292
|
cancelPrompt();
|
|
294
293
|
else {
|
|
@@ -301,7 +300,7 @@ const handlePopupKey = (ctx, input, key) => {
|
|
|
301
300
|
}
|
|
302
301
|
/* press the focused button of the prompt popup: RETURN (or SPACE) presses it,
|
|
303
302
|
and the left/right arrows switch between the request and cancel buttons */
|
|
304
|
-
if (prompt
|
|
303
|
+
if (prompt.focus === "request" || prompt.focus === "cancel") {
|
|
305
304
|
if ((key.return || input === " ") && prompt.focus === "request")
|
|
306
305
|
submitPrompt();
|
|
307
306
|
else if (key.return || input === " ")
|
|
@@ -310,17 +309,7 @@ const handlePopupKey = (ctx, input, key) => {
|
|
|
310
309
|
setPrompt({ ...prompt, focus: prompt.focus === "request" ? "cancel" : "request" });
|
|
311
310
|
return;
|
|
312
311
|
}
|
|
313
|
-
|
|
314
|
-
(under the kitty keyboard protocol only), Alt+RETURN (under the kitty keyboard
|
|
315
|
-
protocol, or where the terminal sends it as ESC+RETURN, e.g. with "Option as Meta"
|
|
316
|
-
on macOS), and Ctrl+J (in every terminal, as a plain linefeed without the kitty
|
|
317
|
-
keyboard protocol) insert a newline,
|
|
318
|
-
the left/right arrows move the cursor by one character, HOME/END move it
|
|
319
|
-
to the start/end, BACKSPACE removes the character before the cursor, DELETE
|
|
320
|
-
removes the character at the cursor, and all other input (also pasted text,
|
|
321
|
-
with its newlines kept and its other control characters turned into spaces)
|
|
322
|
-
is inserted at the cursor */
|
|
323
|
-
if (prompt !== null && prompt.request !== null && prompt.focus === "name") {
|
|
312
|
+
if (prompt.request !== null && prompt.focus === "name") {
|
|
324
313
|
/* type the request name of a request by prompt: RETURN switches to the prompt (resp. to
|
|
325
314
|
the request button if the prompt is ignored), the
|
|
326
315
|
up/down arrows cycle through the suggested request names of the drop-down (matching
|
|
@@ -342,36 +331,53 @@ const handlePopupKey = (ctx, input, key) => {
|
|
|
342
331
|
name(request.name + input.toLowerCase());
|
|
343
332
|
return;
|
|
344
333
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
334
|
+
/* type the prompt of an edit request: RETURN requests the edit, Shift+RETURN
|
|
335
|
+
(under the kitty keyboard protocol only), Alt+RETURN (under the kitty keyboard
|
|
336
|
+
protocol, or where the terminal sends it as ESC+RETURN, e.g. with "Option as Meta"
|
|
337
|
+
on macOS), and Ctrl+J (in every terminal, as a plain linefeed without the kitty
|
|
338
|
+
keyboard protocol) insert a newline,
|
|
339
|
+
the left/right arrows move the cursor by one character, HOME/END move it
|
|
340
|
+
to the start/end, BACKSPACE removes the character before the cursor, DELETE
|
|
341
|
+
removes the character at the cursor, and all other input (also pasted text,
|
|
342
|
+
with its newlines kept and its other control characters turned into spaces)
|
|
343
|
+
is inserted at the cursor */
|
|
344
|
+
const chars = [...prompt.text];
|
|
345
|
+
const pos = Math.max(0, Math.min(prompt.pos, chars.length));
|
|
346
|
+
const insert = (text) => {
|
|
347
|
+
const ins = [...text];
|
|
348
|
+
chars.splice(pos, 0, ...ins);
|
|
349
|
+
setPrompt({ ...prompt, text: chars.join(""), pos: pos + ins.length });
|
|
350
|
+
};
|
|
351
|
+
if ((key.return && (key.shift || key.meta)) || (key.ctrl && input === "j"))
|
|
352
|
+
insert("\n");
|
|
353
|
+
else if (key.return)
|
|
354
|
+
submitPrompt();
|
|
355
|
+
else if (key.leftArrow || key.rightArrow)
|
|
356
|
+
setPrompt({ ...prompt, pos: Math.max(0, Math.min(chars.length, pos + (key.leftArrow ? -1 : 1))) });
|
|
357
|
+
else if (key.home || key.end)
|
|
358
|
+
setPrompt({ ...prompt, pos: key.home ? 0 : chars.length });
|
|
359
|
+
else if (key.backspace) {
|
|
360
|
+
if (pos > 0) {
|
|
361
|
+
chars.splice(pos - 1, 1);
|
|
362
|
+
setPrompt({ ...prompt, text: chars.join(""), pos: pos - 1 });
|
|
366
363
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
364
|
+
}
|
|
365
|
+
else if (key.delete) {
|
|
366
|
+
if (pos < chars.length) {
|
|
367
|
+
chars.splice(pos, 1);
|
|
368
|
+
setPrompt({ ...prompt, text: chars.join(""), pos });
|
|
372
369
|
}
|
|
373
|
-
|
|
374
|
-
|
|
370
|
+
}
|
|
371
|
+
else if (!key.ctrl && !key.meta && input !== "")
|
|
372
|
+
insert(input.replace(/\r\n?/g, "\n").replace(/[^\P{Cc}\n]/gu, " "));
|
|
373
|
+
};
|
|
374
|
+
/* handle a key press onto the deletion confirmation, the transfer popup, the assign
|
|
375
|
+
popup, the prompt popup, or the filter field (outside of the read dialog) */
|
|
376
|
+
const handlePopupKey = (ctx, input, key) => {
|
|
377
|
+
const { dialog, setFilter, typing, setTyping, setNotice, confirm, setConfirm, transfer, setTransfer, transferList, remove, transferTo, assign, setAssign, assignTo, prompt } = ctx;
|
|
378
|
+
/* operate the prompt popup */
|
|
379
|
+
if (prompt !== null) {
|
|
380
|
+
handlePromptKey(ctx, prompt, input, key);
|
|
375
381
|
return;
|
|
376
382
|
}
|
|
377
383
|
/* answer the confirmation of a task deletion: "y" deletes, ESC cancels, the
|