@inteeka/task-cli 0.1.11 → 0.1.13

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 CHANGED
@@ -78,6 +78,23 @@ var PRODUCTION_HOSTS = {
78
78
  };
79
79
  var ALL_VALID_HOSTS = [PRODUCTION_HOSTS.PRIMARY, PRODUCTION_HOSTS.VERCEL];
80
80
 
81
+ // ../../packages/constants/src/ai.ts
82
+ var ANTHROPIC_DEFAULT_MODEL = "claude-sonnet-4-6";
83
+ var ANTHROPIC_HEAVY_MODEL = "claude-opus-4-7";
84
+ var CLI_FIX_MODELS = [
85
+ {
86
+ id: ANTHROPIC_DEFAULT_MODEL,
87
+ label: "Sonnet 4.6",
88
+ description: "Default \u2014 fast, cheap, right for most fixes."
89
+ },
90
+ {
91
+ id: ANTHROPIC_HEAVY_MODEL,
92
+ label: "Opus 4.7",
93
+ description: "Heavy reasoning \u2014 pick for cross-cutting refactors or tricky bugs."
94
+ }
95
+ ];
96
+ var CLI_FIX_MODEL_IDS = CLI_FIX_MODELS.map((m) => m.id);
97
+
81
98
  // ../../packages/constants/src/cli.ts
