@inteeka/task-cli 0.1.10 → 0.1.12
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 +170 -26
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2170,7 +2170,11 @@ import { homedir as homedir5 } from "os";
|
|
|
2170
2170
|
import { join as join7 } from "path";
|
|
2171
2171
|
var FIX_PROMPT_JSON_SCHEMA = {
|
|
2172
2172
|
type: "object",
|
|
2173
|
-
|
|
2173
|
+
// Phase 3 — confidence_reason is REQUIRED unconditionally so the
|
|
2174
|
+
// server-side refine() (which mandates ≥20 chars on low/medium) never
|
|
2175
|
+
// rejects a fresh submission. The model emits it for high too; that's
|
|
2176
|
+
// cheap (≤1500 chars) and doubles as documentation for reviewers.
|
|
2177
|
+
required: ["summary", "suspected_files", "proposed_changes", "confidence", "confidence_reason"],
|
|
2174
2178
|
additionalProperties: false,
|
|
2175
2179
|
properties: {
|
|
2176
2180
|
summary: { type: "string", minLength: 1, maxLength: 2e3 },
|
|
@@ -2209,11 +2213,11 @@ var FIX_PROMPT_JSON_SCHEMA = {
|
|
|
2209
2213
|
},
|
|
2210
2214
|
risk_notes: { type: "string", maxLength: 2e3 },
|
|
2211
2215
|
confidence: { type: "string", enum: ["low", "medium", "high"] },
|
|
2212
|
-
// Phase 3 — explicit reasoning for the confidence rating.
|
|
2213
|
-
//
|
|
2214
|
-
//
|
|
2215
|
-
//
|
|
2216
|
-
confidence_reason: { type: "string", maxLength: 1500 }
|
|
2216
|
+
// Phase 3 — explicit reasoning for the confidence rating. Always
|
|
2217
|
+
// required (see `required` array above). minLength=20 matches the
|
|
2218
|
+
// dashboard's Zod refine() so low/medium submissions never fail
|
|
2219
|
+
// server-side validation; high also emits it (cheap, useful).
|
|
2220
|
+
confidence_reason: { type: "string", minLength: 20, maxLength: 1500 }
|
|
2217
2221
|
}
|
|
2218
2222
|
};
|
|
2219
2223
|
var LlmGenerationError = class extends Error {
|
|
@@ -2232,7 +2236,9 @@ async function generateFixPromptJson(args) {
|
|
|
2232
2236
|
"",
|
|
2233
2237
|
args.ticketBlock,
|
|
2234
2238
|
"",
|
|
2235
|
-
"Return JSON only matching the supplied schema. Do not include explanatory prose, markdown fences, or commentary."
|
|
2239
|
+
"Return JSON only matching the supplied schema. Do not include explanatory prose, markdown fences, or commentary.",
|
|
2240
|
+
"",
|
|
2241
|
+
"IMPORTANT: confidence_reason is REQUIRED for every rating (\u226520 chars). For low/medium, name which investigation axes lacked signal. For high, briefly state which axes corroborated the proposal."
|
|
2236
2242
|
].join("\n");
|
|
2237
2243
|
const cliArgs = [
|
|
2238
2244
|
"--print",
|
|
@@ -2705,6 +2711,143 @@ function clampInt(raw, min, max, fallback) {
|
|
|
2705
2711
|
return Math.min(v, max);
|
|
2706
2712
|
}
|
|
2707
2713
|
|
|
2714
|
+
// src/commands/pr-test.ts
|
|
2715
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
2716
|
+
function registerPrTest(program2) {
|
|
2717
|
+
program2.command("pr-test").description(
|
|
2718
|
+
"Dry-run the full PR pipeline \u2014 cuts a throwaway branch, opens a real PR via the dashboard, then cleans up. Use this to verify your git integration before running task work on real tickets."
|
|
2719
|
+
).option(
|
|
2720
|
+
"--keep",
|
|
2721
|
+
"Leave the PR open and the branches in place after the test (default: clean up)"
|
|
2722
|
+
).option("--silent", "Suppress per-step progress output").action(async (opts) => {
|
|
2723
|
+
await runPrTest(opts);
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
async function runPrTest(opts) {
|
|
2727
|
+
const cwd = findRepoRoot();
|
|
2728
|
+
const project = await readProjectConfig(cwd);
|
|
2729
|
+
if (!project) {
|
|
2730
|
+
throw new CliError(
|
|
2731
|
+
CLI_EXIT_CODES.MISCONFIGURATION,
|
|
2732
|
+
"No project link in this repo",
|
|
2733
|
+
"Run 'task link' first."
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
const baseBranch = project.cli_base_branch ?? "development";
|
|
2737
|
+
const silent = !!opts.silent;
|
|
2738
|
+
if (!silent) process.stdout.write(`${c.dim(`Step 1/6: assertBaseBranch (${baseBranch})\u2026`)}
|
|
2739
|
+
`);
|
|
2740
|
+
assertBaseBranch(cwd, baseBranch);
|
|
2741
|
+
const timestamp = Date.now();
|
|
2742
|
+
const branchName = `task/pr-test-${timestamp}`;
|
|
2743
|
+
const prTitle = `[task pr-test] CLI dry-run probe ${new Date(timestamp).toISOString()}`;
|
|
2744
|
+
if (!silent) process.stdout.write(`${c.dim(`Step 2/6: createTicketBranch (${branchName})\u2026`)}
|
|
2745
|
+
`);
|
|
2746
|
+
createTicketBranch(cwd, branchName, baseBranch);
|
|
2747
|
+
let pushSucceeded = false;
|
|
2748
|
+
const recover = () => {
|
|
2749
|
+
try {
|
|
2750
|
+
checkoutBranch(cwd, baseBranch);
|
|
2751
|
+
} catch {
|
|
2752
|
+
}
|
|
2753
|
+
deleteLocalBranch(cwd, branchName);
|
|
2754
|
+
};
|
|
2755
|
+
try {
|
|
2756
|
+
if (!silent) process.stdout.write(`${c.dim("Step 3/6: empty commit\u2026")}
|
|
2757
|
+
`);
|
|
2758
|
+
execFileSync6(
|
|
2759
|
+
"git",
|
|
2760
|
+
["commit", "--allow-empty", "-m", `task pr-test: connectivity probe ${timestamp}`],
|
|
2761
|
+
{ cwd, stdio: ["ignore", "pipe", "pipe"] }
|
|
2762
|
+
);
|
|
2763
|
+
if (!silent) process.stdout.write(`${c.dim(`Step 4/6: pushBranch (${branchName})\u2026`)}
|
|
2764
|
+
`);
|
|
2765
|
+
pushBranch(cwd, branchName);
|
|
2766
|
+
pushSucceeded = true;
|
|
2767
|
+
if (!silent) process.stdout.write(`${c.dim("Step 5/6: open PR via dashboard\u2026")}
|
|
2768
|
+
`);
|
|
2769
|
+
const pr = await apiCallOrThrow("POST", `/api/v1/cli/me/git/pr-test`, {
|
|
2770
|
+
body: {
|
|
2771
|
+
project_id: project.project_id,
|
|
2772
|
+
source_branch: branchName,
|
|
2773
|
+
base_branch: baseBranch,
|
|
2774
|
+
title: prTitle
|
|
2775
|
+
}
|
|
2776
|
+
});
|
|
2777
|
+
process.stdout.write(
|
|
2778
|
+
`
|
|
2779
|
+
${c.ok("\u2713 PR opened")} ${c.cyan(pr.pr_url)}
|
|
2780
|
+
repo: ${pr.repo}
|
|
2781
|
+
branch: ${pr.source_branch} \u2192 ${pr.base_branch}
|
|
2782
|
+
number: #${pr.pr_number}
|
|
2783
|
+
|
|
2784
|
+
`
|
|
2785
|
+
);
|
|
2786
|
+
if (opts.keep) {
|
|
2787
|
+
process.stdout.write(
|
|
2788
|
+
`${c.warn("--keep")} ${c.dim("flag set \u2014 PR and branches preserved. Manual cleanup:")}
|
|
2789
|
+
${c.cyan(`task pr-test-cleanup ${pr.pr_number} ${branchName}`)}
|
|
2790
|
+
(or close on GitHub + ${c.cyan(`git push origin --delete ${branchName}`)} + ${c.cyan(`git branch -D ${branchName}`)})
|
|
2791
|
+
`
|
|
2792
|
+
);
|
|
2793
|
+
checkoutBranch(cwd, baseBranch);
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
if (!silent) process.stdout.write(`${c.dim("Step 6/6: cleanup\u2026")}
|
|
2797
|
+
`);
|
|
2798
|
+
const cleanup = await apiCall("POST", `/api/v1/cli/me/git/pr-test/cleanup`, {
|
|
2799
|
+
body: {
|
|
2800
|
+
project_id: project.project_id,
|
|
2801
|
+
pr_number: pr.pr_number,
|
|
2802
|
+
source_branch: branchName
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
checkoutBranch(cwd, baseBranch);
|
|
2806
|
+
deleteLocalBranch(cwd, branchName);
|
|
2807
|
+
if (cleanup.ok && cleanup.data) {
|
|
2808
|
+
const { pr_closed, branch_deleted } = cleanup.data;
|
|
2809
|
+
process.stdout.write(
|
|
2810
|
+
`${c.ok("\u2713 Cleanup complete")} \u2014 PR ${pr_closed ? "closed" : c.warn("NOT closed")}, remote branch ${branch_deleted ? "deleted" : c.warn("NOT deleted")}, local branch deleted.
|
|
2811
|
+
`
|
|
2812
|
+
);
|
|
2813
|
+
if (!pr_closed || !branch_deleted) {
|
|
2814
|
+
process.stdout.write(
|
|
2815
|
+
c.dim(` Manually close at: ${pr.pr_url}
|
|
2816
|
+
`) + c.dim(` Manually delete remote: git push origin --delete ${branchName}
|
|
2817
|
+
`)
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
} else {
|
|
2821
|
+
process.stdout.write(
|
|
2822
|
+
`${c.warn("Cleanup endpoint failed \u2014 branches & PR may need manual cleanup:")}
|
|
2823
|
+
${c.cyan(pr.pr_url)}
|
|
2824
|
+
${c.cyan(`git push origin --delete ${branchName}`)}
|
|
2825
|
+
`
|
|
2826
|
+
);
|
|
2827
|
+
}
|
|
2828
|
+
process.stdout.write(
|
|
2829
|
+
`
|
|
2830
|
+
${c.ok("\u2713 pr-test passed")} \u2014 the full CLI \u2192 dashboard \u2192 GitHub PR pipeline works.
|
|
2831
|
+
`
|
|
2832
|
+
);
|
|
2833
|
+
} catch (err) {
|
|
2834
|
+
process.stdout.write(`
|
|
2835
|
+
${c.err("\u2717 pr-test failed")}: ${err.message}
|
|
2836
|
+
`);
|
|
2837
|
+
if (pushSucceeded) {
|
|
2838
|
+
process.stdout.write(
|
|
2839
|
+
c.dim(
|
|
2840
|
+
` The branch was pushed but the PR step failed. Manually clean up:
|
|
2841
|
+
git push origin --delete ${branchName}
|
|
2842
|
+
`
|
|
2843
|
+
)
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2846
|
+
recover();
|
|
2847
|
+
throw err;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2708
2851
|
// src/commands/scheduled-task.ts
|
|
2709
2852
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2710
2853
|
|
|
@@ -2715,7 +2858,7 @@ import { platform as platform2 } from "os";
|
|
|
2715
2858
|
import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile8, unlink as unlink3, readdir } from "fs/promises";
|
|
2716
2859
|
import { homedir as homedir6 } from "os";
|
|
2717
2860
|
import { join as join8 } from "path";
|
|
2718
|
-
import { execFileSync as
|
|
2861
|
+
import { execFileSync as execFileSync7, spawn as spawn4 } from "child_process";
|
|
2719
2862
|
|
|
2720
2863
|
// src/scheduler/cron-translate.ts
|
|
2721
2864
|
function translateToLaunchd(cron) {
|
|
@@ -2883,17 +3026,17 @@ var launchdAdapter = {
|
|
|
2883
3026
|
const path = plistPath(entry.id);
|
|
2884
3027
|
await writeFile8(path, buildPlist(entry));
|
|
2885
3028
|
try {
|
|
2886
|
-
|
|
3029
|
+
execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
|
|
2887
3030
|
} catch {
|
|
2888
3031
|
}
|
|
2889
3032
|
if (entry.enabled) {
|
|
2890
|
-
|
|
3033
|
+
execFileSync7("launchctl", ["bootstrap", bootstrapDomain(), path]);
|
|
2891
3034
|
}
|
|
2892
3035
|
},
|
|
2893
3036
|
async remove(id) {
|
|
2894
3037
|
const path = plistPath(id);
|
|
2895
3038
|
try {
|
|
2896
|
-
|
|
3039
|
+
execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
|
|
2897
3040
|
} catch {
|
|
2898
3041
|
}
|
|
2899
3042
|
try {
|
|
@@ -2958,10 +3101,10 @@ var launchdAdapter = {
|
|
|
2958
3101
|
xml = xml.replace(/\s*<key>Disabled<\/key>\s*<true\/>/, "");
|
|
2959
3102
|
await writeFile8(path, xml);
|
|
2960
3103
|
try {
|
|
2961
|
-
|
|
3104
|
+
execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
|
|
2962
3105
|
} catch {
|
|
2963
3106
|
}
|
|
2964
|
-
|
|
3107
|
+
execFileSync7("launchctl", ["bootstrap", bootstrapDomain(), path]);
|
|
2965
3108
|
} else {
|
|
2966
3109
|
if (!/<key>Disabled<\/key>/.test(xml)) {
|
|
2967
3110
|
xml = xml.replace(
|
|
@@ -2971,7 +3114,7 @@ var launchdAdapter = {
|
|
|
2971
3114
|
await writeFile8(path, xml);
|
|
2972
3115
|
}
|
|
2973
3116
|
try {
|
|
2974
|
-
|
|
3117
|
+
execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
|
|
2975
3118
|
} catch {
|
|
2976
3119
|
}
|
|
2977
3120
|
}
|
|
@@ -2979,7 +3122,7 @@ var launchdAdapter = {
|
|
|
2979
3122
|
};
|
|
2980
3123
|
|
|
2981
3124
|
// src/scheduler/cron.ts
|
|
2982
|
-
import { execFileSync as
|
|
3125
|
+
import { execFileSync as execFileSync8, spawn as spawn5 } from "child_process";
|
|
2983
3126
|
|
|
2984
3127
|
// src/scheduler/safe-command.ts
|
|
2985
3128
|
var FORBIDDEN = /[;&|`$()<>\\]/;
|
|
@@ -3034,7 +3177,7 @@ var MARK_OPEN = (id) => `# task-cli:${id}:start`;
|
|
|
3034
3177
|
var MARK_CLOSE = (id) => `# task-cli:${id}:end`;
|
|
3035
3178
|
function readCrontab() {
|
|
3036
3179
|
try {
|
|
3037
|
-
return
|
|
3180
|
+
return execFileSync8("crontab", ["-l"], { encoding: "utf8" });
|
|
3038
3181
|
} catch {
|
|
3039
3182
|
return "";
|
|
3040
3183
|
}
|
|
@@ -3149,7 +3292,7 @@ var cronAdapter = {
|
|
|
3149
3292
|
};
|
|
3150
3293
|
|
|
3151
3294
|
// src/scheduler/windows.ts
|
|
3152
|
-
import { execFileSync as
|
|
3295
|
+
import { execFileSync as execFileSync9, spawn as spawn6 } from "child_process";
|
|
3153
3296
|
var TASK_PREFIX = "TaskCLI_";
|
|
3154
3297
|
function taskName(id) {
|
|
3155
3298
|
return `${TASK_PREFIX}${id.replace(/[^A-Za-z0-9_-]/g, "_")}`;
|
|
@@ -3214,22 +3357,22 @@ function pad(v) {
|
|
|
3214
3357
|
var windowsAdapter = {
|
|
3215
3358
|
async upsert(entry) {
|
|
3216
3359
|
const args = buildSchtasksArgs(entry, entry.command);
|
|
3217
|
-
|
|
3360
|
+
execFileSync9("schtasks.exe", args, { stdio: "ignore" });
|
|
3218
3361
|
if (!entry.enabled) {
|
|
3219
|
-
|
|
3362
|
+
execFileSync9("schtasks.exe", ["/Change", "/TN", taskName(entry.id), "/DISABLE"], {
|
|
3220
3363
|
stdio: "ignore"
|
|
3221
3364
|
});
|
|
3222
3365
|
}
|
|
3223
3366
|
},
|
|
3224
3367
|
async remove(id) {
|
|
3225
3368
|
try {
|
|
3226
|
-
|
|
3369
|
+
execFileSync9("schtasks.exe", ["/Delete", "/TN", taskName(id), "/F"], { stdio: "ignore" });
|
|
3227
3370
|
} catch {
|
|
3228
3371
|
}
|
|
3229
3372
|
},
|
|
3230
3373
|
async list() {
|
|
3231
3374
|
try {
|
|
3232
|
-
const csv =
|
|
3375
|
+
const csv = execFileSync9("schtasks.exe", ["/Query", "/FO", "CSV", "/V"], {
|
|
3233
3376
|
encoding: "utf8"
|
|
3234
3377
|
});
|
|
3235
3378
|
const lines = csv.split(/\r?\n/);
|
|
@@ -3279,7 +3422,7 @@ var windowsAdapter = {
|
|
|
3279
3422
|
},
|
|
3280
3423
|
async setEnabled(id, enabled) {
|
|
3281
3424
|
try {
|
|
3282
|
-
|
|
3425
|
+
execFileSync9(
|
|
3283
3426
|
"schtasks.exe",
|
|
3284
3427
|
["/Change", "/TN", taskName(id), enabled ? "/ENABLE" : "/DISABLE"],
|
|
3285
3428
|
{ stdio: "ignore" }
|
|
@@ -3687,7 +3830,7 @@ function registerConfig(program2) {
|
|
|
3687
3830
|
}
|
|
3688
3831
|
|
|
3689
3832
|
// src/commands/doctor.ts
|
|
3690
|
-
import { execFileSync as
|
|
3833
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
3691
3834
|
import { request as request5 } from "undici";
|
|
3692
3835
|
function registerDoctor(program2) {
|
|
3693
3836
|
program2.command("doctor").description("Diagnose your CLI setup").action(async () => {
|
|
@@ -3735,7 +3878,7 @@ function registerDoctor(program2) {
|
|
|
3735
3878
|
});
|
|
3736
3879
|
}
|
|
3737
3880
|
try {
|
|
3738
|
-
const dirty =
|
|
3881
|
+
const dirty = execFileSync10("git", ["status", "--porcelain"], {
|
|
3739
3882
|
cwd: root,
|
|
3740
3883
|
encoding: "utf8"
|
|
3741
3884
|
}).trim();
|
|
@@ -3759,7 +3902,7 @@ function registerDoctor(program2) {
|
|
|
3759
3902
|
}
|
|
3760
3903
|
function checkBinary(name, command) {
|
|
3761
3904
|
try {
|
|
3762
|
-
const out =
|
|
3905
|
+
const out = execFileSync10(command, ["--version"], { encoding: "utf8" }).trim();
|
|
3763
3906
|
return { name, ok: true, detail: out.split("\n")[0] ?? out };
|
|
3764
3907
|
} catch {
|
|
3765
3908
|
return { name, ok: false, detail: `'${command}' not found on PATH` };
|
|
@@ -3767,7 +3910,7 @@ function checkBinary(name, command) {
|
|
|
3767
3910
|
}
|
|
3768
3911
|
|
|
3769
3912
|
// src/commands/version.ts
|
|
3770
|
-
var CLI_VERSION = true ? "0.1.
|
|
3913
|
+
var CLI_VERSION = true ? "0.1.12" : "0.0.0-dev";
|
|
3771
3914
|
function registerVersion(program2) {
|
|
3772
3915
|
program2.command("version").description("Print the CLI version").action(() => {
|
|
3773
3916
|
process.stdout.write(CLI_VERSION + "\n");
|
|
@@ -3791,6 +3934,7 @@ registerTickets(program);
|
|
|
3791
3934
|
registerTicket(program);
|
|
3792
3935
|
registerWork(program);
|
|
3793
3936
|
registerScan(program);
|
|
3937
|
+
registerPrTest(program);
|
|
3794
3938
|
registerScheduledTask(program);
|
|
3795
3939
|
registerRuns(program);
|
|
3796
3940
|
registerConfig(program);
|