@asathinkeroops/nova-code 0.1.0-beta.5 → 0.1.0-beta.8
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 -4
- package/dist/index.js +485 -235
- package/package.json +9 -8
package/dist/index.js
CHANGED
|
@@ -1220,8 +1220,10 @@ var DEFAULT_SANDBOX_ALLOW_WRITE = [
|
|
|
1220
1220
|
// pnpm store/bin (Linux)
|
|
1221
1221
|
"~/Library/pnpm",
|
|
1222
1222
|
// pnpm store/bin (macOS)
|
|
1223
|
-
"~/.yarn"
|
|
1223
|
+
"~/.yarn",
|
|
1224
1224
|
// yarn global
|
|
1225
|
+
"~/.config/gh"
|
|
1226
|
+
// gh CLI auth/token refresh (git/PR workflow)
|
|
1225
1227
|
];
|
|
1226
1228
|
var modelPricingSchema = z2.object({
|
|
1227
1229
|
input: z2.number().nonnegative().describe("Price per 1M uncached (cache-miss) input tokens."),
|
|
@@ -1393,6 +1395,20 @@ var settingsSchema = z2.object({
|
|
|
1393
1395
|
additionalDirectories: [],
|
|
1394
1396
|
autoMode: { llmClassifier: true, classifierTimeoutMs: 8e3 }
|
|
1395
1397
|
}),
|
|
1398
|
+
// Workspace trust. On startup nova checks whether the workspace it was
|
|
1399
|
+
// launched in has been granted file access; an untrusted workspace prompts
|
|
1400
|
+
// the user to confirm, and declining exits. Trust is recorded HERE (in the
|
|
1401
|
+
// user-global config, which a project cannot write) rather than in any
|
|
1402
|
+
// project-checked-in file — mirroring Claude Code's `~/.claude.json` model,
|
|
1403
|
+
// so a cloned repo can never mark itself trusted. `trustedRoots` are absolute
|
|
1404
|
+
// canonical paths; a workspace equal to or nested under any of them is
|
|
1405
|
+
// trusted (so trusting a repo root covers its subdirectories). Users may
|
|
1406
|
+
// pre-seed roots by hand. Set `enabled: false` to restore the legacy
|
|
1407
|
+
// "workspace is always trusted on launch" behavior.
|
|
1408
|
+
trust: z2.object({
|
|
1409
|
+
enabled: z2.boolean().default(true),
|
|
1410
|
+
trustedRoots: z2.array(z2.string().min(1)).default([])
|
|
1411
|
+
}).default({ enabled: true, trustedRoots: [] }),
|
|
1396
1412
|
transcript: z2.object({
|
|
1397
1413
|
enabled: z2.boolean().default(true)
|
|
1398
1414
|
}).default({ enabled: true }),
|
|
@@ -2415,7 +2431,7 @@ function buildSystemPrompt(workspace, memory, sessionId, skillsBlock = "", langu
|
|
|
2415
2431
|
- Run long-lived commands (dev servers, watchers, builds) with runInBackground; it returns immediately and its output is delivered to you automatically when the command finishes \u2014 no need to poll.
|
|
2416
2432
|
- Load specialized knowledge with loadSkill.
|
|
2417
2433
|
- Delegate focused subtasks to parallel sub-agents with createSubAgent (type: explore = read-only retrieval, plan = read-only planning, general-purpose = full tools).
|
|
2418
|
-
- Don't guess file paths. When unsure whether a file
|
|
2434
|
+
- Don't guess file paths. When unsure whether a file or directory exists, locate it with glob/grep/ls before acting \u2014 a read, write, edit, or mkdir on a wrong path just wastes a turn or causes damage.
|
|
2419
2435
|
- Respond in ${language} by default, even when a tool or sub-agent returns content in another language \u2014 relay and summarize it in ${language}. Preserve that exact script and regional variant; do not switch it.
|
|
2420
2436
|
|
|
2421
2437
|
When I ask you to commit, follow the repo's own version-control conventions:
|
|
@@ -2424,6 +2440,11 @@ When I ask you to commit, follow the repo's own version-control conventions:
|
|
|
2424
2440
|
- Default to Conventional Commits (\`type(scope): summary\` \u2014 imperative subject \u226472 chars, a short body explaining the *why* when the change is non-trivial), but defer to the repo's own convention where it differs.
|
|
2425
2441
|
- Never push, and never add co-author or tool-attribution trailers, unless I ask.
|
|
2426
2442
|
|
|
2443
|
+
When I ask you to open a pull request, use the \`gh\` CLI (it is already authenticated \u2014 don't build a GitHub API client):
|
|
2444
|
+
- Gather context first: \`git status\` / \`git diff\`, \`git log <base>..HEAD\` for the commits, and the default branch (\`gh repo view --json defaultBranchRef\`) for the PR base.
|
|
2445
|
+
- Push the branch if it has no upstream (never straight to main/master), then run \`gh pr create\`. Pass the body via a quoted HEREDOC so its markdown survives: \`gh pr create --title "\u2026" --body "$(cat <<'EOF' \u2026 EOF)"\`.
|
|
2446
|
+
- Structure the body as \`## Summary\` (1\u20133 bullets on what changed and why) and \`## Test plan\` (a checklist of how to verify) \u2014 keep it to what the diff actually supports.
|
|
2447
|
+
|
|
2427
2448
|
Act, don't explain.
|
|
2428
2449
|
|
|
2429
2450
|
<identity name="Nova"></identity>
|
|
@@ -5291,13 +5312,15 @@ ${xmlEscape(payload)}
|
|
|
5291
5312
|
|
|
5292
5313
|
// ../../packages/tools/src/builtin/read.ts
|
|
5293
5314
|
import { readFile as readFile10 } from "fs/promises";
|
|
5294
|
-
import { extname as extname2, resolve as resolve9 } from "path";
|
|
5315
|
+
import { basename as basename3, extname as extname2, resolve as resolve9 } from "path";
|
|
5295
5316
|
import { z as z13 } from "zod";
|
|
5296
5317
|
import * as XLSX from "xlsx";
|
|
5318
|
+
import { extractText as extractText2 } from "unpdf";
|
|
5297
5319
|
var MAX_CHARS = 2e5;
|
|
5298
5320
|
var MAX_LINE_CHARS = 16e3;
|
|
5299
5321
|
var LINE_NO_WIDTH = 6;
|
|
5300
5322
|
var EXCEL_EXTENSIONS = /* @__PURE__ */ new Set([".xlsx", ".xls", ".xlsm", ".xlsb", ".ods"]);
|
|
5323
|
+
var MAX_PDF_BYTES = 3e7;
|
|
5301
5324
|
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
|
|
5302
5325
|
var MAX_IMAGE_BYTES = 2e7;
|
|
5303
5326
|
var IMAGE_MAGIC = {
|
|
@@ -5345,6 +5368,28 @@ function renderLines(lines, total, startIdx, endIdx, path3) {
|
|
|
5345
5368
|
}
|
|
5346
5369
|
return body;
|
|
5347
5370
|
}
|
|
5371
|
+
function paginateLines(raw, input, path3) {
|
|
5372
|
+
const lines = raw.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
5373
|
+
const total = lines.length;
|
|
5374
|
+
const startLine = input.offset ?? 1;
|
|
5375
|
+
const startIdx = startLine - 1;
|
|
5376
|
+
if (startIdx >= total && total > 0) {
|
|
5377
|
+
return {
|
|
5378
|
+
output: `read: offset ${startLine} is past end of file (it has ${total} lines)`,
|
|
5379
|
+
isError: true
|
|
5380
|
+
};
|
|
5381
|
+
}
|
|
5382
|
+
let endIdx = startIdx;
|
|
5383
|
+
let chars = 0;
|
|
5384
|
+
while (endIdx < total) {
|
|
5385
|
+
if (input.limit !== void 0 && endIdx - startIdx >= input.limit) break;
|
|
5386
|
+
const cost = Math.min(lines[endIdx].length, MAX_LINE_CHARS);
|
|
5387
|
+
if (chars > 0 && chars + cost > MAX_CHARS) break;
|
|
5388
|
+
chars += cost;
|
|
5389
|
+
endIdx += 1;
|
|
5390
|
+
}
|
|
5391
|
+
return { output: renderLines(lines, total, startIdx, endIdx, path3) };
|
|
5392
|
+
}
|
|
5348
5393
|
async function readText(abs, input, path3) {
|
|
5349
5394
|
let raw;
|
|
5350
5395
|
try {
|
|
@@ -5372,26 +5417,60 @@ async function readText(abs, input, path3) {
|
|
|
5372
5417
|
isError: true
|
|
5373
5418
|
};
|
|
5374
5419
|
}
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5420
|
+
return paginateLines(raw, input, path3);
|
|
5421
|
+
}
|
|
5422
|
+
async function readPdf(abs, input, path3) {
|
|
5423
|
+
let buf;
|
|
5424
|
+
try {
|
|
5425
|
+
buf = await readFile10(abs);
|
|
5426
|
+
} catch (err) {
|
|
5427
|
+
const code = err.code;
|
|
5428
|
+
if (code === "ENOENT") {
|
|
5429
|
+
return {
|
|
5430
|
+
output: `read failed: no such file: ${path3}. It may not exist at this path (or anywhere) \u2014 use glob/grep to locate it rather than guessing another path.`,
|
|
5431
|
+
isError: true
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
5434
|
+
if (code === "EISDIR") {
|
|
5435
|
+
return {
|
|
5436
|
+
output: `read failed: ${path3} is a directory, not a file. Use glob to list its contents or grep to search inside it.`,
|
|
5437
|
+
isError: true
|
|
5438
|
+
};
|
|
5439
|
+
}
|
|
5440
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5441
|
+
return { output: `read failed: ${msg}`, isError: true };
|
|
5442
|
+
}
|
|
5443
|
+
if (buf.length > MAX_PDF_BYTES) {
|
|
5444
|
+
const mb = (buf.length / 1e6).toFixed(1);
|
|
5445
|
+
const cap = (MAX_PDF_BYTES / 1e6).toFixed(0);
|
|
5380
5446
|
return {
|
|
5381
|
-
output: `read:
|
|
5447
|
+
output: `read failed: ${path3} is ${mb} MB (PDF cap is ${cap} MB). Use bash with a command-line tool (e.g. \`pdftotext\`, \`qpdf\`) to split or extract it.`,
|
|
5382
5448
|
isError: true
|
|
5383
5449
|
};
|
|
5384
5450
|
}
|
|
5385
|
-
let
|
|
5386
|
-
let
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5451
|
+
let totalPages;
|
|
5452
|
+
let pages;
|
|
5453
|
+
try {
|
|
5454
|
+
const extracted = await extractText2(new Uint8Array(buf), { mergePages: false });
|
|
5455
|
+
totalPages = extracted.totalPages;
|
|
5456
|
+
pages = extracted.text;
|
|
5457
|
+
} catch (err) {
|
|
5458
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5459
|
+
return { output: `read failed: cannot parse ${path3} as a PDF: ${msg}`, isError: true };
|
|
5393
5460
|
}
|
|
5394
|
-
|
|
5461
|
+
const meta = `PDF "${basename3(path3)}" \u2014 ${totalPages} page${totalPages === 1 ? "" : "s"}`;
|
|
5462
|
+
if (pages.every((p) => p.trim() === "")) {
|
|
5463
|
+
return {
|
|
5464
|
+
output: `${meta}
|
|
5465
|
+
read: no extractable text \u2014 this PDF is likely scanned or image-only. Use bash with an OCR tool (e.g. \`ocrmypdf\`, \`tesseract\`) to extract its text.`
|
|
5466
|
+
};
|
|
5467
|
+
}
|
|
5468
|
+
const combined = pages.map((p, i) => `[Page ${i + 1}]
|
|
5469
|
+
${p.trim()}`).join("\n\n");
|
|
5470
|
+
const result = paginateLines(combined, input, path3);
|
|
5471
|
+
if (result.isError) return result;
|
|
5472
|
+
return { output: `${meta}
|
|
5473
|
+
${result.output}` };
|
|
5395
5474
|
}
|
|
5396
5475
|
function resolveSheet(sheetNames, spec) {
|
|
5397
5476
|
if (spec === void 0) return sheetNames[0] ?? null;
|
|
@@ -5557,7 +5636,7 @@ async function readImage(abs, ext, path3) {
|
|
|
5557
5636
|
var readTool = {
|
|
5558
5637
|
definition: {
|
|
5559
5638
|
name: "read",
|
|
5560
|
-
description: "Read a text file, spreadsheet, or image from disk. For text files, output is line-numbered (`<line>\\t<text>`, `cat -n` style, 1-based); returns up to `limit` lines (and at most ~200K characters) per call. If more remains, the result tells you the exact read(path, offset) call to continue from. The line-number prefix is display only \u2014 strip it before passing text to `edit`. For Excel files (.xlsx/.xls/.xlsm/.xlsb/.ods), each row is rendered as a TSV line with a metadata header; use the optional `sheet` parameter to select a sheet. For image files (.png/.jpg/.jpeg/.gif/.webp), returns the image as a base64 block alongside metadata \u2014 only when the active model supports image input; capped at 20 MB.",
|
|
5639
|
+
description: "Read a text file, spreadsheet, PDF, or image from disk. For text files, output is line-numbered (`<line>\\t<text>`, `cat -n` style, 1-based); returns up to `limit` lines (and at most ~200K characters) per call. If more remains, the result tells you the exact read(path, offset) call to continue from. The line-number prefix is display only \u2014 strip it before passing text to `edit`. For Excel files (.xlsx/.xls/.xlsm/.xlsb/.ods), each row is rendered as a TSV line with a metadata header; use the optional `sheet` parameter to select a sheet. For PDF files (.pdf), extracted text is returned line-numbered with a `[Page N]` marker before each page and the same offset/limit paging; scanned/image-only PDFs have no extractable text (capped at 30 MB). For image files (.png/.jpg/.jpeg/.gif/.webp), returns the image as a base64 block alongside metadata \u2014 only when the active model supports image input; capped at 20 MB.",
|
|
5561
5640
|
inputSchema: inputSchema8
|
|
5562
5641
|
},
|
|
5563
5642
|
async run(rawInput, ctx) {
|
|
@@ -5576,6 +5655,9 @@ var readTool = {
|
|
|
5576
5655
|
if (EXCEL_EXTENSIONS.has(ext)) {
|
|
5577
5656
|
return readExcel(abs, input, input.path);
|
|
5578
5657
|
}
|
|
5658
|
+
if (ext === ".pdf") {
|
|
5659
|
+
return readPdf(abs, input, input.path);
|
|
5660
|
+
}
|
|
5579
5661
|
return readText(abs, input, input.path);
|
|
5580
5662
|
}
|
|
5581
5663
|
};
|
|
@@ -5642,11 +5724,14 @@ function parseSkillFile(text) {
|
|
|
5642
5724
|
if (typeof descRaw !== "string" || descRaw.trim().length === 0) {
|
|
5643
5725
|
return { error: "missing description" };
|
|
5644
5726
|
}
|
|
5645
|
-
const
|
|
5646
|
-
const
|
|
5647
|
-
const
|
|
5727
|
+
const whenRaw = meta["when_to_use"];
|
|
5728
|
+
const whenToUse = typeof whenRaw === "string" ? whenRaw.trim() : "";
|
|
5729
|
+
const combined = whenToUse ? `${descRaw} ${whenToUse}` : descRaw;
|
|
5730
|
+
const description = combined.length > DESCRIPTION_MAX ? combined.slice(0, DESCRIPTION_MAX) : combined;
|
|
5731
|
+
const disableModelInvocation = parseBool(meta["disable-model-invocation"], false);
|
|
5732
|
+
const userInvocable = parseBool(meta["user-invocable"], true);
|
|
5648
5733
|
const body = normalized.slice(fm[0].length).trimStart();
|
|
5649
|
-
return { ok: { name, description,
|
|
5734
|
+
return { ok: { name, description, disableModelInvocation, userInvocable, body } };
|
|
5650
5735
|
}
|
|
5651
5736
|
function parseFrontMatter2(src) {
|
|
5652
5737
|
const out = {};
|
|
@@ -5691,6 +5776,13 @@ function parseScalar2(v) {
|
|
|
5691
5776
|
}
|
|
5692
5777
|
return v;
|
|
5693
5778
|
}
|
|
5779
|
+
function parseBool(v, dflt) {
|
|
5780
|
+
if (typeof v !== "string") return dflt;
|
|
5781
|
+
const s = v.trim().toLowerCase();
|
|
5782
|
+
if (s === "true") return true;
|
|
5783
|
+
if (s === "false") return false;
|
|
5784
|
+
return dflt;
|
|
5785
|
+
}
|
|
5694
5786
|
function scan(r, logger) {
|
|
5695
5787
|
const targets = [];
|
|
5696
5788
|
for (const d of r.projectDirs) targets.push({ kind: "project", root: resolve10(r.cwd, d) });
|
|
@@ -5727,7 +5819,8 @@ function scan(r, logger) {
|
|
|
5727
5819
|
list.push({
|
|
5728
5820
|
name: parsed.ok.name,
|
|
5729
5821
|
description: parsed.ok.description,
|
|
5730
|
-
|
|
5822
|
+
disableModelInvocation: parsed.ok.disableModelInvocation,
|
|
5823
|
+
userInvocable: parsed.ok.userInvocable,
|
|
5731
5824
|
location: dir
|
|
5732
5825
|
});
|
|
5733
5826
|
seen.add(parsed.ok.name);
|
|
@@ -8219,7 +8312,7 @@ Working directory: ${workspace}
|
|
|
8219
8312
|
- Use tools to complete the task yourself. Work autonomously; do not ask the parent for clarification unless you are truly blocked.
|
|
8220
8313
|
- You run in isolation. The parent sees ONLY your final message \u2014 it cannot see your intermediate steps or tool output. End by reporting results concisely: what you did, key findings, and concrete file paths / follow-ups.
|
|
8221
8314
|
- Stay within the assigned task. Do not spawn further sub-agents.
|
|
8222
|
-
- Don't guess file paths. When unsure whether a file
|
|
8315
|
+
- Don't guess file paths. When unsure whether a file or directory exists, locate it with glob/grep/ls before acting \u2014 read, write, edit, or mkdir.
|
|
8223
8316
|
- Write your final report in the language of the task you were given, matching its exact script and regional variant (e.g. Simplified vs Traditional Chinese \u2014 don't switch a Simplified-Chinese task to Traditional), not the language of these instructions.
|
|
8224
8317
|
${extra}
|
|
8225
8318
|
Act, don't explain.
|
|
@@ -9238,6 +9331,9 @@ var DENY_RULES = [
|
|
|
9238
9331
|
{ re: /\bgit\s+clean\s+-[a-z]*f/i, reason: "git clean -f (deletes untracked files)" },
|
|
9239
9332
|
{ re: /\bgit\s+stash\s+(?:drop|clear)\b/i, reason: "git stash drop/clear (loses stashed work)" },
|
|
9240
9333
|
{ re: /\bgit\s+commit\b[^|&;]*--amend\b/i, reason: "git commit --amend (rewrites history)" },
|
|
9334
|
+
// Irreversible GitHub (gh) operations — merging or deleting.
|
|
9335
|
+
{ re: /\bgh\s+pr\s+merge\b/i, reason: "gh pr merge (merges a pull request)" },
|
|
9336
|
+
{ re: /\bgh\s+repo\s+delete\b/i, reason: "gh repo delete" },
|
|
9241
9337
|
// Infrastructure-as-code teardown.
|
|
9242
9338
|
{ re: /\b(?:terraform|terragrunt|pulumi|cdk|cdktf)\s+destroy\b/i, reason: "infrastructure destroy" },
|
|
9243
9339
|
// Mass cloud deletion.
|
|
@@ -9298,6 +9394,18 @@ var READ_ONLY_GIT_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
9298
9394
|
"tag",
|
|
9299
9395
|
"config"
|
|
9300
9396
|
]);
|
|
9397
|
+
var READ_ONLY_GH_SIGNATURES = /* @__PURE__ */ new Set([
|
|
9398
|
+
"pr view",
|
|
9399
|
+
"pr diff",
|
|
9400
|
+
"pr list",
|
|
9401
|
+
"pr checks",
|
|
9402
|
+
"pr status",
|
|
9403
|
+
"issue view",
|
|
9404
|
+
"issue list",
|
|
9405
|
+
"repo view",
|
|
9406
|
+
"run view",
|
|
9407
|
+
"run list"
|
|
9408
|
+
]);
|
|
9301
9409
|
var COMPOUND_RE = /[|&;<>`]|\$\(|\bxargs\b|\beval\b|\bexec\b/;
|
|
9302
9410
|
function effectiveTokens(command) {
|
|
9303
9411
|
const tokens = command.trim().split(/\s+/);
|
|
@@ -9316,9 +9424,10 @@ function classifyCommandStatic(command) {
|
|
|
9316
9424
|
if (re.test(cmd)) return "deny";
|
|
9317
9425
|
}
|
|
9318
9426
|
if (!COMPOUND_RE.test(cmd)) {
|
|
9319
|
-
const [verb, sub] = effectiveTokens(cmd);
|
|
9427
|
+
const [verb, sub, action] = effectiveTokens(cmd);
|
|
9320
9428
|
if (verb && READ_ONLY_COMMANDS.has(verb)) return "allow";
|
|
9321
9429
|
if (verb === "git" && sub && READ_ONLY_GIT_SUBCOMMANDS.has(sub)) return "allow";
|
|
9430
|
+
if (verb === "gh" && sub && action && READ_ONLY_GH_SIGNATURES.has(`${sub} ${action}`)) return "allow";
|
|
9322
9431
|
}
|
|
9323
9432
|
return "unknown";
|
|
9324
9433
|
}
|
|
@@ -9810,7 +9919,16 @@ async function writeStamp(dir) {
|
|
|
9810
9919
|
async function git(cwd, args, logger) {
|
|
9811
9920
|
logger?.debug({ args }, "nova-code-guide: git");
|
|
9812
9921
|
try {
|
|
9813
|
-
|
|
9922
|
+
const p = execFileP2("git", [...args], { cwd });
|
|
9923
|
+
const child = p.child;
|
|
9924
|
+
if (child) {
|
|
9925
|
+
const unref = (s) => s?.unref?.();
|
|
9926
|
+
child.unref();
|
|
9927
|
+
unref(child.stdin);
|
|
9928
|
+
unref(child.stdout);
|
|
9929
|
+
unref(child.stderr);
|
|
9930
|
+
}
|
|
9931
|
+
await p;
|
|
9814
9932
|
} catch (err) {
|
|
9815
9933
|
if (isEnoent(err)) {
|
|
9816
9934
|
throw new GuideProvisionError(
|
|
@@ -10709,6 +10827,7 @@ function loadSkillCommandsInto(registry, opts) {
|
|
|
10709
10827
|
const taken = new Set(registry.list().map((c) => c.name));
|
|
10710
10828
|
let added = 0;
|
|
10711
10829
|
for (const item of items) {
|
|
10830
|
+
if (!item.userInvocable) continue;
|
|
10712
10831
|
const cmd = {
|
|
10713
10832
|
name: item.name,
|
|
10714
10833
|
description: item.description,
|
|
@@ -10942,7 +11061,7 @@ import { render } from "ink";
|
|
|
10942
11061
|
|
|
10943
11062
|
// src/ui/app.tsx
|
|
10944
11063
|
import React12 from "react";
|
|
10945
|
-
import { Box as
|
|
11064
|
+
import { Box as Box15, Text as Text15 } from "ink";
|
|
10946
11065
|
import { useShallow as useShallow3 } from "zustand/react/shallow";
|
|
10947
11066
|
|
|
10948
11067
|
// src/ui/input-box.tsx
|
|
@@ -11811,20 +11930,24 @@ import { Box as Box4, Text as Text4 } from "ink";
|
|
|
11811
11930
|
|
|
11812
11931
|
// src/ui/logo.ts
|
|
11813
11932
|
var LOGO = [
|
|
11814
|
-
"\
|
|
11815
|
-
"\u2588\u2588\u2588 \u2588\u2588
|
|
11816
|
-
"
|
|
11817
|
-
"\u2588\u2588
|
|
11818
|
-
"\
|
|
11933
|
+
" \xB7 \u22C6 \u2726 \u22C6 \xB7 ",
|
|
11934
|
+
" \u22C6 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u22C6 ",
|
|
11935
|
+
" \u2727 \xB7 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \xB7 \u2727 ",
|
|
11936
|
+
"\u2726 \u207A \u2588\u2588 \u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u207A \u2726",
|
|
11937
|
+
" \xB7 \u2727 \u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2727 \xB7 ",
|
|
11938
|
+
" \u22C6 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u22C6 ",
|
|
11939
|
+
" \u207A \xB7 \u22C6 \xB7 \u2726 \u207A "
|
|
11819
11940
|
];
|
|
11820
11941
|
var LOGO_GRADIENT = [
|
|
11942
|
+
[140, 246, 255],
|
|
11821
11943
|
[0, 238, 255],
|
|
11822
11944
|
[90, 160, 255],
|
|
11823
11945
|
[170, 110, 245],
|
|
11824
11946
|
[230, 80, 210],
|
|
11825
|
-
[255, 60, 170]
|
|
11947
|
+
[255, 60, 170],
|
|
11948
|
+
[255, 140, 215]
|
|
11826
11949
|
];
|
|
11827
|
-
var LOGO_FALLBACK = [accent, blue, magenta, magenta, magenta];
|
|
11950
|
+
var LOGO_FALLBACK = [accent, accent, blue, magenta, magenta, magenta, magenta];
|
|
11828
11951
|
function bannerLine(line, row) {
|
|
11829
11952
|
if (useTruecolor) {
|
|
11830
11953
|
const stop = LOGO_GRADIENT[row];
|
|
@@ -11928,10 +12051,28 @@ function SetupView({ state }) {
|
|
|
11928
12051
|
] });
|
|
11929
12052
|
}
|
|
11930
12053
|
|
|
12054
|
+
// src/ui/trust-view.tsx
|
|
12055
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
12056
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
12057
|
+
function TrustView({ state }) {
|
|
12058
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
|
|
12059
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text5, { children: [
|
|
12060
|
+
/* @__PURE__ */ jsx5(Text5, { color: ACCENT_HEX, children: ">_" }),
|
|
12061
|
+
" Nova Code",
|
|
12062
|
+
" ",
|
|
12063
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: `(v${state.version})` })
|
|
12064
|
+
] }) }),
|
|
12065
|
+
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, children: LOGO.map((line, i) => /* @__PURE__ */ jsx5(Text5, { color: LOGO_ROW_HEX[i] ?? ACCENT_HEX, children: line }, i)) }),
|
|
12066
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { bold: true, color: ACCENT_HEX, children: "Do you trust the files in this folder?" }) }),
|
|
12067
|
+
/* @__PURE__ */ jsx5(Text5, { children: state.workspace }),
|
|
12068
|
+
/* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, children: state.lines.map((line, i) => /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: line }, i)) })
|
|
12069
|
+
] });
|
|
12070
|
+
}
|
|
12071
|
+
|
|
11931
12072
|
// src/ui/status-line.tsx
|
|
11932
12073
|
import React3 from "react";
|
|
11933
|
-
import { Box as
|
|
11934
|
-
import { basename as
|
|
12074
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
12075
|
+
import { basename as basename4 } from "path";
|
|
11935
12076
|
import { useShallow } from "zustand/react/shallow";
|
|
11936
12077
|
|
|
11937
12078
|
// src/ui/status-format.ts
|
|
@@ -12009,17 +12150,17 @@ function fitSegments(segments, maxWidth, sep3 = " | ") {
|
|
|
12009
12150
|
}
|
|
12010
12151
|
|
|
12011
12152
|
// src/ui/status-line.tsx
|
|
12012
|
-
import { jsx as
|
|
12153
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
12013
12154
|
function SegmentRow({
|
|
12014
12155
|
segments,
|
|
12015
12156
|
termCols
|
|
12016
12157
|
}) {
|
|
12017
12158
|
const shown = fitSegments(segments, Math.max(0, termCols - 2));
|
|
12018
|
-
return /* @__PURE__ */
|
|
12019
|
-
/* @__PURE__ */
|
|
12020
|
-
shown.map((seg, i) => /* @__PURE__ */
|
|
12021
|
-
i > 0 ? /* @__PURE__ */
|
|
12022
|
-
/* @__PURE__ */
|
|
12159
|
+
return /* @__PURE__ */ jsxs5(Box6, { children: [
|
|
12160
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
12161
|
+
shown.map((seg, i) => /* @__PURE__ */ jsxs5(React3.Fragment, { children: [
|
|
12162
|
+
i > 0 ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " | " }) : null,
|
|
12163
|
+
/* @__PURE__ */ jsxs5(Text6, { color: seg.color, children: [
|
|
12023
12164
|
seg.icon,
|
|
12024
12165
|
" ",
|
|
12025
12166
|
seg.text
|
|
@@ -12060,15 +12201,15 @@ function StatusLine({ store, shellMode = false }) {
|
|
|
12060
12201
|
}))
|
|
12061
12202
|
);
|
|
12062
12203
|
if (shellMode) {
|
|
12063
|
-
return /* @__PURE__ */
|
|
12064
|
-
/* @__PURE__ */
|
|
12065
|
-
/* @__PURE__ */
|
|
12204
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
12205
|
+
/* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text6, { color: SHELL_MODE_INDICATOR.color, children: ` ${SHELL_MODE_INDICATOR.label}` }) }),
|
|
12206
|
+
/* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text6, { children: " " }) })
|
|
12066
12207
|
] });
|
|
12067
12208
|
}
|
|
12068
12209
|
if (copyNotice) {
|
|
12069
|
-
return /* @__PURE__ */
|
|
12070
|
-
/* @__PURE__ */
|
|
12071
|
-
/* @__PURE__ */
|
|
12210
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
12211
|
+
/* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text6, { color: copyNoticeTone === "warn" ? "red" : "green", children: ` ${copyNotice}` }) }),
|
|
12212
|
+
/* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text6, { children: " " }) })
|
|
12072
12213
|
] });
|
|
12073
12214
|
}
|
|
12074
12215
|
const main = [];
|
|
@@ -12083,7 +12224,7 @@ function StatusLine({ store, shellMode = false }) {
|
|
|
12083
12224
|
main.push({ icon: "\u25CB", text: `${contextBar(pct)} ${pct}%`, color: "yellow" });
|
|
12084
12225
|
}
|
|
12085
12226
|
if (banner?.cwd) {
|
|
12086
|
-
main.push({ icon: "\u25C8", text:
|
|
12227
|
+
main.push({ icon: "\u25C8", text: basename4(banner.cwd) || banner.cwd, color: "green" });
|
|
12087
12228
|
}
|
|
12088
12229
|
if (gitBranch) {
|
|
12089
12230
|
main.push({ icon: "\u2387", text: gitBranch, color: "blue" });
|
|
@@ -12129,15 +12270,15 @@ function StatusLine({ store, shellMode = false }) {
|
|
|
12129
12270
|
color: "#d97706"
|
|
12130
12271
|
});
|
|
12131
12272
|
}
|
|
12132
|
-
return /* @__PURE__ */
|
|
12133
|
-
/* @__PURE__ */
|
|
12134
|
-
/* @__PURE__ */
|
|
12273
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
12274
|
+
/* @__PURE__ */ jsx6(SegmentRow, { segments: main, termCols }),
|
|
12275
|
+
/* @__PURE__ */ jsx6(SegmentRow, { segments: usage, termCols })
|
|
12135
12276
|
] });
|
|
12136
12277
|
}
|
|
12137
12278
|
|
|
12138
12279
|
// src/ui/picker.tsx
|
|
12139
12280
|
import { useState as useState2 } from "react";
|
|
12140
|
-
import { Box as
|
|
12281
|
+
import { Box as Box7, Text as Text7, useInput as useInput2, useStdout as useStdout2 } from "ink";
|
|
12141
12282
|
|
|
12142
12283
|
// src/ui/selection.ts
|
|
12143
12284
|
function sliceVisualCols(line, startCol, endCol) {
|
|
@@ -12233,7 +12374,7 @@ function highlightLines(lines, rect) {
|
|
|
12233
12374
|
}
|
|
12234
12375
|
|
|
12235
12376
|
// src/ui/picker.tsx
|
|
12236
|
-
import { jsx as
|
|
12377
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
12237
12378
|
function highlightRow(text, width) {
|
|
12238
12379
|
const tw = visibleWidth(text);
|
|
12239
12380
|
const padded = tw < width ? text + " ".repeat(width - tw) : text;
|
|
@@ -12336,12 +12477,12 @@ function PickList({ opts, onResolve, panelWidth }) {
|
|
|
12336
12477
|
const text = rendered[i - windowStart];
|
|
12337
12478
|
const body = i === selected && useColor ? highlightRow(text, barWidth) : text;
|
|
12338
12479
|
rows.push(
|
|
12339
|
-
/* @__PURE__ */
|
|
12480
|
+
/* @__PURE__ */ jsx7(Text7, { ...fullWidth ? { wrap: "truncate-end" } : {}, children: body }, i)
|
|
12340
12481
|
);
|
|
12341
12482
|
}
|
|
12342
12483
|
const borderProps = overlayBorderProps(bordered, opts.topRuleColor);
|
|
12343
|
-
return /* @__PURE__ */
|
|
12344
|
-
|
|
12484
|
+
return /* @__PURE__ */ jsxs6(
|
|
12485
|
+
Box7,
|
|
12345
12486
|
{
|
|
12346
12487
|
flexDirection: "column",
|
|
12347
12488
|
marginTop: 1,
|
|
@@ -12349,10 +12490,10 @@ function PickList({ opts, onResolve, panelWidth }) {
|
|
|
12349
12490
|
...fullWidth ? { width: cols } : {},
|
|
12350
12491
|
...borderProps,
|
|
12351
12492
|
children: [
|
|
12352
|
-
opts.header ? /* @__PURE__ */
|
|
12493
|
+
opts.header ? /* @__PURE__ */ jsx7(Text7, { children: opts.header }) : null,
|
|
12353
12494
|
rows,
|
|
12354
|
-
indicator ? /* @__PURE__ */
|
|
12355
|
-
opts.footer ? /* @__PURE__ */
|
|
12495
|
+
indicator ? /* @__PURE__ */ jsx7(Text7, { children: indicator }) : null,
|
|
12496
|
+
opts.footer ? /* @__PURE__ */ jsx7(Text7, { children: opts.footer }) : null
|
|
12356
12497
|
]
|
|
12357
12498
|
}
|
|
12358
12499
|
);
|
|
@@ -12408,13 +12549,13 @@ function ScrollViewer({ opts, onResolve, panelWidth }) {
|
|
|
12408
12549
|
for (let i = 0; i < visible.length; i++) {
|
|
12409
12550
|
const line = visible[i];
|
|
12410
12551
|
const body = line.bg && useColor ? tintRow(line.text, barWidth, line.bg) : line.text;
|
|
12411
|
-
rows.push(/* @__PURE__ */
|
|
12552
|
+
rows.push(/* @__PURE__ */ jsx7(Text7, { children: (line.gutter ?? "") + body }, start + i));
|
|
12412
12553
|
}
|
|
12413
12554
|
const indicator = lines.length > pageSize ? useColor ? dim(` (${start + 1}\u2013${end}/${lines.length})`) : ` (${start + 1}\u2013${end}/${lines.length})` : null;
|
|
12414
12555
|
const fullWidth = !bordered && !!opts.topRuleColor;
|
|
12415
12556
|
const borderProps = overlayBorderProps(bordered, opts.topRuleColor);
|
|
12416
|
-
return /* @__PURE__ */
|
|
12417
|
-
|
|
12557
|
+
return /* @__PURE__ */ jsxs6(
|
|
12558
|
+
Box7,
|
|
12418
12559
|
{
|
|
12419
12560
|
flexDirection: "column",
|
|
12420
12561
|
marginTop: 1,
|
|
@@ -12422,10 +12563,10 @@ function ScrollViewer({ opts, onResolve, panelWidth }) {
|
|
|
12422
12563
|
...fullWidth ? { width: cols } : {},
|
|
12423
12564
|
...borderProps,
|
|
12424
12565
|
children: [
|
|
12425
|
-
opts.header ? /* @__PURE__ */
|
|
12566
|
+
opts.header ? /* @__PURE__ */ jsx7(Text7, { children: opts.header }) : null,
|
|
12426
12567
|
rows,
|
|
12427
|
-
indicator ? /* @__PURE__ */
|
|
12428
|
-
opts.footer ? /* @__PURE__ */
|
|
12568
|
+
indicator ? /* @__PURE__ */ jsx7(Text7, { children: indicator }) : null,
|
|
12569
|
+
opts.footer ? /* @__PURE__ */ jsx7(Text7, { children: opts.footer }) : null
|
|
12429
12570
|
]
|
|
12430
12571
|
}
|
|
12431
12572
|
);
|
|
@@ -12473,11 +12614,11 @@ function PickHorizontal({ opts, onResolve, panelWidth }) {
|
|
|
12473
12614
|
items.forEach((item, i) => {
|
|
12474
12615
|
const badge = opts.badge?.(item) ?? null;
|
|
12475
12616
|
const label = ` ${opts.label(item)} `;
|
|
12476
|
-
if (i > 0) cells.push(/* @__PURE__ */
|
|
12617
|
+
if (i > 0) cells.push(/* @__PURE__ */ jsx7(Text7, { children: separator }, `sep-${i}`));
|
|
12477
12618
|
cells.push(
|
|
12478
|
-
/* @__PURE__ */
|
|
12479
|
-
/* @__PURE__ */
|
|
12480
|
-
badge ? /* @__PURE__ */
|
|
12619
|
+
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
12620
|
+
/* @__PURE__ */ jsx7(Text7, { inverse: i === selected, dimColor: i !== selected, children: label }),
|
|
12621
|
+
badge ? /* @__PURE__ */ jsx7(Text7, { color: ACCENT_HEX, bold: true, children: ` ${badge} ` }) : null
|
|
12481
12622
|
] }, i)
|
|
12482
12623
|
);
|
|
12483
12624
|
});
|
|
@@ -12485,8 +12626,8 @@ function PickHorizontal({ opts, onResolve, panelWidth }) {
|
|
|
12485
12626
|
const fullWidth = !bordered && !!opts.topRuleColor;
|
|
12486
12627
|
const cols = panelWidth ?? stdout?.columns ?? 80;
|
|
12487
12628
|
const borderProps = overlayBorderProps(bordered, opts.topRuleColor);
|
|
12488
|
-
return /* @__PURE__ */
|
|
12489
|
-
|
|
12629
|
+
return /* @__PURE__ */ jsxs6(
|
|
12630
|
+
Box7,
|
|
12490
12631
|
{
|
|
12491
12632
|
flexDirection: "column",
|
|
12492
12633
|
marginTop: 1,
|
|
@@ -12495,11 +12636,11 @@ function PickHorizontal({ opts, onResolve, panelWidth }) {
|
|
|
12495
12636
|
...fullWidth ? { width: cols } : {},
|
|
12496
12637
|
...borderProps,
|
|
12497
12638
|
children: [
|
|
12498
|
-
opts.header ? /* @__PURE__ */
|
|
12499
|
-
/* @__PURE__ */
|
|
12500
|
-
/* @__PURE__ */
|
|
12501
|
-
/* @__PURE__ */
|
|
12502
|
-
opts.footer ? /* @__PURE__ */
|
|
12639
|
+
opts.header ? /* @__PURE__ */ jsx7(Text7, { children: opts.header }) : null,
|
|
12640
|
+
/* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
12641
|
+
/* @__PURE__ */ jsx7(Box7, { children: cells }),
|
|
12642
|
+
/* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
12643
|
+
opts.footer ? /* @__PURE__ */ jsx7(Text7, { children: opts.footer }) : null
|
|
12503
12644
|
]
|
|
12504
12645
|
}
|
|
12505
12646
|
);
|
|
@@ -12507,12 +12648,12 @@ function PickHorizontal({ opts, onResolve, panelWidth }) {
|
|
|
12507
12648
|
|
|
12508
12649
|
// src/ui/viewport.tsx
|
|
12509
12650
|
import React11 from "react";
|
|
12510
|
-
import { Box as
|
|
12651
|
+
import { Box as Box14, Text as Text14 } from "ink";
|
|
12511
12652
|
import { useShallow as useShallow2 } from "zustand/react/shallow";
|
|
12512
12653
|
|
|
12513
12654
|
// src/ui/approval.tsx
|
|
12514
12655
|
import { useState as useState3 } from "react";
|
|
12515
|
-
import { Box as
|
|
12656
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
12516
12657
|
|
|
12517
12658
|
// src/ui/measure.ts
|
|
12518
12659
|
import wrapAnsi2 from "wrap-ansi";
|
|
@@ -13069,7 +13210,7 @@ function wrapThinkingBody(text, width) {
|
|
|
13069
13210
|
return wrapAnsi(trimmed, bodyWidth, { hard: true, wordWrap: false, trim: false }).split("\n");
|
|
13070
13211
|
}
|
|
13071
13212
|
function renderThinking(text, label, width, collapsed = false, expanded = false) {
|
|
13072
|
-
const head = `${magenta("\
|
|
13213
|
+
const head = `${magenta("\u2726")} ${dim(`thinking${label ? ` \xB7 ${label}` : ""}`)}`;
|
|
13073
13214
|
const lines = wrapThinkingBody(text, width);
|
|
13074
13215
|
if (lines.length === 0) return head;
|
|
13075
13216
|
const overflow = collapsed ? Math.max(0, lines.length - THINKING_COLLAPSED_MAX_LINES) : 0;
|
|
@@ -13092,7 +13233,7 @@ function thinkingToggleLineIndex(item, width) {
|
|
|
13092
13233
|
return 1 + shown;
|
|
13093
13234
|
}
|
|
13094
13235
|
function renderRedactedThinking(label) {
|
|
13095
|
-
return `${magenta("\
|
|
13236
|
+
return `${magenta("\u2726")} ${dim(`thinking${label ? ` \xB7 ${label}` : ""} (redacted)`)}`;
|
|
13096
13237
|
}
|
|
13097
13238
|
function renderCard(card) {
|
|
13098
13239
|
const color = cardColor[card.kind];
|
|
@@ -13364,7 +13505,7 @@ function genericUseHeader(use) {
|
|
|
13364
13505
|
return header(use.name, dim(trim(compact)));
|
|
13365
13506
|
}
|
|
13366
13507
|
var DETAIL_MARK = {
|
|
13367
|
-
thinking: "\
|
|
13508
|
+
thinking: "\u2726",
|
|
13368
13509
|
tool_use: "\u2692",
|
|
13369
13510
|
final: "\u2192"
|
|
13370
13511
|
};
|
|
@@ -13563,7 +13704,7 @@ function sliceLines(items, width, offset, viewportRows) {
|
|
|
13563
13704
|
}
|
|
13564
13705
|
|
|
13565
13706
|
// src/ui/approval.tsx
|
|
13566
|
-
import { Fragment as Fragment2, jsx as
|
|
13707
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
13567
13708
|
var TOOL_PROMPTS = {
|
|
13568
13709
|
read: "Allow reading this file?",
|
|
13569
13710
|
write: "Allow writing this file?",
|
|
@@ -13680,8 +13821,8 @@ function ApprovalPrompt({
|
|
|
13680
13821
|
});
|
|
13681
13822
|
const inner = approvalInnerWidth(width);
|
|
13682
13823
|
const { text: detail, truncated } = clampDetail(describeToolInput(input.input));
|
|
13683
|
-
return /* @__PURE__ */
|
|
13684
|
-
|
|
13824
|
+
return /* @__PURE__ */ jsxs7(
|
|
13825
|
+
Box8,
|
|
13685
13826
|
{
|
|
13686
13827
|
flexDirection: "column",
|
|
13687
13828
|
marginTop: 1,
|
|
@@ -13690,31 +13831,31 @@ function ApprovalPrompt({
|
|
|
13690
13831
|
borderStyle: "round",
|
|
13691
13832
|
borderColor: "gray",
|
|
13692
13833
|
children: [
|
|
13693
|
-
/* @__PURE__ */
|
|
13694
|
-
/* @__PURE__ */
|
|
13834
|
+
/* @__PURE__ */ jsx8(Text8, { children: promptFor(input.tool) }),
|
|
13835
|
+
/* @__PURE__ */ jsx8(Text8, { children: " " }),
|
|
13695
13836
|
input.tool === "bash" ? (
|
|
13696
13837
|
// Mirror the message-stream bash rendering: `● bash` header with the
|
|
13697
13838
|
// command under a `⎿` gutter, so heredocs/long one-liners preview the
|
|
13698
13839
|
// way they'll print in the transcript instead of as a flat line.
|
|
13699
|
-
/* @__PURE__ */
|
|
13700
|
-
/* @__PURE__ */
|
|
13701
|
-
/* @__PURE__ */
|
|
13702
|
-
truncated ? /* @__PURE__ */
|
|
13840
|
+
/* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
13841
|
+
/* @__PURE__ */ jsx8(Text8, { children: BASH_HEADER }),
|
|
13842
|
+
/* @__PURE__ */ jsx8(Text8, { children: renderCommandBody(detail, inner) }),
|
|
13843
|
+
truncated ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: `${BASH_BODY_INDENT}\u2026 (truncated)` }) : null
|
|
13703
13844
|
] })
|
|
13704
|
-
) : /* @__PURE__ */
|
|
13705
|
-
/* @__PURE__ */
|
|
13845
|
+
) : /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
13846
|
+
/* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
|
|
13706
13847
|
input.tool,
|
|
13707
13848
|
" "
|
|
13708
13849
|
] }),
|
|
13709
|
-
/* @__PURE__ */
|
|
13710
|
-
truncated ? /* @__PURE__ */
|
|
13850
|
+
/* @__PURE__ */ jsx8(Text8, { color: ACCENT_HEX, children: detail }),
|
|
13851
|
+
truncated ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " \u2026 (truncated)" }) : null
|
|
13711
13852
|
] }),
|
|
13712
|
-
/* @__PURE__ */
|
|
13853
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", marginTop: 1, children: OPTIONS.map((opt, i) => {
|
|
13713
13854
|
const active = i === cursor;
|
|
13714
|
-
return /* @__PURE__ */
|
|
13855
|
+
return /* @__PURE__ */ jsxs7(Text8, { color: active ? opt.color : void 0, children: [
|
|
13715
13856
|
active ? "\u276F " : " ",
|
|
13716
|
-
/* @__PURE__ */
|
|
13717
|
-
/* @__PURE__ */
|
|
13857
|
+
/* @__PURE__ */ jsx8(Text8, { children: opt.label }),
|
|
13858
|
+
/* @__PURE__ */ jsxs7(Text8, { dimColor: true, children: [
|
|
13718
13859
|
" (",
|
|
13719
13860
|
opt.hint,
|
|
13720
13861
|
")"
|
|
@@ -13728,8 +13869,8 @@ function ApprovalPrompt({
|
|
|
13728
13869
|
|
|
13729
13870
|
// src/ui/ask-user.tsx
|
|
13730
13871
|
import { useState as useState4 } from "react";
|
|
13731
|
-
import { Box as
|
|
13732
|
-
import { Fragment as Fragment3, jsx as
|
|
13872
|
+
import { Box as Box9, Text as Text9, useInput as useInput4 } from "ink";
|
|
13873
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
13733
13874
|
var OTHER_LABEL = "Other";
|
|
13734
13875
|
var CONFIRM_HEADER = "Confirm";
|
|
13735
13876
|
var CONFIRM_SUBMIT = 0;
|
|
@@ -14000,25 +14141,25 @@ function AskPanel({ req, onResolve }) {
|
|
|
14000
14141
|
});
|
|
14001
14142
|
const labelWidth = q ? Math.max(...q.options.map((o) => visibleWidth(o.label))) : 0;
|
|
14002
14143
|
const everyAnswered = allAnswered(states);
|
|
14003
|
-
return /* @__PURE__ */
|
|
14004
|
-
/* @__PURE__ */
|
|
14005
|
-
/* @__PURE__ */
|
|
14144
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", padding: 1, marginTop: 1, marginBottom: 1, borderStyle: "round", children: [
|
|
14145
|
+
/* @__PURE__ */ jsxs8(Text9, { children: [
|
|
14146
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, color: ACCENT_HEX, children: "?" }),
|
|
14006
14147
|
" ",
|
|
14007
14148
|
isConfirm ? "Review your answers and submit." : q?.spec.question
|
|
14008
14149
|
] }),
|
|
14009
|
-
/* @__PURE__ */
|
|
14150
|
+
/* @__PURE__ */ jsxs8(Box9, { children: [
|
|
14010
14151
|
states.map((s, i) => {
|
|
14011
14152
|
const status = isAnswered(s) ? "\u2713" : i === tab ? "\u25CF" : "\u25CB";
|
|
14012
14153
|
const text = ` ${status} ${s.spec.header} `;
|
|
14013
|
-
return /* @__PURE__ */
|
|
14154
|
+
return /* @__PURE__ */ jsxs8(Text9, { color: i === tab ? ACCENT_HEX : void 0, dimColor: i !== tab, children: [
|
|
14014
14155
|
i > 0 ? " " : "",
|
|
14015
14156
|
"[",
|
|
14016
14157
|
text,
|
|
14017
14158
|
"]"
|
|
14018
14159
|
] }, i);
|
|
14019
14160
|
}),
|
|
14020
|
-
/* @__PURE__ */
|
|
14021
|
-
|
|
14161
|
+
/* @__PURE__ */ jsxs8(
|
|
14162
|
+
Text9,
|
|
14022
14163
|
{
|
|
14023
14164
|
color: isConfirm ? ACCENT_HEX : everyAnswered ? "green" : void 0,
|
|
14024
14165
|
dimColor: !isConfirm,
|
|
@@ -14033,34 +14174,34 @@ function AskPanel({ req, onResolve }) {
|
|
|
14033
14174
|
}
|
|
14034
14175
|
)
|
|
14035
14176
|
] }),
|
|
14036
|
-
/* @__PURE__ */
|
|
14037
|
-
isConfirm ? /* @__PURE__ */
|
|
14177
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
14178
|
+
isConfirm ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
14038
14179
|
states.map((s, i) => {
|
|
14039
14180
|
const labels = answerLabels(s);
|
|
14040
14181
|
const val = labels.length > 0 ? labels.join(", ") : "(no answer)";
|
|
14041
|
-
return /* @__PURE__ */
|
|
14182
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
14042
14183
|
" ",
|
|
14043
|
-
/* @__PURE__ */
|
|
14184
|
+
/* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
|
|
14044
14185
|
s.spec.header,
|
|
14045
14186
|
":"
|
|
14046
14187
|
] }),
|
|
14047
14188
|
" ",
|
|
14048
|
-
/* @__PURE__ */
|
|
14189
|
+
/* @__PURE__ */ jsx9(Text9, { color: labels.length > 0 ? void 0 : "yellow", children: val })
|
|
14049
14190
|
] }, i);
|
|
14050
14191
|
}),
|
|
14051
|
-
/* @__PURE__ */
|
|
14192
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
14052
14193
|
[
|
|
14053
14194
|
{ idx: CONFIRM_SUBMIT, label: "Submit", color: "green" },
|
|
14054
14195
|
{ idx: CONFIRM_CANCEL, label: "Cancel", color: "red" }
|
|
14055
14196
|
].map((b) => {
|
|
14056
14197
|
const isCur = confirmIndex === b.idx;
|
|
14057
14198
|
const disabled = b.idx === CONFIRM_SUBMIT && !everyAnswered;
|
|
14058
|
-
return /* @__PURE__ */
|
|
14199
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
14059
14200
|
" ",
|
|
14060
|
-
/* @__PURE__ */
|
|
14201
|
+
/* @__PURE__ */ jsx9(Text9, { color: isCur ? ACCENT_HEX : void 0, children: isCur ? "\u276F" : " " }),
|
|
14061
14202
|
" ",
|
|
14062
|
-
/* @__PURE__ */
|
|
14063
|
-
disabled ? /* @__PURE__ */
|
|
14203
|
+
/* @__PURE__ */ jsx9(Text9, { color: isCur ? b.color : void 0, dimColor: disabled && !isCur, children: b.label }),
|
|
14204
|
+
disabled ? /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " (answer all questions first)" }) : null
|
|
14064
14205
|
] }, b.idx);
|
|
14065
14206
|
})
|
|
14066
14207
|
] }) : q?.options.map((o, i) => {
|
|
@@ -14069,34 +14210,34 @@ function AskPanel({ req, onResolve }) {
|
|
|
14069
14210
|
const marker2 = q.spec.multiSelect ? isSelected ? "[x]" : "[ ]" : isSelected ? "\u25CF" : "\u25CB";
|
|
14070
14211
|
const cur = isCur ? "\u276F" : " ";
|
|
14071
14212
|
const pad = " ".repeat(Math.max(0, labelWidth - visibleWidth(o.label)));
|
|
14072
|
-
return /* @__PURE__ */
|
|
14213
|
+
return /* @__PURE__ */ jsxs8(Text9, { children: [
|
|
14073
14214
|
" ",
|
|
14074
|
-
/* @__PURE__ */
|
|
14215
|
+
/* @__PURE__ */ jsx9(Text9, { color: isCur ? ACCENT_HEX : void 0, children: cur }),
|
|
14075
14216
|
" ",
|
|
14076
14217
|
marker2,
|
|
14077
14218
|
" ",
|
|
14078
|
-
/* @__PURE__ */
|
|
14219
|
+
/* @__PURE__ */ jsx9(Text9, { color: isCur ? ACCENT_HEX : void 0, children: o.label }),
|
|
14079
14220
|
pad,
|
|
14080
|
-
o.description ? /* @__PURE__ */
|
|
14221
|
+
o.description ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
14081
14222
|
" ",
|
|
14082
|
-
/* @__PURE__ */
|
|
14223
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: o.description })
|
|
14083
14224
|
] }) : null
|
|
14084
14225
|
] }, i);
|
|
14085
14226
|
}),
|
|
14086
|
-
phase === "freeform" ? /* @__PURE__ */
|
|
14087
|
-
/* @__PURE__ */
|
|
14088
|
-
/* @__PURE__ */
|
|
14089
|
-
/* @__PURE__ */
|
|
14090
|
-
/* @__PURE__ */
|
|
14091
|
-
/* @__PURE__ */
|
|
14092
|
-
freeformBuffer.length === 0 ? /* @__PURE__ */
|
|
14227
|
+
phase === "freeform" ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
14228
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
14229
|
+
/* @__PURE__ */ jsxs8(Box9, { children: [
|
|
14230
|
+
/* @__PURE__ */ jsx9(Text9, { color: ACCENT_HEX, children: " \u203A " }),
|
|
14231
|
+
/* @__PURE__ */ jsx9(Text9, { children: freeformBuffer }),
|
|
14232
|
+
/* @__PURE__ */ jsx9(Text9, { inverse: true, children: " " }),
|
|
14233
|
+
freeformBuffer.length === 0 ? /* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
|
|
14093
14234
|
" ",
|
|
14094
14235
|
"type your custom answer, Enter to confirm, Esc to cancel"
|
|
14095
14236
|
] }) : null
|
|
14096
14237
|
] })
|
|
14097
14238
|
] }) : null,
|
|
14098
|
-
/* @__PURE__ */
|
|
14099
|
-
/* @__PURE__ */
|
|
14239
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
14240
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: [
|
|
14100
14241
|
"\u2190/\u2192 tab",
|
|
14101
14242
|
isConfirm ? "\u2191/\u2193 button" : "\u2191/\u2193 option",
|
|
14102
14243
|
!isConfirm && q?.spec.multiSelect ? "space toggle" : "",
|
|
@@ -14108,8 +14249,8 @@ function AskPanel({ req, onResolve }) {
|
|
|
14108
14249
|
|
|
14109
14250
|
// src/ui/slider.tsx
|
|
14110
14251
|
import { useEffect as useEffect2, useState as useState5 } from "react";
|
|
14111
|
-
import { Box as
|
|
14112
|
-
import { Fragment as Fragment4, jsx as
|
|
14252
|
+
import { Box as Box10, Text as Text10, useInput as useInput5, useStdout as useStdout3 } from "ink";
|
|
14253
|
+
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
14113
14254
|
var SEP = 3;
|
|
14114
14255
|
function hslToRgb(h, s, l) {
|
|
14115
14256
|
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
@@ -14211,8 +14352,8 @@ function SliderPicker({ opts, onResolve, panelWidth }) {
|
|
|
14211
14352
|
const fullWidth = !!opts.topRuleColor;
|
|
14212
14353
|
const cols = panelWidth ?? stdout?.columns ?? 80;
|
|
14213
14354
|
const borderProps = overlayBorderProps(false, opts.topRuleColor);
|
|
14214
|
-
return /* @__PURE__ */
|
|
14215
|
-
|
|
14355
|
+
return /* @__PURE__ */ jsxs9(
|
|
14356
|
+
Box10,
|
|
14216
14357
|
{
|
|
14217
14358
|
flexDirection: "column",
|
|
14218
14359
|
marginTop: 1,
|
|
@@ -14220,15 +14361,15 @@ function SliderPicker({ opts, onResolve, panelWidth }) {
|
|
|
14220
14361
|
...fullWidth ? { width: cols } : {},
|
|
14221
14362
|
...borderProps,
|
|
14222
14363
|
children: [
|
|
14223
|
-
/* @__PURE__ */
|
|
14224
|
-
/* @__PURE__ */
|
|
14225
|
-
description ? /* @__PURE__ */
|
|
14226
|
-
/* @__PURE__ */
|
|
14227
|
-
/* @__PURE__ */
|
|
14364
|
+
/* @__PURE__ */ jsx10(Text10, { children: scaleLine }),
|
|
14365
|
+
/* @__PURE__ */ jsx10(Text10, { children: itemLine }),
|
|
14366
|
+
description ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
14367
|
+
/* @__PURE__ */ jsx10(Text10, { children: " " }),
|
|
14368
|
+
/* @__PURE__ */ jsx10(Text10, { children: dim(description) })
|
|
14228
14369
|
] }) : null,
|
|
14229
|
-
opts.footer ? /* @__PURE__ */
|
|
14230
|
-
/* @__PURE__ */
|
|
14231
|
-
/* @__PURE__ */
|
|
14370
|
+
opts.footer ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
14371
|
+
/* @__PURE__ */ jsx10(Text10, { children: " " }),
|
|
14372
|
+
/* @__PURE__ */ jsx10(Text10, { children: opts.footer })
|
|
14232
14373
|
] }) : null
|
|
14233
14374
|
]
|
|
14234
14375
|
}
|
|
@@ -14455,9 +14596,10 @@ function appendAssistantItems(items, msg, resultIndex, thinkingLabel, nextKey, t
|
|
|
14455
14596
|
|
|
14456
14597
|
// src/ui/spinner.tsx
|
|
14457
14598
|
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
14458
|
-
import { Box as
|
|
14459
|
-
import { jsx as
|
|
14460
|
-
var FRAMES = ["\
|
|
14599
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
14600
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
14601
|
+
var FRAMES = ["\xB7", "\u2726", "\u2727", "\u2736", "\u2738", "\u273A", "\u2738", "\u2736", "\u2727", "\u2726"];
|
|
14602
|
+
var ANIM_SLOWDOWN = 2;
|
|
14461
14603
|
function shimmer(text, frame, [r, g, b]) {
|
|
14462
14604
|
let out = "\x1B[1m";
|
|
14463
14605
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -14488,28 +14630,29 @@ function Spinner({ spec }) {
|
|
|
14488
14630
|
return () => clearInterval(id);
|
|
14489
14631
|
}, [tickMs]);
|
|
14490
14632
|
const elapsed = formatElapsed(Date.now() - spec.startedAt);
|
|
14491
|
-
const
|
|
14633
|
+
const phase = Math.floor(frame / ANIM_SLOWDOWN);
|
|
14634
|
+
const frameChar = FRAMES[phase % FRAMES.length] ?? "";
|
|
14492
14635
|
const upStr = spec.inputTokens != null ? ` \xB7 \u2191 ${formatTokenCount(spec.inputTokens)} tok` : "";
|
|
14493
14636
|
const downStr = spec.tokens != null ? ` \xB7 \u2193 ~${formatTokenCount(spec.tokens)} tok` : "";
|
|
14494
14637
|
const tokenStr = `${upStr}${downStr}`;
|
|
14495
14638
|
const hintStr = spec.hint ? ` \xB7 ${spec.hint}` : "";
|
|
14496
14639
|
let line;
|
|
14497
14640
|
if (canShimmer && tint) {
|
|
14498
|
-
const head = shimmer(frameChar,
|
|
14499
|
-
const word = shimmer(spec.activeWord,
|
|
14641
|
+
const head = shimmer(frameChar, phase + 1, tint);
|
|
14642
|
+
const word = shimmer(spec.activeWord, phase, tint);
|
|
14500
14643
|
line = `${head} ${word} \xB7 ${elapsed}${tokenStr}${hintStr}`;
|
|
14501
14644
|
} else {
|
|
14502
14645
|
const renderedFrame = isStatic ? frameChar : bold(colorize(frameChar));
|
|
14503
14646
|
const word = isStatic ? spec.activeWord : bold(colorize(spec.activeWord));
|
|
14504
14647
|
line = `${renderedFrame} ${word} \xB7 ${elapsed}${tokenStr}${hintStr}`;
|
|
14505
14648
|
}
|
|
14506
|
-
return /* @__PURE__ */
|
|
14649
|
+
return /* @__PURE__ */ jsx11(Box11, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(Text11, { children: line }) });
|
|
14507
14650
|
}
|
|
14508
14651
|
|
|
14509
14652
|
// src/ui/task-footer.tsx
|
|
14510
14653
|
import { useEffect as useEffect4, useState as useState7 } from "react";
|
|
14511
|
-
import { Box as
|
|
14512
|
-
import { jsx as
|
|
14654
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
14655
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
14513
14656
|
var MAX_VISIBLE = 5;
|
|
14514
14657
|
var MIN_VISIBLE_TASKS = 2;
|
|
14515
14658
|
function taskFooterVisible(tasks) {
|
|
@@ -14521,28 +14664,28 @@ var STATUS_RANK = {
|
|
|
14521
14664
|
completed: 2
|
|
14522
14665
|
};
|
|
14523
14666
|
function TaskRow({ task, isFirst }) {
|
|
14524
|
-
const prefix = isFirst ? /* @__PURE__ */
|
|
14525
|
-
const suffix = task.blockedBy.length > 0 ? /* @__PURE__ */
|
|
14667
|
+
const prefix = isFirst ? /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: " \u23BF " }) : /* @__PURE__ */ jsx12(Text12, { children: " " });
|
|
14668
|
+
const suffix = task.blockedBy.length > 0 ? /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: ` \u27F5 ${task.blockedBy.length}` }) : null;
|
|
14526
14669
|
switch (task.status) {
|
|
14527
14670
|
case "completed":
|
|
14528
|
-
return /* @__PURE__ */
|
|
14671
|
+
return /* @__PURE__ */ jsxs10(Text12, { children: [
|
|
14529
14672
|
prefix,
|
|
14530
|
-
/* @__PURE__ */
|
|
14673
|
+
/* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713" }),
|
|
14531
14674
|
" ",
|
|
14532
|
-
/* @__PURE__ */
|
|
14675
|
+
/* @__PURE__ */ jsx12(Text12, { color: "gray", strikethrough: true, children: task.description }),
|
|
14533
14676
|
suffix
|
|
14534
14677
|
] });
|
|
14535
14678
|
case "in_progress":
|
|
14536
|
-
return /* @__PURE__ */
|
|
14679
|
+
return /* @__PURE__ */ jsxs10(Text12, { children: [
|
|
14537
14680
|
prefix,
|
|
14538
|
-
/* @__PURE__ */
|
|
14681
|
+
/* @__PURE__ */ jsx12(Text12, { color: ACCENT_HEX, children: "\u25A0" }),
|
|
14539
14682
|
" ",
|
|
14540
|
-
/* @__PURE__ */
|
|
14683
|
+
/* @__PURE__ */ jsx12(Text12, { color: ACCENT_HEX, bold: true, children: task.description }),
|
|
14541
14684
|
suffix
|
|
14542
14685
|
] });
|
|
14543
14686
|
case "pending":
|
|
14544
14687
|
default:
|
|
14545
|
-
return /* @__PURE__ */
|
|
14688
|
+
return /* @__PURE__ */ jsxs10(Text12, { children: [
|
|
14546
14689
|
prefix,
|
|
14547
14690
|
"\u25A1 ",
|
|
14548
14691
|
task.description,
|
|
@@ -14554,9 +14697,9 @@ function SummaryRow({
|
|
|
14554
14697
|
hidden,
|
|
14555
14698
|
counts
|
|
14556
14699
|
}) {
|
|
14557
|
-
return /* @__PURE__ */
|
|
14700
|
+
return /* @__PURE__ */ jsxs10(Text12, { children: [
|
|
14558
14701
|
" ",
|
|
14559
|
-
/* @__PURE__ */
|
|
14702
|
+
/* @__PURE__ */ jsxs10(Text12, { dimColor: true, children: [
|
|
14560
14703
|
"... ",
|
|
14561
14704
|
hidden,
|
|
14562
14705
|
" More, ",
|
|
@@ -14605,17 +14748,17 @@ function TaskFooter({ tasks }) {
|
|
|
14605
14748
|
startedAt,
|
|
14606
14749
|
activeWord: `TASK: ${spinnerTask.description}...`
|
|
14607
14750
|
} : null;
|
|
14608
|
-
return /* @__PURE__ */
|
|
14609
|
-
spinnerSpec ? /* @__PURE__ */
|
|
14610
|
-
visible.map((t, i) => /* @__PURE__ */
|
|
14611
|
-
hidden > 0 ? /* @__PURE__ */
|
|
14751
|
+
return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
14752
|
+
spinnerSpec ? /* @__PURE__ */ jsx12(Spinner, { spec: spinnerSpec }) : null,
|
|
14753
|
+
visible.map((t, i) => /* @__PURE__ */ jsx12(TaskRow, { task: t, isFirst: i === 0 }, t.id)),
|
|
14754
|
+
hidden > 0 ? /* @__PURE__ */ jsx12(SummaryRow, { hidden, counts }) : null
|
|
14612
14755
|
] });
|
|
14613
14756
|
}
|
|
14614
14757
|
|
|
14615
14758
|
// src/ui/todo-footer.tsx
|
|
14616
14759
|
import { useEffect as useEffect5, useState as useState8 } from "react";
|
|
14617
|
-
import { Box as
|
|
14618
|
-
import { jsx as
|
|
14760
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
14761
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
14619
14762
|
var MAX_VISIBLE2 = 5;
|
|
14620
14763
|
var MIN_VISIBLE_TODOS = 2;
|
|
14621
14764
|
function todoFooterVisible(todos) {
|
|
@@ -14627,25 +14770,25 @@ var STATUS_RANK2 = {
|
|
|
14627
14770
|
completed: 2
|
|
14628
14771
|
};
|
|
14629
14772
|
function TodoRow({ todo, isFirst }) {
|
|
14630
|
-
const prefix = isFirst ? /* @__PURE__ */
|
|
14773
|
+
const prefix = isFirst ? /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: " \u23BF " }) : /* @__PURE__ */ jsx13(Text13, { children: " " });
|
|
14631
14774
|
switch (todo.status) {
|
|
14632
14775
|
case "completed":
|
|
14633
|
-
return /* @__PURE__ */
|
|
14776
|
+
return /* @__PURE__ */ jsxs11(Text13, { children: [
|
|
14634
14777
|
prefix,
|
|
14635
|
-
/* @__PURE__ */
|
|
14778
|
+
/* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
|
|
14636
14779
|
" ",
|
|
14637
|
-
/* @__PURE__ */
|
|
14780
|
+
/* @__PURE__ */ jsx13(Text13, { color: "gray", strikethrough: true, children: todo.description })
|
|
14638
14781
|
] });
|
|
14639
14782
|
case "in_progress":
|
|
14640
|
-
return /* @__PURE__ */
|
|
14783
|
+
return /* @__PURE__ */ jsxs11(Text13, { children: [
|
|
14641
14784
|
prefix,
|
|
14642
|
-
/* @__PURE__ */
|
|
14785
|
+
/* @__PURE__ */ jsx13(Text13, { color: "blue", children: "\u25A0" }),
|
|
14643
14786
|
" ",
|
|
14644
|
-
/* @__PURE__ */
|
|
14787
|
+
/* @__PURE__ */ jsx13(Text13, { color: "blue", bold: true, children: todo.description })
|
|
14645
14788
|
] });
|
|
14646
14789
|
case "pending":
|
|
14647
14790
|
default:
|
|
14648
|
-
return /* @__PURE__ */
|
|
14791
|
+
return /* @__PURE__ */ jsxs11(Text13, { children: [
|
|
14649
14792
|
prefix,
|
|
14650
14793
|
"\u25A1 ",
|
|
14651
14794
|
todo.description
|
|
@@ -14656,9 +14799,9 @@ function SummaryRow2({
|
|
|
14656
14799
|
hidden,
|
|
14657
14800
|
counts
|
|
14658
14801
|
}) {
|
|
14659
|
-
return /* @__PURE__ */
|
|
14802
|
+
return /* @__PURE__ */ jsxs11(Text13, { children: [
|
|
14660
14803
|
" ",
|
|
14661
|
-
/* @__PURE__ */
|
|
14804
|
+
/* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
|
|
14662
14805
|
"... ",
|
|
14663
14806
|
hidden,
|
|
14664
14807
|
" More, ",
|
|
@@ -14707,15 +14850,15 @@ function TodoFooter({ todos }) {
|
|
|
14707
14850
|
startedAt,
|
|
14708
14851
|
activeWord: `TODO: ${spinnerTodo.description}...`
|
|
14709
14852
|
} : null;
|
|
14710
|
-
return /* @__PURE__ */
|
|
14711
|
-
spinnerSpec ? /* @__PURE__ */
|
|
14712
|
-
visible.map((t, i) => /* @__PURE__ */
|
|
14713
|
-
hidden > 0 ? /* @__PURE__ */
|
|
14853
|
+
return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", marginTop: 1, children: [
|
|
14854
|
+
spinnerSpec ? /* @__PURE__ */ jsx13(Spinner, { spec: spinnerSpec }) : null,
|
|
14855
|
+
visible.map((t, i) => /* @__PURE__ */ jsx13(TodoRow, { todo: t, isFirst: i === 0 }, t.id)),
|
|
14856
|
+
hidden > 0 ? /* @__PURE__ */ jsx13(SummaryRow2, { hidden, counts }) : null
|
|
14714
14857
|
] });
|
|
14715
14858
|
}
|
|
14716
14859
|
|
|
14717
14860
|
// src/ui/viewport.tsx
|
|
14718
|
-
import { Fragment as Fragment5, jsx as
|
|
14861
|
+
import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
14719
14862
|
var MIN_ROWS = 3;
|
|
14720
14863
|
var H_PAD = 2;
|
|
14721
14864
|
function Viewport({ store, rows, resolveModal }) {
|
|
@@ -14829,7 +14972,7 @@ function Viewport({ store, rows, resolveModal }) {
|
|
|
14829
14972
|
}, [hasPending]);
|
|
14830
14973
|
const displayLines = hasPending && !blinkOn ? highlighted.map((l) => blinkPendingOff(l)) : highlighted;
|
|
14831
14974
|
if (inStreamModal) {
|
|
14832
|
-
const modalEl = /* @__PURE__ */
|
|
14975
|
+
const modalEl = /* @__PURE__ */ jsx14(
|
|
14833
14976
|
InStreamModal,
|
|
14834
14977
|
{
|
|
14835
14978
|
modal: inStreamModal,
|
|
@@ -14839,26 +14982,26 @@ function Viewport({ store, rows, resolveModal }) {
|
|
|
14839
14982
|
}
|
|
14840
14983
|
);
|
|
14841
14984
|
if (inStreamModal.kind === "approval") {
|
|
14842
|
-
return /* @__PURE__ */
|
|
14843
|
-
displayLines.length > 0 ? /* @__PURE__ */
|
|
14985
|
+
return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", flexGrow: 1, flexShrink: 1, overflowY: "hidden", paddingX: H_PAD, children: [
|
|
14986
|
+
displayLines.length > 0 ? /* @__PURE__ */ jsx14(Text14, { children: displayLines.join("\n") }) : null,
|
|
14844
14987
|
modalEl,
|
|
14845
|
-
/* @__PURE__ */
|
|
14988
|
+
/* @__PURE__ */ jsx14(Box14, { flexGrow: 1 })
|
|
14846
14989
|
] });
|
|
14847
14990
|
}
|
|
14848
|
-
return /* @__PURE__ */
|
|
14849
|
-
displayLines.length > 0 ? /* @__PURE__ */
|
|
14850
|
-
/* @__PURE__ */
|
|
14991
|
+
return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", flexGrow: 1, flexShrink: 1, overflowY: "hidden", paddingX: H_PAD, children: [
|
|
14992
|
+
displayLines.length > 0 ? /* @__PURE__ */ jsx14(Text14, { children: displayLines.join("\n") }) : null,
|
|
14993
|
+
/* @__PURE__ */ jsx14(Box14, { flexGrow: 1 }),
|
|
14851
14994
|
modalEl
|
|
14852
14995
|
] });
|
|
14853
14996
|
}
|
|
14854
|
-
return /* @__PURE__ */
|
|
14855
|
-
displayLines.length > 0 ? /* @__PURE__ */
|
|
14856
|
-
hasSpinner && !anyFooterVisible ? /* @__PURE__ */
|
|
14857
|
-
hasSpinner && anyFooterVisible ? /* @__PURE__ */
|
|
14858
|
-
/* @__PURE__ */
|
|
14859
|
-
/* @__PURE__ */
|
|
14997
|
+
return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", flexGrow: 1, flexShrink: 1, overflowY: "hidden", paddingX: H_PAD, children: [
|
|
14998
|
+
displayLines.length > 0 ? /* @__PURE__ */ jsx14(Text14, { children: displayLines.join("\n") }) : null,
|
|
14999
|
+
hasSpinner && !anyFooterVisible ? /* @__PURE__ */ jsx14(SpinnerWrapper, { store }) : null,
|
|
15000
|
+
hasSpinner && anyFooterVisible ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
15001
|
+
/* @__PURE__ */ jsx14(TaskFooter, { tasks }),
|
|
15002
|
+
/* @__PURE__ */ jsx14(TodoFooter, { todos })
|
|
14860
15003
|
] }) : null,
|
|
14861
|
-
/* @__PURE__ */
|
|
15004
|
+
/* @__PURE__ */ jsx14(Box14, { flexGrow: 1 })
|
|
14862
15005
|
] });
|
|
14863
15006
|
}
|
|
14864
15007
|
function SpinnerWrapper({
|
|
@@ -14866,7 +15009,7 @@ function SpinnerWrapper({
|
|
|
14866
15009
|
}) {
|
|
14867
15010
|
const spec = store((s) => s.spinner);
|
|
14868
15011
|
if (!spec) return null;
|
|
14869
|
-
return /* @__PURE__ */
|
|
15012
|
+
return /* @__PURE__ */ jsx14(Spinner, { spec });
|
|
14870
15013
|
}
|
|
14871
15014
|
function chromeRowsFor(hasSpinner, modal, todos, tasks, cols) {
|
|
14872
15015
|
if (modal) {
|
|
@@ -14941,7 +15084,7 @@ function InStreamModal({
|
|
|
14941
15084
|
}) {
|
|
14942
15085
|
switch (modal.kind) {
|
|
14943
15086
|
case "approval":
|
|
14944
|
-
return /* @__PURE__ */
|
|
15087
|
+
return /* @__PURE__ */ jsx14(
|
|
14945
15088
|
ApprovalPrompt,
|
|
14946
15089
|
{
|
|
14947
15090
|
decision: modal.decision,
|
|
@@ -14953,9 +15096,9 @@ function InStreamModal({
|
|
|
14953
15096
|
}
|
|
14954
15097
|
);
|
|
14955
15098
|
case "ask":
|
|
14956
|
-
return /* @__PURE__ */
|
|
15099
|
+
return /* @__PURE__ */ jsx14(AskPanel, { req: modal.req, onResolve: (value) => resolveModal(value) });
|
|
14957
15100
|
case "pick":
|
|
14958
|
-
return /* @__PURE__ */
|
|
15101
|
+
return /* @__PURE__ */ jsx14(
|
|
14959
15102
|
PickList,
|
|
14960
15103
|
{
|
|
14961
15104
|
opts: modal.opts,
|
|
@@ -14964,7 +15107,7 @@ function InStreamModal({
|
|
|
14964
15107
|
}
|
|
14965
15108
|
);
|
|
14966
15109
|
case "pickH":
|
|
14967
|
-
return /* @__PURE__ */
|
|
15110
|
+
return /* @__PURE__ */ jsx14(
|
|
14968
15111
|
PickHorizontal,
|
|
14969
15112
|
{
|
|
14970
15113
|
opts: modal.opts,
|
|
@@ -14973,7 +15116,7 @@ function InStreamModal({
|
|
|
14973
15116
|
}
|
|
14974
15117
|
);
|
|
14975
15118
|
case "slider":
|
|
14976
|
-
return /* @__PURE__ */
|
|
15119
|
+
return /* @__PURE__ */ jsx14(
|
|
14977
15120
|
SliderPicker,
|
|
14978
15121
|
{
|
|
14979
15122
|
opts: modal.opts,
|
|
@@ -14982,7 +15125,7 @@ function InStreamModal({
|
|
|
14982
15125
|
}
|
|
14983
15126
|
);
|
|
14984
15127
|
case "viewer":
|
|
14985
|
-
return /* @__PURE__ */
|
|
15128
|
+
return /* @__PURE__ */ jsx14(
|
|
14986
15129
|
ScrollViewer,
|
|
14987
15130
|
{
|
|
14988
15131
|
opts: modal.opts,
|
|
@@ -15005,7 +15148,7 @@ function hitTestJumpButton(row, col) {
|
|
|
15005
15148
|
}
|
|
15006
15149
|
|
|
15007
15150
|
// src/ui/app.tsx
|
|
15008
|
-
import { Fragment as Fragment6, jsx as
|
|
15151
|
+
import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
15009
15152
|
function pinnedBottomRows(inputRows, extraRows) {
|
|
15010
15153
|
return STATUS_LINE_ROWS + INPUT_TOP_SPACER_ROWS + inputRows + extraRows;
|
|
15011
15154
|
}
|
|
@@ -15036,12 +15179,13 @@ function JumpToBottomHint({
|
|
|
15036
15179
|
store.getState().setJumpButtonHovered(false);
|
|
15037
15180
|
};
|
|
15038
15181
|
}, [show, row, colStart, colEnd, store]);
|
|
15039
|
-
return /* @__PURE__ */
|
|
15182
|
+
return /* @__PURE__ */ jsx15(Box15, { flexShrink: 0, height: INPUT_TOP_SPACER_ROWS, justifyContent: "center", children: show ? /* @__PURE__ */ jsx15(Text15, { color: "black", backgroundColor: hovered ? "cyan" : "gray", children: JUMP_TO_BOTTOM_LABEL }) : null });
|
|
15040
15183
|
}
|
|
15041
15184
|
var MIN_FRAME_ROWS = 4;
|
|
15042
15185
|
function App({ store }) {
|
|
15043
15186
|
const {
|
|
15044
15187
|
setup,
|
|
15188
|
+
trust,
|
|
15045
15189
|
modal,
|
|
15046
15190
|
slashCommands,
|
|
15047
15191
|
mentionFiles,
|
|
@@ -15056,6 +15200,7 @@ function App({ store }) {
|
|
|
15056
15200
|
} = store(
|
|
15057
15201
|
useShallow3((s) => ({
|
|
15058
15202
|
setup: s.setup,
|
|
15203
|
+
trust: s.trust,
|
|
15059
15204
|
modal: s.modal,
|
|
15060
15205
|
slashCommands: s.slashCommands,
|
|
15061
15206
|
mentionFiles: s.mentionFiles,
|
|
@@ -15092,12 +15237,13 @@ function App({ store }) {
|
|
|
15092
15237
|
[store]
|
|
15093
15238
|
);
|
|
15094
15239
|
React12.useEffect(() => {
|
|
15095
|
-
if (setup || overlayModal) setCursorTarget(null);
|
|
15096
|
-
}, [setup, overlayModal]);
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15240
|
+
if (setup || trust || overlayModal) setCursorTarget(null);
|
|
15241
|
+
}, [setup, trust, overlayModal]);
|
|
15242
|
+
const fullscreenView = setup ? /* @__PURE__ */ jsx15(SetupView, { state: setup }) : trust ? /* @__PURE__ */ jsx15(TrustView, { state: trust }) : null;
|
|
15243
|
+
if (fullscreenView) {
|
|
15244
|
+
return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
|
|
15245
|
+
fullscreenView,
|
|
15246
|
+
modal?.kind === "input" ? /* @__PURE__ */ jsx15(
|
|
15101
15247
|
InputBox,
|
|
15102
15248
|
{
|
|
15103
15249
|
options: modal.opts,
|
|
@@ -15106,14 +15252,14 @@ function App({ store }) {
|
|
|
15106
15252
|
onMeasure: onMeasureInput
|
|
15107
15253
|
}
|
|
15108
15254
|
) : null,
|
|
15109
|
-
modal?.kind === "pickH" ? /* @__PURE__ */
|
|
15255
|
+
modal?.kind === "pickH" ? /* @__PURE__ */ jsx15(
|
|
15110
15256
|
PickHorizontal,
|
|
15111
15257
|
{
|
|
15112
15258
|
opts: modal.opts,
|
|
15113
15259
|
onResolve: (value) => resolveModal(value)
|
|
15114
15260
|
}
|
|
15115
15261
|
) : null,
|
|
15116
|
-
modal?.kind === "pick" ? /* @__PURE__ */
|
|
15262
|
+
modal?.kind === "pick" ? /* @__PURE__ */ jsx15(
|
|
15117
15263
|
PickList,
|
|
15118
15264
|
{
|
|
15119
15265
|
opts: modal.opts,
|
|
@@ -15126,10 +15272,10 @@ function App({ store }) {
|
|
|
15126
15272
|
const indicatorRows = modeIndicator ? 1 : 0;
|
|
15127
15273
|
const bottomChromeRows = overlayModal ? 0 : pinnedBottomRows(inputRows, indicatorRows);
|
|
15128
15274
|
const viewportRows = Math.max(3, termRows - bottomChromeRows - 1);
|
|
15129
|
-
return /* @__PURE__ */
|
|
15130
|
-
/* @__PURE__ */
|
|
15131
|
-
overlayModal ? null : /* @__PURE__ */
|
|
15132
|
-
/* @__PURE__ */
|
|
15275
|
+
return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", height: Math.max(MIN_FRAME_ROWS, termRows - 1), overflowY: "hidden", children: [
|
|
15276
|
+
/* @__PURE__ */ jsx15(Viewport, { store, rows: viewportRows, resolveModal }),
|
|
15277
|
+
overlayModal ? null : /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
15278
|
+
/* @__PURE__ */ jsx15(
|
|
15133
15279
|
JumpToBottomHint,
|
|
15134
15280
|
{
|
|
15135
15281
|
store,
|
|
@@ -15140,7 +15286,7 @@ function App({ store }) {
|
|
|
15140
15286
|
indicatorRows
|
|
15141
15287
|
}
|
|
15142
15288
|
),
|
|
15143
|
-
/* @__PURE__ */
|
|
15289
|
+
/* @__PURE__ */ jsx15(Box15, { flexShrink: 0, flexDirection: "column", children: /* @__PURE__ */ jsx15(
|
|
15144
15290
|
InputBox,
|
|
15145
15291
|
{
|
|
15146
15292
|
options: {
|
|
@@ -15166,11 +15312,11 @@ function App({ store }) {
|
|
|
15166
15312
|
onImageAttached: imagePaste?.attached
|
|
15167
15313
|
}
|
|
15168
15314
|
) }),
|
|
15169
|
-
/* @__PURE__ */
|
|
15170
|
-
/* @__PURE__ */
|
|
15171
|
-
modeIndicator ? /* @__PURE__ */
|
|
15172
|
-
/* @__PURE__ */
|
|
15173
|
-
/* @__PURE__ */
|
|
15315
|
+
/* @__PURE__ */ jsxs13(Box15, { flexShrink: 0, flexDirection: "column", children: [
|
|
15316
|
+
/* @__PURE__ */ jsx15(StatusLine, { store, shellMode }),
|
|
15317
|
+
modeIndicator ? /* @__PURE__ */ jsxs13(Box15, { children: [
|
|
15318
|
+
/* @__PURE__ */ jsx15(Text15, { color: modeIndicator.color, children: ` ${modeIndicator.label}` }),
|
|
15319
|
+
/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: ` ${PERMISSION_MODE_HINT}` })
|
|
15174
15320
|
] }) : null
|
|
15175
15321
|
] })
|
|
15176
15322
|
] })
|
|
@@ -15544,6 +15690,7 @@ function createAppStore(opts = {}) {
|
|
|
15544
15690
|
escHandler: null,
|
|
15545
15691
|
thinkingLabel: void 0,
|
|
15546
15692
|
setup: null,
|
|
15693
|
+
trust: null,
|
|
15547
15694
|
termCols: process.stdout.columns ?? 80,
|
|
15548
15695
|
termRows: process.stdout.rows ?? 24,
|
|
15549
15696
|
scrollOffset: 0,
|
|
@@ -15685,6 +15832,12 @@ function createAppStore(opts = {}) {
|
|
|
15685
15832
|
beginSetup(state) {
|
|
15686
15833
|
set({ setup: state });
|
|
15687
15834
|
},
|
|
15835
|
+
beginTrust(state) {
|
|
15836
|
+
set({ trust: state });
|
|
15837
|
+
},
|
|
15838
|
+
endTrust() {
|
|
15839
|
+
set({ trust: null });
|
|
15840
|
+
},
|
|
15688
15841
|
setSetupPrompt(prompt) {
|
|
15689
15842
|
const cur = get().setup;
|
|
15690
15843
|
if (!cur) return;
|
|
@@ -16423,6 +16576,12 @@ var Screen = class {
|
|
|
16423
16576
|
endSetup() {
|
|
16424
16577
|
this.store.getState().endSetup();
|
|
16425
16578
|
}
|
|
16579
|
+
beginTrust(state) {
|
|
16580
|
+
this.store.getState().beginTrust(state);
|
|
16581
|
+
}
|
|
16582
|
+
endTrust() {
|
|
16583
|
+
this.store.getState().endTrust();
|
|
16584
|
+
}
|
|
16426
16585
|
async promptInput(opts) {
|
|
16427
16586
|
return this.store.getState().openInputModal(opts);
|
|
16428
16587
|
}
|
|
@@ -17173,7 +17332,7 @@ async function handleDiff(ctx, args) {
|
|
|
17173
17332
|
|
|
17174
17333
|
// src/doctor.ts
|
|
17175
17334
|
import { readFile as readFile21 } from "fs/promises";
|
|
17176
|
-
import { basename as
|
|
17335
|
+
import { basename as basename5 } from "path";
|
|
17177
17336
|
import { Command } from "commander";
|
|
17178
17337
|
function zodIssues(err) {
|
|
17179
17338
|
if (err && typeof err === "object" && Array.isArray(err.issues)) {
|
|
@@ -17291,7 +17450,7 @@ async function diagnoseConfig(opts = {}) {
|
|
|
17291
17450
|
for (const e of hooks.errors) {
|
|
17292
17451
|
issues.push({
|
|
17293
17452
|
level: "warn",
|
|
17294
|
-
title: `invalid hook file: ${
|
|
17453
|
+
title: `invalid hook file: ${basename5(e.source)}`,
|
|
17295
17454
|
detail: e.message,
|
|
17296
17455
|
hint: "fix or remove it \u2014 nova skips it and continues"
|
|
17297
17456
|
});
|
|
@@ -18323,8 +18482,17 @@ async function handleResume(ctx, arg) {
|
|
|
18323
18482
|
}
|
|
18324
18483
|
|
|
18325
18484
|
// src/commands/review.ts
|
|
18485
|
+
var DEFAULT_FOCUS = "correctness and regressions in adjacent code";
|
|
18486
|
+
var PR_REF_RE = /^(?:#?(\d+)|https?:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/(\d+))\b\s*/;
|
|
18326
18487
|
function handleReview(args) {
|
|
18327
|
-
const
|
|
18488
|
+
const trimmed = args.trim();
|
|
18489
|
+
const pr = trimmed.match(PR_REF_RE);
|
|
18490
|
+
if (pr) {
|
|
18491
|
+
const number = pr[1] ?? pr[2] ?? "";
|
|
18492
|
+
const focus2 = trimmed.slice(pr[0].length).trim() || DEFAULT_FOCUS;
|
|
18493
|
+
return { kind: "prompt", text: prReviewText(number, focus2) };
|
|
18494
|
+
}
|
|
18495
|
+
const focus = trimmed || DEFAULT_FOCUS;
|
|
18328
18496
|
const text = `Review the repository's uncommitted changes. Do NOT edit anything \u2014 this is a review only.
|
|
18329
18497
|
|
|
18330
18498
|
1. Run \`git status\` and \`git diff\` (include \`git diff --staged\`) to see every pending change.
|
|
@@ -18334,6 +18502,15 @@ function handleReview(args) {
|
|
|
18334
18502
|
Write your review in the same language and script as this request.`;
|
|
18335
18503
|
return { kind: "prompt", text };
|
|
18336
18504
|
}
|
|
18505
|
+
function prReviewText(pr, focus) {
|
|
18506
|
+
return `Review GitHub pull request #${pr}. Do NOT edit anything and do NOT post any comment to GitHub \u2014 this is a read-only review.
|
|
18507
|
+
|
|
18508
|
+
1. Fetch it with \`gh pr view ${pr}\` (title, description, checks) and \`gh pr diff ${pr}\` (the full diff). If \`gh\` is missing or not authenticated, say so and stop.
|
|
18509
|
+
2. Review the diff with a focus on ${focus}. Flag anything risky, surprising, broken, or inconsistent with the surrounding code and conventions. Note missing tests or edge cases where they matter.
|
|
18510
|
+
3. Be concrete \u2014 reference file paths and line numbers, group findings by severity, and skip praise and restating the obvious. If the diff looks clean, say so plainly.
|
|
18511
|
+
|
|
18512
|
+
Write your review in the same language and script as this request.`;
|
|
18513
|
+
}
|
|
18337
18514
|
|
|
18338
18515
|
// src/commands/rewind.ts
|
|
18339
18516
|
import { relative as relative4 } from "path";
|
|
@@ -18570,8 +18747,7 @@ async function handleSkills(ctx) {
|
|
|
18570
18747
|
topRuleColor: PURPLE_HEX,
|
|
18571
18748
|
render: (s, selected) => {
|
|
18572
18749
|
const name = s.name.padEnd(nameWidth, " ");
|
|
18573
|
-
|
|
18574
|
-
return `${pickerArrow(selected)} ${name} ${s.description}${trig}`;
|
|
18750
|
+
return `${pickerArrow(selected)} ${name} ${s.description}`;
|
|
18575
18751
|
}
|
|
18576
18752
|
});
|
|
18577
18753
|
}
|
|
@@ -18936,8 +19112,8 @@ function registerBuiltinSlashCommands(ctx) {
|
|
|
18936
19112
|
});
|
|
18937
19113
|
ctx.registry.register({
|
|
18938
19114
|
name: "review",
|
|
18939
|
-
description: "review the
|
|
18940
|
-
argHint: "[focus\u2026]",
|
|
19115
|
+
description: "review the uncommitted diff, or a GitHub PR by number (read-only)",
|
|
19116
|
+
argHint: "[PR# | focus\u2026]",
|
|
18941
19117
|
source: { kind: "builtin" },
|
|
18942
19118
|
run: (_c, args) => handleReview(args)
|
|
18943
19119
|
});
|
|
@@ -19170,7 +19346,8 @@ async function createContext(settings, screen, cliOpts) {
|
|
|
19170
19346
|
logger
|
|
19171
19347
|
} : void 0;
|
|
19172
19348
|
const skillItems = skillsOpts ? getSkillList(skillsOpts) : [];
|
|
19173
|
-
const
|
|
19349
|
+
const modelSkillItems = skillItems.filter((s) => !s.disableModelInvocation);
|
|
19350
|
+
const skillsBlock = skillsOpts ? renderSkillsBlock(modelSkillItems, settings.skills.maxIndexBytes) : "";
|
|
19174
19351
|
if (skillsOpts) {
|
|
19175
19352
|
await transcript.append({
|
|
19176
19353
|
kind: "skills_loaded",
|
|
@@ -21537,6 +21714,68 @@ function buildUpgradeCommand() {
|
|
|
21537
21714
|
});
|
|
21538
21715
|
}
|
|
21539
21716
|
|
|
21717
|
+
// src/workspace-trust.ts
|
|
21718
|
+
import { homedir as homedir17 } from "os";
|
|
21719
|
+
async function isWorkspaceTrusted(settings, workspace) {
|
|
21720
|
+
if (!settings.trust.enabled) return true;
|
|
21721
|
+
const wsCanon = await canonicalizePath(workspace, ".");
|
|
21722
|
+
for (const root of settings.trust.trustedRoots) {
|
|
21723
|
+
const rootCanon = await canonicalizePath(workspace, root);
|
|
21724
|
+
if (isWithin(rootCanon, wsCanon)) return true;
|
|
21725
|
+
}
|
|
21726
|
+
return false;
|
|
21727
|
+
}
|
|
21728
|
+
async function trustWorkspace(settings, workspace, configPath) {
|
|
21729
|
+
const wsCanon = await canonicalizePath(workspace, ".");
|
|
21730
|
+
const roots = settings.trust.trustedRoots.includes(wsCanon) ? settings.trust.trustedRoots : [...settings.trust.trustedRoots, wsCanon];
|
|
21731
|
+
settings.trust = { ...settings.trust, trustedRoots: roots };
|
|
21732
|
+
const home = await canonicalizePath(homedir17(), ".");
|
|
21733
|
+
if (wsCanon === home) return;
|
|
21734
|
+
await saveSettings({ trust: settings.trust }, configPath);
|
|
21735
|
+
}
|
|
21736
|
+
async function ensureWorkspaceTrust(settings, screen, workspace, configPath) {
|
|
21737
|
+
if (await isWorkspaceTrusted(settings, workspace)) return;
|
|
21738
|
+
const wsCanon = await canonicalizePath(workspace, ".");
|
|
21739
|
+
screen.beginTrust({
|
|
21740
|
+
version: await readCliVersion(),
|
|
21741
|
+
workspace: wsCanon,
|
|
21742
|
+
lines: [
|
|
21743
|
+
"nova has not been granted access to this folder yet. Granting access",
|
|
21744
|
+
"lets it read and edit files here (and in subdirectories) without",
|
|
21745
|
+
"confirming each time, and runs any project hooks the folder defines.",
|
|
21746
|
+
"Only trust folders you recognize \u2014 declining exits without touching",
|
|
21747
|
+
"anything."
|
|
21748
|
+
]
|
|
21749
|
+
});
|
|
21750
|
+
try {
|
|
21751
|
+
const choice = await screen.pickOne({
|
|
21752
|
+
items: [{ trust: true }, { trust: false }],
|
|
21753
|
+
footer: dim("\u2191/\u2193 choose \xB7 Enter confirm \xB7 Ctrl+C to exit"),
|
|
21754
|
+
border: false,
|
|
21755
|
+
topRuleColor: PURPLE_HEX,
|
|
21756
|
+
render: (it, selected) => {
|
|
21757
|
+
const label = it.trust ? "Yes, trust this folder" : "No, exit";
|
|
21758
|
+
return `${pickerArrow(selected)} ${label}`;
|
|
21759
|
+
}
|
|
21760
|
+
});
|
|
21761
|
+
if (choice === null || !choice.trust) {
|
|
21762
|
+
await fatalExit(screen, `workspace not trusted \u2014 exiting.
|
|
21763
|
+
${wsCanon}`, 1);
|
|
21764
|
+
}
|
|
21765
|
+
try {
|
|
21766
|
+
await trustWorkspace(settings, workspace, configPath);
|
|
21767
|
+
} catch (err) {
|
|
21768
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21769
|
+
screen.card(`could not persist workspace trust: ${msg}`, {
|
|
21770
|
+
kind: "warn",
|
|
21771
|
+
title: "workspace trust"
|
|
21772
|
+
});
|
|
21773
|
+
}
|
|
21774
|
+
} finally {
|
|
21775
|
+
screen.endTrust();
|
|
21776
|
+
}
|
|
21777
|
+
}
|
|
21778
|
+
|
|
21540
21779
|
// src/index.ts
|
|
21541
21780
|
function applyCliOverrides(settings, opts) {
|
|
21542
21781
|
if (opts.model) {
|
|
@@ -21610,6 +21849,14 @@ async function runHeadlessMode(settings, positional, opts) {
|
|
|
21610
21849
|
dieHeadless(err instanceof Error ? err.message : String(err), 2);
|
|
21611
21850
|
}
|
|
21612
21851
|
const approvalPolicy = opts.dangerouslySkipPermissions ? "allow" : "deny";
|
|
21852
|
+
const headlessCwd = opts.cwd ?? process.cwd();
|
|
21853
|
+
if (!opts.dangerouslySkipPermissions && !await isWorkspaceTrusted(settings, headlessCwd)) {
|
|
21854
|
+
dieHeadless(
|
|
21855
|
+
`workspace not trusted: ${headlessCwd}
|
|
21856
|
+
Trust it by running nova interactively here once, by adding it to trust.trustedRoots in nova.config.json, or by passing --dangerously-skip-permissions.`,
|
|
21857
|
+
1
|
|
21858
|
+
);
|
|
21859
|
+
}
|
|
21613
21860
|
let code;
|
|
21614
21861
|
try {
|
|
21615
21862
|
code = await runHeadless(settings, {
|
|
@@ -21680,6 +21927,9 @@ ${formatIssues(report)}
|
|
|
21680
21927
|
"apiKey is not set in nova.config.json (or equivalent settings file)."
|
|
21681
21928
|
);
|
|
21682
21929
|
}
|
|
21930
|
+
if (!opts.dangerouslySkipPermissions) {
|
|
21931
|
+
await ensureWorkspaceTrust(settings, screen, opts.cwd ?? process.cwd());
|
|
21932
|
+
}
|
|
21683
21933
|
const ctx = await createContext(settings, screen, {
|
|
21684
21934
|
...opts.cwd !== void 0 ? { cwd: opts.cwd } : {},
|
|
21685
21935
|
...opts.resume !== void 0 ? { resume: opts.resume } : {},
|