@inteeka/task-cli 0.2.30 → 0.2.31
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/dist/cli.js +323 -36
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
+
// ../../packages/constants/src/tickets.ts
|
|
7
|
+
var TERMINAL_STATUSES = ["done", "duplicate_archive", "not_required_archive"];
|
|
8
|
+
var RESOLVED_TICKET_STATUSES = [
|
|
9
|
+
"complete",
|
|
10
|
+
"done",
|
|
11
|
+
"duplicate_archive",
|
|
12
|
+
"not_required_archive"
|
|
13
|
+
];
|
|
14
|
+
var RESOLVED_TICKET_STATUSES_FILTER = `(${RESOLVED_TICKET_STATUSES.join(",")})`;
|
|
15
|
+
var TERMINAL_STATUSES_FILTER = `(${TERMINAL_STATUSES.join(",")})`;
|
|
16
|
+
|
|
6
17
|
// ../../packages/constants/src/plans.ts
|
|
7
18
|
var PLAN_LIMITS = {
|
|
8
19
|
free: {
|
|
@@ -1612,11 +1623,74 @@ ${args.repoOverviewBlock}
|
|
|
1612
1623
|
${args.ticketSystemPrompt}${overview}`;
|
|
1613
1624
|
}
|
|
1614
1625
|
|
|
1626
|
+
// src/agent/stream-render.ts
|
|
1627
|
+
function truncate(value, max) {
|
|
1628
|
+
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
1629
|
+
return oneLine.length > max ? `${oneLine.slice(0, max - 1)}\u2026` : oneLine;
|
|
1630
|
+
}
|
|
1631
|
+
function formatToolUse(block) {
|
|
1632
|
+
const name = typeof block["name"] === "string" ? block["name"] : "tool";
|
|
1633
|
+
const input = typeof block["input"] === "object" && block["input"] !== null ? block["input"] : {};
|
|
1634
|
+
const filePath = typeof input["file_path"] === "string" ? input["file_path"] : null;
|
|
1635
|
+
const command = typeof input["command"] === "string" ? input["command"] : null;
|
|
1636
|
+
const pattern = typeof input["pattern"] === "string" ? input["pattern"] : null;
|
|
1637
|
+
if (name === "Bash" && command) return `Bash: ${truncate(command, 120)}`;
|
|
1638
|
+
if (filePath && (name === "Edit" || name === "Write" || name === "Read" || name === "MultiEdit")) {
|
|
1639
|
+
return `${name} ${filePath}`;
|
|
1640
|
+
}
|
|
1641
|
+
if (pattern && (name === "Grep" || name === "Glob")) return `${name} ${truncate(pattern, 80)}`;
|
|
1642
|
+
return name;
|
|
1643
|
+
}
|
|
1644
|
+
function summariseStreamLine(line) {
|
|
1645
|
+
const trimmed = line.trim();
|
|
1646
|
+
if (trimmed.length === 0) return { display: null, resultError: null };
|
|
1647
|
+
let evt;
|
|
1648
|
+
try {
|
|
1649
|
+
evt = JSON.parse(trimmed);
|
|
1650
|
+
} catch {
|
|
1651
|
+
return { display: null, resultError: null };
|
|
1652
|
+
}
|
|
1653
|
+
if (typeof evt !== "object" || evt === null) return { display: null, resultError: null };
|
|
1654
|
+
const e = evt;
|
|
1655
|
+
if (e["type"] === "assistant") {
|
|
1656
|
+
const message = typeof e["message"] === "object" && e["message"] !== null ? e["message"] : {};
|
|
1657
|
+
const content = message["content"];
|
|
1658
|
+
if (!Array.isArray(content)) return { display: null, resultError: null };
|
|
1659
|
+
const parts = [];
|
|
1660
|
+
for (const block of content) {
|
|
1661
|
+
if (typeof block !== "object" || block === null) continue;
|
|
1662
|
+
const b = block;
|
|
1663
|
+
if (b["type"] === "text" && typeof b["text"] === "string") {
|
|
1664
|
+
const text = b["text"].trim();
|
|
1665
|
+
if (text.length > 0) parts.push(text);
|
|
1666
|
+
} else if (b["type"] === "tool_use") {
|
|
1667
|
+
parts.push(c.cyan(` \u2192 ${formatToolUse(b)}`));
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
return { display: parts.length > 0 ? parts.join("\n") : null, resultError: null };
|
|
1671
|
+
}
|
|
1672
|
+
if (e["type"] === "result") {
|
|
1673
|
+
if (e["is_error"] === true) {
|
|
1674
|
+
const detail = typeof e["result"] === "string" && e["result"].trim().length > 0 ? e["result"].trim() : typeof e["subtype"] === "string" ? e["subtype"] : "unknown error";
|
|
1675
|
+
return {
|
|
1676
|
+
display: c.err(` \u2717 agent run errored: ${truncate(detail, 240)}`),
|
|
1677
|
+
resultError: detail
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
return { display: c.dim(" agent run complete"), resultError: null };
|
|
1681
|
+
}
|
|
1682
|
+
return { display: null, resultError: null };
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1615
1685
|
// src/agent/agent-service.ts
|
|
1616
1686
|
async function runAgent(args) {
|
|
1617
1687
|
const systemPrompt = buildSystemPrompt(args);
|
|
1618
1688
|
const claude = args.claudePath ?? "claude";
|
|
1619
1689
|
const cliArgs = [
|
|
1690
|
+
"--print",
|
|
1691
|
+
"--verbose",
|
|
1692
|
+
"--output-format",
|
|
1693
|
+
"stream-json",
|
|
1620
1694
|
"--allowedTools",
|
|
1621
1695
|
allowedToolsFlag(),
|
|
1622
1696
|
"--system-prompt",
|
|
@@ -1646,11 +1720,27 @@ async function runAgent(args) {
|
|
|
1646
1720
|
logHandle?.end();
|
|
1647
1721
|
reject(err);
|
|
1648
1722
|
});
|
|
1723
|
+
let stdoutBuffer = "";
|
|
1724
|
+
const renderLine = (line) => {
|
|
1725
|
+
const summary = summariseStreamLine(line);
|
|
1726
|
+
if (summary.display !== null) process.stdout.write(`${summary.display}
|
|
1727
|
+
`);
|
|
1728
|
+
if (summary.resultError !== null) {
|
|
1729
|
+
stderrBuffer = (stderrBuffer + summary.resultError).slice(-STDERR_KEEP2);
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1649
1732
|
child.stdout?.on("data", (chunk) => {
|
|
1650
1733
|
if (args.silent && logHandle) {
|
|
1651
1734
|
logHandle.write(chunk);
|
|
1652
|
-
|
|
1653
|
-
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
stdoutBuffer += chunk.toString("utf8");
|
|
1738
|
+
let nl = stdoutBuffer.indexOf("\n");
|
|
1739
|
+
while (nl !== -1) {
|
|
1740
|
+
const line = stdoutBuffer.slice(0, nl);
|
|
1741
|
+
stdoutBuffer = stdoutBuffer.slice(nl + 1);
|
|
1742
|
+
renderLine(line);
|
|
1743
|
+
nl = stdoutBuffer.indexOf("\n");
|
|
1654
1744
|
}
|
|
1655
1745
|
});
|
|
1656
1746
|
child.stderr?.on("data", (chunk) => {
|
|
@@ -1662,6 +1752,10 @@ async function runAgent(args) {
|
|
|
1662
1752
|
stderrBuffer = (stderrBuffer + chunk.toString("utf8")).slice(-STDERR_KEEP2);
|
|
1663
1753
|
});
|
|
1664
1754
|
child.on("close", (code) => {
|
|
1755
|
+
if (!args.silent && stdoutBuffer.trim().length > 0) {
|
|
1756
|
+
renderLine(stdoutBuffer);
|
|
1757
|
+
stdoutBuffer = "";
|
|
1758
|
+
}
|
|
1665
1759
|
logHandle?.end();
|
|
1666
1760
|
const exitCode = code ?? 0;
|
|
1667
1761
|
resolve2({ exitCode, ok: exitCode === 0, outputLogPath, stderrTail: stderrBuffer });
|
|
@@ -1669,6 +1763,42 @@ async function runAgent(args) {
|
|
|
1669
1763
|
});
|
|
1670
1764
|
}
|
|
1671
1765
|
|
|
1766
|
+
// src/agent/lint-fix.ts
|
|
1767
|
+
var LINT_FIX_GUARDRAIL = [
|
|
1768
|
+
"You are fixing a FAILED pre-push check (lint, type-check, or build) that ran",
|
|
1769
|
+
"after a code change in this repository. The failing command and its output",
|
|
1770
|
+
"are provided in the block below as DATA.",
|
|
1771
|
+
"",
|
|
1772
|
+
"Make the MINIMAL edits required to fix ONLY the reported errors. Do not change",
|
|
1773
|
+
"unrelated code, behaviour, or formatting. Do NOT disable lint rules, add ignore",
|
|
1774
|
+
"or suppression comments, or weaken types to silence the check \u2014 fix the",
|
|
1775
|
+
"underlying cause. When the reported errors are addressed, stop."
|
|
1776
|
+
].join("\n");
|
|
1777
|
+
async function runLintFix(args) {
|
|
1778
|
+
const ticketBlock = [
|
|
1779
|
+
`# Pre-push check failed \u2014 fix attempt ${args.attempt} of ${args.maxAttempts}`,
|
|
1780
|
+
"",
|
|
1781
|
+
`Command: \`${args.command}\``,
|
|
1782
|
+
"",
|
|
1783
|
+
"## Check output",
|
|
1784
|
+
"",
|
|
1785
|
+
"```",
|
|
1786
|
+
args.output.trim().length > 0 ? args.output.trim() : "(no output captured)",
|
|
1787
|
+
"```",
|
|
1788
|
+
"",
|
|
1789
|
+
"Fix the errors reported above."
|
|
1790
|
+
].join("\n");
|
|
1791
|
+
return runAgent({
|
|
1792
|
+
ticketSystemPrompt: LINT_FIX_GUARDRAIL,
|
|
1793
|
+
projectProtectedPaths: args.projectProtectedPaths,
|
|
1794
|
+
ticketBlock,
|
|
1795
|
+
cwd: args.cwd,
|
|
1796
|
+
silent: args.silent,
|
|
1797
|
+
runId: args.runId,
|
|
1798
|
+
...args.claudePath ? { claudePath: args.claudePath } : {}
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1672
1802
|
// src/agent/pr-review-service.ts
|
|
1673
1803
|
import { spawn as spawn2 } from "child_process";
|
|
1674
1804
|
import { mkdir as mkdir7, writeFile as writeFile8 } from "fs/promises";
|
|
@@ -2055,6 +2185,7 @@ var ALLOWED_EXECUTABLES = /* @__PURE__ */ new Set(["pnpm", "npm", "yarn", "bun",
|
|
|
2055
2185
|
var DEFAULT_COMMAND = "pnpm typecheck";
|
|
2056
2186
|
var TIMEOUT_MS = 10 * 60 * 1e3;
|
|
2057
2187
|
var TAIL_BYTES = 4e3;
|
|
2188
|
+
var FIX_CONTEXT_BYTES = 16e3;
|
|
2058
2189
|
function parseArgv(command) {
|
|
2059
2190
|
return command.trim().split(/\s+/).filter((s) => s.length > 0);
|
|
2060
2191
|
}
|
|
@@ -2088,25 +2219,31 @@ async function runProjectTest(args) {
|
|
|
2088
2219
|
let buf = "";
|
|
2089
2220
|
const append = (chunk) => {
|
|
2090
2221
|
buf += chunk.toString("utf8");
|
|
2091
|
-
if (buf.length >
|
|
2092
|
-
buf = buf.slice(-
|
|
2222
|
+
if (buf.length > FIX_CONTEXT_BYTES * 2) {
|
|
2223
|
+
buf = buf.slice(-FIX_CONTEXT_BYTES);
|
|
2093
2224
|
}
|
|
2094
2225
|
};
|
|
2095
|
-
child.stdout?.on("data",
|
|
2096
|
-
|
|
2226
|
+
child.stdout?.on("data", (chunk) => {
|
|
2227
|
+
append(chunk);
|
|
2228
|
+
if (!args.silent) process.stdout.write(chunk);
|
|
2229
|
+
});
|
|
2230
|
+
child.stderr?.on("data", (chunk) => {
|
|
2231
|
+
append(chunk);
|
|
2232
|
+
if (!args.silent) process.stderr.write(chunk);
|
|
2233
|
+
});
|
|
2097
2234
|
const timeoutHandle = setTimeout(() => {
|
|
2098
2235
|
child.kill("SIGKILL");
|
|
2099
2236
|
}, TIMEOUT_MS);
|
|
2100
2237
|
child.on("close", (code) => {
|
|
2101
2238
|
clearTimeout(timeoutHandle);
|
|
2102
2239
|
const durationMs = Date.now() - startedAt;
|
|
2103
|
-
const tail = buf.slice(-TAIL_BYTES);
|
|
2104
2240
|
resolve2({
|
|
2105
2241
|
ok: code === 0,
|
|
2106
2242
|
exitCode: code,
|
|
2107
2243
|
durationMs,
|
|
2108
2244
|
command,
|
|
2109
|
-
tail
|
|
2245
|
+
tail: buf.slice(-TAIL_BYTES),
|
|
2246
|
+
fixContext: buf.slice(-FIX_CONTEXT_BYTES)
|
|
2110
2247
|
});
|
|
2111
2248
|
});
|
|
2112
2249
|
child.on("error", () => {
|
|
@@ -2116,7 +2253,8 @@ async function runProjectTest(args) {
|
|
|
2116
2253
|
exitCode: null,
|
|
2117
2254
|
durationMs: Date.now() - startedAt,
|
|
2118
2255
|
command,
|
|
2119
|
-
tail: buf.slice(-TAIL_BYTES)
|
|
2256
|
+
tail: buf.slice(-TAIL_BYTES),
|
|
2257
|
+
fixContext: buf.slice(-FIX_CONTEXT_BYTES)
|
|
2120
2258
|
});
|
|
2121
2259
|
});
|
|
2122
2260
|
});
|
|
@@ -2349,6 +2487,9 @@ function registerWork(program2) {
|
|
|
2349
2487
|
).option(
|
|
2350
2488
|
"--no-auto-review",
|
|
2351
2489
|
"Skip the post-PR /qa + /security auto-review. The PR is left open for a human to review and merge."
|
|
2490
|
+
).option(
|
|
2491
|
+
"--no-fix-lint",
|
|
2492
|
+
"Disable the auto-fix loop for the pre-push check \u2014 fail immediately on lint/type errors (legacy behaviour)."
|
|
2352
2493
|
).option(
|
|
2353
2494
|
"--base-branch <name>",
|
|
2354
2495
|
"Override the base branch for this run. Wins over the project's configured cli_base_branch and the git auto-detect fallback. Typically supplied by the dashboard listener on each spawn."
|
|
@@ -2725,10 +2866,77 @@ ${installResult.tail}`.slice(
|
|
|
2725
2866
|
)
|
|
2726
2867
|
);
|
|
2727
2868
|
}
|
|
2869
|
+
const MAX_FIX_ATTEMPTS = 2;
|
|
2870
|
+
const fixEnabled = opts.fixLint !== false;
|
|
2728
2871
|
if (!silent)
|
|
2729
|
-
process.stdout.write(c.dim(` running pre-push
|
|
2872
|
+
process.stdout.write(c.dim(` running pre-push check: ${testCommand ?? "pnpm typecheck"}
|
|
2730
2873
|
`));
|
|
2731
|
-
|
|
2874
|
+
let testResult = await runProjectTest({ cwd, command: testCommand, silent });
|
|
2875
|
+
let fixAttempts = 0;
|
|
2876
|
+
while (!testResult.ok && fixEnabled && fixAttempts < MAX_FIX_ATTEMPTS) {
|
|
2877
|
+
fixAttempts += 1;
|
|
2878
|
+
if (!silent)
|
|
2879
|
+
process.stdout.write(
|
|
2880
|
+
c.warn(
|
|
2881
|
+
` \u2717 pre-push check failed (exit ${testResult.exitCode}) \u2014 attempting fix ${fixAttempts}/${MAX_FIX_ATTEMPTS}
|
|
2882
|
+
`
|
|
2883
|
+
)
|
|
2884
|
+
);
|
|
2885
|
+
const fixResult = await runLintFix({
|
|
2886
|
+
command: testResult.command,
|
|
2887
|
+
output: testResult.fixContext,
|
|
2888
|
+
attempt: fixAttempts,
|
|
2889
|
+
maxAttempts: MAX_FIX_ATTEMPTS,
|
|
2890
|
+
projectProtectedPaths: detail.project_protected_paths,
|
|
2891
|
+
cwd,
|
|
2892
|
+
silent,
|
|
2893
|
+
runId,
|
|
2894
|
+
...ctx.localCfg.claude_path ? { claudePath: ctx.localCfg.claude_path } : {}
|
|
2895
|
+
}).catch((err) => {
|
|
2896
|
+
if (!silent) process.stdout.write(c.dim(` lint-fix agent could not run: ${err.message}
|
|
2897
|
+
`));
|
|
2898
|
+
return null;
|
|
2899
|
+
});
|
|
2900
|
+
if (!fixResult || !fixResult.ok) break;
|
|
2901
|
+
const fixGuardrail = checkDiff({
|
|
2902
|
+
cwd,
|
|
2903
|
+
projectProtectedPaths: detail.project_protected_paths
|
|
2904
|
+
});
|
|
2905
|
+
if (fixGuardrail.violation) {
|
|
2906
|
+
discardWorkingTreeChanges(cwd);
|
|
2907
|
+
try {
|
|
2908
|
+
checkoutBranch(cwd, baseBranch);
|
|
2909
|
+
} catch {
|
|
2910
|
+
}
|
|
2911
|
+
deleteLocalBranch(cwd, branchName);
|
|
2912
|
+
if (!silent) {
|
|
2913
|
+
process.stdout.write(
|
|
2914
|
+
`${c.err("\u2717 Guardrail blocked")} \u2014 lint-fix agent attempted to modify protected files:
|
|
2915
|
+
`
|
|
2916
|
+
);
|
|
2917
|
+
for (const p of fixGuardrail.offendingPaths) {
|
|
2918
|
+
process.stdout.write(` - ${p}
|
|
2919
|
+
`);
|
|
2920
|
+
}
|
|
2921
|
+
process.stdout.write(c.dim(" Working tree restored. Branch deleted.\n"));
|
|
2922
|
+
}
|
|
2923
|
+
await apiCall("POST", "/api/v1/cli/me/runs", {
|
|
2924
|
+
body: {
|
|
2925
|
+
ticket_id: detail.id,
|
|
2926
|
+
schedule_id: opts.scheduleId,
|
|
2927
|
+
event: "guardrail_blocked",
|
|
2928
|
+
claude_session_id: runId,
|
|
2929
|
+
offending_paths: fixGuardrail.offendingPaths
|
|
2930
|
+
}
|
|
2931
|
+
});
|
|
2932
|
+
throw new CliError(
|
|
2933
|
+
CLI_EXIT_CODES.GUARDRAIL_BLOCKED,
|
|
2934
|
+
`Lint-fix agent attempted to modify ${fixGuardrail.offendingPaths.length} protected file(s)`
|
|
2935
|
+
);
|
|
2936
|
+
}
|
|
2937
|
+
if (!silent) process.stdout.write(c.dim(" re-running pre-push check\u2026\n"));
|
|
2938
|
+
testResult = await runProjectTest({ cwd, command: testCommand, silent });
|
|
2939
|
+
}
|
|
2732
2940
|
if (!testResult.ok) {
|
|
2733
2941
|
discardWorkingTreeChanges(cwd);
|
|
2734
2942
|
try {
|
|
@@ -2748,16 +2956,21 @@ ${installResult.tail}`.slice(
|
|
|
2748
2956
|
});
|
|
2749
2957
|
if (!silent) {
|
|
2750
2958
|
process.stdout.write(
|
|
2751
|
-
`${c.err("\u2717 Pre-push
|
|
2959
|
+
`${c.err("\u2717 Pre-push check failed")} (exit ${testResult.exitCode})` + (fixAttempts > 0 ? ` after ${fixAttempts} fix attempt${fixAttempts === 1 ? "" : "s"}` : "") + ` \u2014 branch deleted, no push.
|
|
2752
2960
|
`
|
|
2753
2961
|
);
|
|
2754
|
-
if (testResult.tail.trim().length > 0) {
|
|
2755
|
-
process.stdout.write(c.dim(testResult.tail.slice(-1e3) + "\n"));
|
|
2756
|
-
}
|
|
2757
2962
|
}
|
|
2758
2963
|
throw new CliError(
|
|
2759
2964
|
CLI_EXIT_CODES.GENERIC_ERROR,
|
|
2760
|
-
`Pre-push
|
|
2965
|
+
`Pre-push check failed: ${testResult.command} (exit ${testResult.exitCode})`
|
|
2966
|
+
);
|
|
2967
|
+
}
|
|
2968
|
+
if (fixAttempts > 0 && !silent) {
|
|
2969
|
+
process.stdout.write(
|
|
2970
|
+
c.dim(
|
|
2971
|
+
` pre-push check passed after ${fixAttempts} fix attempt${fixAttempts === 1 ? "" : "s"}
|
|
2972
|
+
`
|
|
2973
|
+
)
|
|
2761
2974
|
);
|
|
2762
2975
|
}
|
|
2763
2976
|
await apiCall("POST", "/api/v1/cli/me/runs", {
|
|
@@ -3204,6 +3417,9 @@ function registerMultiWork(program2) {
|
|
|
3204
3417
|
).option("--confirm", "Confirm --reset in non-TTY (silent / scheduled-task) contexts.").option(
|
|
3205
3418
|
"--base-branch <name>",
|
|
3206
3419
|
"Override the base branch for the whole batch. Wins over the project's configured cli_base_branch and the git auto-detect fallback."
|
|
3420
|
+
).option(
|
|
3421
|
+
"--no-fix-lint",
|
|
3422
|
+
"Disable the pre-push check auto-fix loop \u2014 fail immediately on lint/type errors (legacy behaviour)."
|
|
3207
3423
|
).option("--schedule-id <id>", "Internal: schedule id when invoked from a scheduled task").action(async (opts) => {
|
|
3208
3424
|
await runMultiWork(opts);
|
|
3209
3425
|
});
|
|
@@ -3225,6 +3441,7 @@ async function runMultiWork(opts) {
|
|
|
3225
3441
|
scheduleId: opts.scheduleId,
|
|
3226
3442
|
reset: opts.reset,
|
|
3227
3443
|
confirm: opts.confirm,
|
|
3444
|
+
fixLint: opts.fixLint,
|
|
3228
3445
|
...opts.baseBranch ? { baseBranch: opts.baseBranch } : {}
|
|
3229
3446
|
};
|
|
3230
3447
|
const results = [];
|
|
@@ -4726,6 +4943,9 @@ function registerFastTrack(program2) {
|
|
|
4726
4943
|
).option("--confirm", "Confirm --reset in non-TTY (silent / scheduled-task) contexts").option(
|
|
4727
4944
|
"--no-auto-review",
|
|
4728
4945
|
"Skip the post-PR /qa + /security auto-review. Each PR is left open for a human to review and merge."
|
|
4946
|
+
).option(
|
|
4947
|
+
"--no-fix-lint",
|
|
4948
|
+
"Disable the pre-push check auto-fix loop \u2014 fail immediately on lint/type errors (legacy behaviour)."
|
|
4729
4949
|
).option(
|
|
4730
4950
|
"--base-branch <name>",
|
|
4731
4951
|
"Override the base branch for this run. Wins over the project's configured cli_base_branch and the git auto-detect fallback."
|
|
@@ -4779,6 +4999,7 @@ async function runFastTrack(opts) {
|
|
|
4779
4999
|
// is passed; otherwise it's `undefined` and processOneTicketImpl
|
|
4780
5000
|
// treats `undefined` as "auto-review enabled" (opts.autoReview !== false).
|
|
4781
5001
|
...opts.autoReview === false ? { autoReview: false } : {},
|
|
5002
|
+
...opts.fixLint === false ? { fixLint: false } : {},
|
|
4782
5003
|
...opts.baseBranch ? { baseBranch: opts.baseBranch } : {}
|
|
4783
5004
|
};
|
|
4784
5005
|
const outcome = await fastTrackOneTicket({
|
|
@@ -6095,15 +6316,21 @@ function registerConfig(program2) {
|
|
|
6095
6316
|
|
|
6096
6317
|
// src/commands/doctor.ts
|
|
6097
6318
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
6319
|
+
import { existsSync } from "fs";
|
|
6098
6320
|
import { readFile as readFile8, writeFile as writeFile13 } from "fs/promises";
|
|
6099
|
-
import { join as join15 } from "path";
|
|
6321
|
+
import { isAbsolute, join as join15 } from "path";
|
|
6100
6322
|
import { request as request6 } from "undici";
|
|
6101
6323
|
var ALLOWED_TEST_EXECUTABLES = /* @__PURE__ */ new Set(["pnpm", "npm", "yarn", "bun", "node", "npx"]);
|
|
6324
|
+
var PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["pnpm", "npm", "yarn", "bun"]);
|
|
6102
6325
|
var DEFAULT_TEST_COMMAND = "pnpm typecheck";
|
|
6326
|
+
var INSTALL_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
6103
6327
|
function registerDoctor(program2) {
|
|
6104
6328
|
program2.command("doctor").description(
|
|
6105
6329
|
"Diagnose your CLI setup \u2014 Identity (who you are signed in as) first, then Setup checks"
|
|
6106
|
-
).option(
|
|
6330
|
+
).option(
|
|
6331
|
+
"--fix",
|
|
6332
|
+
"attempt to auto-remediate fixable problems (install missing dependencies, add a typecheck script)"
|
|
6333
|
+
).action(async (opts) => {
|
|
6107
6334
|
const checks = [];
|
|
6108
6335
|
const creds = await readCredentials();
|
|
6109
6336
|
let accessLite = null;
|
|
@@ -6310,15 +6537,47 @@ async function checkPrePushTest(root, configuredCommand, fix) {
|
|
|
6310
6537
|
remediation: "update projects.cli_test_command via the dashboard"
|
|
6311
6538
|
};
|
|
6312
6539
|
}
|
|
6313
|
-
const scriptName =
|
|
6540
|
+
const { scriptName, subdir } = resolveTestTarget(argv);
|
|
6541
|
+
const targetDir = subdir ? join15(root, subdir) : root;
|
|
6542
|
+
const where = subdir ? `${subdir}/` : "repo root";
|
|
6543
|
+
const inSubdir = subdir ? ` in ${subdir}/` : "";
|
|
6544
|
+
if (PACKAGE_MANAGERS.has(exe)) {
|
|
6545
|
+
const nodeModules = join15(targetDir, "node_modules");
|
|
6546
|
+
if (!existsSync(nodeModules)) {
|
|
6547
|
+
if (fix) {
|
|
6548
|
+
try {
|
|
6549
|
+
execFileSync12(exe, ["install"], {
|
|
6550
|
+
cwd: targetDir,
|
|
6551
|
+
stdio: "inherit",
|
|
6552
|
+
timeout: INSTALL_TIMEOUT_MS2
|
|
6553
|
+
});
|
|
6554
|
+
} catch (err) {
|
|
6555
|
+
return {
|
|
6556
|
+
name: "pre-push test",
|
|
6557
|
+
ok: false,
|
|
6558
|
+
detail: `dependencies missing (${where}) \u2014 \`${exe} install\` failed: ${err.message}`,
|
|
6559
|
+
remediation: `run \`${exe} install\`${inSubdir} manually, then re-run \`task doctor\``
|
|
6560
|
+
};
|
|
6561
|
+
}
|
|
6562
|
+
}
|
|
6563
|
+
if (!existsSync(nodeModules)) {
|
|
6564
|
+
return {
|
|
6565
|
+
name: "pre-push test",
|
|
6566
|
+
ok: false,
|
|
6567
|
+
detail: `dependencies not installed \u2014 ${where} has no node_modules, so "${command}" will fail`,
|
|
6568
|
+
remediation: `run \`${exe} install\`${inSubdir}, or re-run \`task doctor --fix\` to install automatically`
|
|
6569
|
+
};
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6314
6573
|
if (!scriptName) {
|
|
6315
6574
|
return {
|
|
6316
6575
|
name: "pre-push test",
|
|
6317
6576
|
ok: true,
|
|
6318
|
-
detail: `${command} (non-script executable, not statically verifiable)`
|
|
6577
|
+
detail: PACKAGE_MANAGERS.has(exe) ? `${command} (dependencies present; script not statically verifiable)` : `${command} (non-script executable, not statically verifiable)`
|
|
6319
6578
|
};
|
|
6320
6579
|
}
|
|
6321
|
-
const pkgPath = join15(
|
|
6580
|
+
const pkgPath = join15(targetDir, "package.json");
|
|
6322
6581
|
let pkgRaw;
|
|
6323
6582
|
try {
|
|
6324
6583
|
pkgRaw = await readFile8(pkgPath, "utf8");
|
|
@@ -6326,7 +6585,7 @@ async function checkPrePushTest(root, configuredCommand, fix) {
|
|
|
6326
6585
|
return {
|
|
6327
6586
|
name: "pre-push test",
|
|
6328
6587
|
ok: false,
|
|
6329
|
-
detail: `no package.json
|
|
6588
|
+
detail: `no package.json in ${where} for "${command}"`,
|
|
6330
6589
|
remediation: `add a package.json with a "${scriptName}" script, or set projects.cli_test_command in the dashboard`
|
|
6331
6590
|
};
|
|
6332
6591
|
}
|
|
@@ -6337,7 +6596,7 @@ async function checkPrePushTest(root, configuredCommand, fix) {
|
|
|
6337
6596
|
return {
|
|
6338
6597
|
name: "pre-push test",
|
|
6339
6598
|
ok: false,
|
|
6340
|
-
detail: `package.json is invalid JSON: ${err.message}`
|
|
6599
|
+
detail: `package.json in ${where} is invalid JSON: ${err.message}`
|
|
6341
6600
|
};
|
|
6342
6601
|
}
|
|
6343
6602
|
const scripts = pkg.scripts ?? {};
|
|
@@ -6361,29 +6620,57 @@ async function checkPrePushTest(root, configuredCommand, fix) {
|
|
|
6361
6620
|
detail: `added "typecheck": "tsc --noEmit" to ${pkgPath}`
|
|
6362
6621
|
};
|
|
6363
6622
|
}
|
|
6364
|
-
const remediation = isDefaultTypecheck ? hasTypeScript ? 're-run with --fix to add "typecheck": "tsc --noEmit" to package.json' : 'add a "typecheck" script to package.json, or set a different cli_test_command in the dashboard' : `add a "${scriptName}" script to package.json, or update cli_test_command in the dashboard`;
|
|
6623
|
+
const remediation = isDefaultTypecheck ? hasTypeScript ? 're-run with --fix to add "typecheck": "tsc --noEmit" to package.json' : 'add a "typecheck" script to package.json, or set a different cli_test_command in the dashboard' : `add a "${scriptName}" script to ${where} package.json, or update cli_test_command in the dashboard`;
|
|
6365
6624
|
return {
|
|
6366
6625
|
name: "pre-push test",
|
|
6367
6626
|
ok: false,
|
|
6368
|
-
detail: `"${scriptName}" script missing \u2014 "${command}" will
|
|
6627
|
+
detail: `"${scriptName}" script missing from ${where} package.json \u2014 "${command}" will fail`,
|
|
6369
6628
|
remediation
|
|
6370
6629
|
};
|
|
6371
6630
|
}
|
|
6372
|
-
function
|
|
6631
|
+
function resolveTestTarget(argv) {
|
|
6373
6632
|
const [exe, ...rest] = argv;
|
|
6374
|
-
|
|
6375
|
-
const
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
const
|
|
6379
|
-
if (
|
|
6380
|
-
|
|
6381
|
-
|
|
6633
|
+
let subdir = null;
|
|
6634
|
+
const positional = [];
|
|
6635
|
+
let opaque = false;
|
|
6636
|
+
for (let i = 0; i < rest.length; i++) {
|
|
6637
|
+
const tok = rest[i];
|
|
6638
|
+
if (tok === "--prefix" || tok === "-C" || tok === "--dir") {
|
|
6639
|
+
const val = rest[i + 1];
|
|
6640
|
+
if (val !== void 0) {
|
|
6641
|
+
subdir = val;
|
|
6642
|
+
i++;
|
|
6643
|
+
}
|
|
6644
|
+
continue;
|
|
6645
|
+
}
|
|
6646
|
+
const eq = tok.match(/^(?:--prefix|--dir)=(.+)$/);
|
|
6647
|
+
if (eq) {
|
|
6648
|
+
subdir = eq[1];
|
|
6649
|
+
continue;
|
|
6650
|
+
}
|
|
6651
|
+
if (tok.startsWith("-")) {
|
|
6652
|
+
opaque = true;
|
|
6653
|
+
continue;
|
|
6654
|
+
}
|
|
6655
|
+
positional.push(tok);
|
|
6656
|
+
}
|
|
6657
|
+
if (subdir !== null && (isAbsolute(subdir) || subdir.split(/[\\/]/).includes(".."))) {
|
|
6658
|
+
subdir = null;
|
|
6659
|
+
opaque = true;
|
|
6382
6660
|
}
|
|
6661
|
+
const scriptName = opaque ? null : resolveScriptName(exe, positional);
|
|
6662
|
+
return { scriptName, subdir, opaque };
|
|
6663
|
+
}
|
|
6664
|
+
function resolveScriptName(exe, positional) {
|
|
6665
|
+
if (!exe || positional.length === 0) return null;
|
|
6383
6666
|
if (exe === "npm") {
|
|
6384
|
-
if (
|
|
6667
|
+
if (positional[0] === "run" || positional[0] === "run-script") return positional[1] ?? null;
|
|
6385
6668
|
return null;
|
|
6386
6669
|
}
|
|
6670
|
+
if (exe === "pnpm" || exe === "yarn" || exe === "bun") {
|
|
6671
|
+
if (positional[0] === "run") return positional[1] ?? null;
|
|
6672
|
+
return positional[0] ?? null;
|
|
6673
|
+
}
|
|
6387
6674
|
return null;
|
|
6388
6675
|
}
|
|
6389
6676
|
function detectIndent(raw) {
|
|
@@ -6403,7 +6690,7 @@ function checkBinary(name, command) {
|
|
|
6403
6690
|
}
|
|
6404
6691
|
|
|
6405
6692
|
// src/commands/version.ts
|
|
6406
|
-
var CLI_VERSION = true ? "0.2.
|
|
6693
|
+
var CLI_VERSION = true ? "0.2.31" : "0.0.0-dev";
|
|
6407
6694
|
function registerVersion(program2) {
|
|
6408
6695
|
program2.command("version").description("Print the CLI version").action(() => {
|
|
6409
6696
|
process.stdout.write(CLI_VERSION + "\n");
|