82
99
  var CLI_DEFAULT_PROTECTED_PATHS = Object.freeze([
83
100
  // Package manifests + lockfiles
@@ -2711,6 +2728,143 @@ function clampInt(raw, min, max, fallback) {
2711
2728
  return Math.min(v, max);
2712
2729
  }
2713
2730
 
2731
+ // src/commands/pr-test.ts
2732
+ import { execFileSync as execFileSync6 } from "child_process";
2733
+ function registerPrTest(program2) {
2734
+ program2.command("pr-test").description(
2735
+ "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."
2736
+ ).option(
2737
+ "--keep",
2738
+ "Leave the PR open and the branches in place after the test (default: clean up)"
2739
+ ).option("--silent", "Suppress per-step progress output").action(async (opts) => {
2740
+ await runPrTest(opts);
2741
+ });
2742
+ }
2743
+ async function runPrTest(opts) {
2744
+ const cwd = findRepoRoot();
2745
+ const project = await readProjectConfig(cwd);
2746
+ if (!project) {
2747
+ throw new CliError(
2748
+ CLI_EXIT_CODES.MISCONFIGURATION,
2749
+ "No project link in this repo",
2750
+ "Run 'task link' first."
2751
+ );
2752
+ }
2753
+ const baseBranch = project.cli_base_branch ?? "development";
2754
+ const silent = !!opts.silent;
2755
+ if (!silent) process.stdout.write(`${c.dim(`Step 1/6: assertBaseBranch (${baseBranch})\u2026`)}
2756
+ `);
2757
+ assertBaseBranch(cwd, baseBranch);
2758
+ const timestamp = Date.now();
2759
+ const branchName = `task/pr-test-${timestamp}`;
2760
+ const prTitle = `[task pr-test] CLI dry-run probe ${new Date(timestamp).toISOString()}`;
2761
+ if (!silent) process.stdout.write(`${c.dim(`Step 2/6: createTicketBranch (${branchName})\u2026`)}
2762
+ `);
2763
+ createTicketBranch(cwd, branchName, baseBranch);
2764
+ let pushSucceeded = false;
2765
+ const recover = () => {
2766
+ try {
2767
+ checkoutBranch(cwd, baseBranch);
2768
+ } catch {
2769
+ }
2770
+ deleteLocalBranch(cwd, branchName);
2771
+ };
2772
+ try {
2773
+ if (!silent) process.stdout.write(`${c.dim("Step 3/6: empty commit\u2026")}
2774
+ `);
2775
+ execFileSync6(
2776
+ "git",
2777
+ ["commit", "--allow-empty", "-m", `task pr-test: connectivity probe ${timestamp}`],
2778
+ { cwd, stdio: ["ignore", "pipe", "pipe"] }
2779
+ );
2780
+ if (!silent) process.stdout.write(`${c.dim(`Step 4/6: pushBranch (${branchName})\u2026`)}
2781
+ `);
2782
+ pushBranch(cwd, branchName);
2783
+ pushSucceeded = true;
2784
+ if (!silent) process.stdout.write(`${c.dim("Step 5/6: open PR via dashboard\u2026")}
2785
+ `);
2786
+ const pr = await apiCallOrThrow("POST", `/api/v1/cli/me/git/pr-test`, {
2787
+ body: {
2788
+ project_id: project.project_id,
2789
+ source_branch: branchName,
2790
+ base_branch: baseBranch,
2791
+ title: prTitle
2792
+ }
2793
+ });
2794
+ process.stdout.write(
2795
+ `
2796
+ ${c.ok("\u2713 PR opened")} ${c.cyan(pr.pr_url)}
2797
+ repo: ${pr.repo}
2798
+ branch: ${pr.source_branch} \u2192 ${pr.base_branch}
2799
+ number: #${pr.pr_number}
2800
+
2801
+ `
2802
+ );
2803
+ if (opts.keep) {
2804
+ process.stdout.write(
2805
+ `${c.warn("--keep")} ${c.dim("flag set \u2014 PR and branches preserved. Manual cleanup:")}
2806
+ ${c.cyan(`task pr-test-cleanup ${pr.pr_number} ${branchName}`)}
2807
+ (or close on GitHub + ${c.cyan(`git push origin --delete ${branchName}`)} + ${c.cyan(`git branch -D ${branchName}`)})
2808
+ `
2809
+ );
2810
+ checkoutBranch(cwd, baseBranch);
2811
+ return;
2812
+ }
2813
+ if (!silent) process.stdout.write(`${c.dim("Step 6/6: cleanup\u2026")}
2814
+ `);
2815
+ const cleanup = await apiCall("POST", `/api/v1/cli/me/git/pr-test/cleanup`, {
2816
+ body: {
2817
+ project_id: project.project_id,
2818
+ pr_number: pr.pr_number,
2819
+ source_branch: branchName
2820
+ }
2821
+ });
2822
+ checkoutBranch(cwd, baseBranch);
2823
+ deleteLocalBranch(cwd, branchName);
2824
+ if (cleanup.ok && cleanup.data) {
2825
+ const { pr_closed, branch_deleted } = cleanup.data;
2826
+ process.stdout.write(
2827
+ `${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.
2828
+ `
2829
+ );
2830
+ if (!pr_closed || !branch_deleted) {
2831
+ process.stdout.write(
2832
+ c.dim(` Manually close at: ${pr.pr_url}
2833
+ `) + c.dim(` Manually delete remote: git push origin --delete ${branchName}
2834
+ `)
2835
+ );
2836
+ }
2837
+ } else {
2838
+ process.stdout.write(
2839
+ `${c.warn("Cleanup endpoint failed \u2014 branches & PR may need manual cleanup:")}
2840
+ ${c.cyan(pr.pr_url)}
2841
+ ${c.cyan(`git push origin --delete ${branchName}`)}
2842
+ `
2843
+ );
2844
+ }
2845
+ process.stdout.write(
2846
+ `
2847
+ ${c.ok("\u2713 pr-test passed")} \u2014 the full CLI \u2192 dashboard \u2192 GitHub PR pipeline works.
2848
+ `
2849
+ );
2850
+ } catch (err) {
2851
+ process.stdout.write(`
2852
+ ${c.err("\u2717 pr-test failed")}: ${err.message}
2853
+ `);
2854
+ if (pushSucceeded) {
2855
+ process.stdout.write(
2856
+ c.dim(
2857
+ ` The branch was pushed but the PR step failed. Manually clean up:
2858
+ git push origin --delete ${branchName}
2859
+ `
2860
+ )
2861
+ );
2862
+ }
2863
+ recover();
2864
+ throw err;
2865
+ }
2866
+ }
2867
+
2714
2868
  // src/commands/scheduled-task.ts
2715
2869
  import { randomUUID as randomUUID3 } from "crypto";
2716
2870
 
@@ -2721,7 +2875,7 @@ import { platform as platform2 } from "os";
2721
2875
  import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile8, unlink as unlink3, readdir } from "fs/promises";
2722
2876
  import { homedir as homedir6 } from "os";
2723
2877
  import { join as join8 } from "path";
2724
- import { execFileSync as execFileSync6, spawn as spawn4 } from "child_process";
2878
+ import { execFileSync as execFileSync7, spawn as spawn4 } from "child_process";
2725
2879
 
2726
2880
  // src/scheduler/cron-translate.ts
2727
2881
  function translateToLaunchd(cron) {
@@ -2889,17 +3043,17 @@ var launchdAdapter = {
2889
3043
  const path = plistPath(entry.id);
2890
3044
  await writeFile8(path, buildPlist(entry));
2891
3045
  try {
2892
- execFileSync6("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
3046
+ execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
2893
3047
  } catch {
2894
3048
  }
2895
3049
  if (entry.enabled) {
2896
- execFileSync6("launchctl", ["bootstrap", bootstrapDomain(), path]);
3050
+ execFileSync7("launchctl", ["bootstrap", bootstrapDomain(), path]);
2897
3051
  }
2898
3052
  },
2899
3053
  async remove(id) {
2900
3054
  const path = plistPath(id);
2901
3055
  try {
2902
- execFileSync6("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
3056
+ execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
2903
3057
  } catch {
2904
3058
  }
2905
3059
  try {
@@ -2964,10 +3118,10 @@ var launchdAdapter = {
2964
3118
  xml = xml.replace(/\s*<key>Disabled<\/key>\s*<true\/>/, "");
2965
3119
  await writeFile8(path, xml);
2966
3120
  try {
2967
- execFileSync6("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
3121
+ execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
2968
3122
  } catch {
2969
3123
  }
2970
- execFileSync6("launchctl", ["bootstrap", bootstrapDomain(), path]);
3124
+ execFileSync7("launchctl", ["bootstrap", bootstrapDomain(), path]);
2971
3125
  } else {
2972
3126
  if (!/<key>Disabled<\/key>/.test(xml)) {
2973
3127
  xml = xml.replace(
@@ -2977,7 +3131,7 @@ var launchdAdapter = {
2977
3131
  await writeFile8(path, xml);
2978
3132
  }
2979
3133
  try {
2980
- execFileSync6("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
3134
+ execFileSync7("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
2981
3135
  } catch {
2982
3136
  }
2983
3137
  }
@@ -2985,7 +3139,7 @@ var launchdAdapter = {
2985
3139
  };
2986
3140
 
2987
3141
  // src/scheduler/cron.ts
2988
- import { execFileSync as execFileSync7, spawn as spawn5 } from "child_process";
3142
+ import { execFileSync as execFileSync8, spawn as spawn5 } from "child_process";
2989
3143
 
2990
3144
  // src/scheduler/safe-command.ts
2991
3145
  var FORBIDDEN = /[;&|`$()<>\\]/;
@@ -3040,7 +3194,7 @@ var MARK_OPEN = (id) => `# task-cli:${id}:start`;
3040
3194
  var MARK_CLOSE = (id) => `# task-cli:${id}:end`;
3041
3195
  function readCrontab() {
3042
3196
  try {
3043
- return execFileSync7("crontab", ["-l"], { encoding: "utf8" });
3197
+ return execFileSync8("crontab", ["-l"], { encoding: "utf8" });
3044
3198
  } catch {
3045
3199
  return "";
3046
3200
  }
@@ -3155,7 +3309,7 @@ var cronAdapter = {
3155
3309
  };
3156
3310
 
3157
3311
  // src/scheduler/windows.ts
3158
- import { execFileSync as execFileSync8, spawn as spawn6 } from "child_process";
3312
+ import { execFileSync as execFileSync9, spawn as spawn6 } from "child_process";
3159
3313
  var TASK_PREFIX = "TaskCLI_";
3160
3314
  function taskName(id) {
3161
3315
  return `${TASK_PREFIX}${id.replace(/[^A-Za-z0-9_-]/g, "_")}`;
@@ -3220,22 +3374,22 @@ function pad(v) {
3220
3374
  var windowsAdapter = {
3221
3375
  async upsert(entry) {
3222
3376
  const args = buildSchtasksArgs(entry, entry.command);
3223
- execFileSync8("schtasks.exe", args, { stdio: "ignore" });
3377
+ execFileSync9("schtasks.exe", args, { stdio: "ignore" });
3224
3378
  if (!entry.enabled) {
3225
- execFileSync8("schtasks.exe", ["/Change", "/TN", taskName(entry.id), "/DISABLE"], {
3379
+ execFileSync9("schtasks.exe", ["/Change", "/TN", taskName(entry.id), "/DISABLE"], {
3226
3380
  stdio: "ignore"
3227
3381
  });
3228
3382
  }
3229
3383
  },
3230
3384
  async remove(id) {
3231
3385
  try {
3232
- execFileSync8("schtasks.exe", ["/Delete", "/TN", taskName(id), "/F"], { stdio: "ignore" });
3386
+ execFileSync9("schtasks.exe", ["/Delete", "/TN", taskName(id), "/F"], { stdio: "ignore" });
3233
3387
  } catch {
3234
3388
  }
3235
3389
  },
3236
3390
  async list() {
3237
3391
  try {
3238
- const csv = execFileSync8("schtasks.exe", ["/Query", "/FO", "CSV", "/V"], {
3392
+ const csv = execFileSync9("schtasks.exe", ["/Query", "/FO", "CSV", "/V"], {
3239
3393
  encoding: "utf8"
3240
3394
  });
3241
3395
  const lines = csv.split(/\r?\n/);
@@ -3285,7 +3439,7 @@ var windowsAdapter = {
3285
3439
  },
3286
3440
  async setEnabled(id, enabled) {
3287
3441
  try {
3288
- execFileSync8(
3442
+ execFileSync9(
3289
3443
  "schtasks.exe",
3290
3444
  ["/Change", "/TN", taskName(id), enabled ? "/ENABLE" : "/DISABLE"],
3291
3445
  { stdio: "ignore" }
@@ -3693,7 +3847,7 @@ function registerConfig(program2) {
3693
3847
  }
3694
3848
 
3695
3849
  // src/commands/doctor.ts
3696
- import { execFileSync as execFileSync9 } from "child_process";
3850
+ import { execFileSync as execFileSync10 } from "child_process";
3697
3851
  import { request as request5 } from "undici";
3698
3852
  function registerDoctor(program2) {
3699
3853
  program2.command("doctor").description("Diagnose your CLI setup").action(async () => {
@@ -3741,7 +3895,7 @@ function registerDoctor(program2) {
3741
3895
  });
3742
3896
  }
3743
3897
  try {
3744
- const dirty = execFileSync9("git", ["status", "--porcelain"], {
3898
+ const dirty = execFileSync10("git", ["status", "--porcelain"], {
3745
3899
  cwd: root,
3746
3900
  encoding: "utf8"
3747
3901
  }).trim();
@@ -3765,7 +3919,7 @@ function registerDoctor(program2) {
3765
3919
  }
3766
3920
  function checkBinary(name, command) {
3767
3921
  try {
3768
- const out = execFileSync9(command, ["--version"], { encoding: "utf8" }).trim();
3922
+ const out = execFileSync10(command, ["--version"], { encoding: "utf8" }).trim();
3769
3923
  return { name, ok: true, detail: out.split("\n")[0] ?? out };
3770
3924
  } catch {
3771
3925
  return { name, ok: false, detail: `'${command}' not found on PATH` };
@@ -3773,7 +3927,7 @@ function checkBinary(name, command) {
3773
3927
  }
3774
3928
 
3775
3929
  // src/commands/version.ts
3776
- var CLI_VERSION = true ? "0.1.11" : "0.0.0-dev";
3930
+ var CLI_VERSION = true ? "0.1.13" : "0.0.0-dev";
3777
3931
  function registerVersion(program2) {
3778
3932
  program2.command("version").description("Print the CLI version").action(() => {
3779
3933
  process.stdout.write(CLI_VERSION + "\n");
@@ -3797,6 +3951,7 @@ registerTickets(program);
3797
3951
  registerTicket(program);
3798
3952
  registerWork(program);
3799
3953
  registerScan(program);
3954
+ registerPrTest(program);
3800
3955
  registerScheduledTask(program);
3801
3956
  registerRuns(program);
3802
3957
  registerConfig(program);