@inteeka/task-cli 0.1.3 → 0.1.5

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
@@ -416,6 +416,9 @@ function sleep(ms) {
416
416
 
417
417
  // src/api/client.ts
418
418
  import { request as request3 } from "undici";
419
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
420
+ import { homedir as homedir2 } from "os";
421
+ import { join as join2 } from "path";
419
422
 
420
423
  // src/auth/refresh.ts
421
424
  import { request as request2 } from "undici";
@@ -480,6 +483,32 @@ async function manualRefresh() {
480
483
  }
481
484
 
482
485
  // src/api/client.ts
486
+ async function dumpServerError(method, path, status, rawBody, headers) {
487
+ try {
488
+ const dir = join2(homedir2(), ".cache", "task", "api-debug");
489
+ await mkdir2(dir, { recursive: true });
490
+ const file = join2(dir, `${Date.now()}-${status}.log`);
491
+ const safeHeaders = { ...headers };
492
+ delete safeHeaders["Authorization"];
493
+ delete safeHeaders["authorization"];
494
+ await writeFile2(
495
+ file,
496
+ [
497
+ `## ${method} ${path}`,
498
+ `## status ${status}`,
499
+ "",
500
+ "## request_headers",
501
+ JSON.stringify(safeHeaders, null, 2),
502
+ "",
503
+ "## response_body",
504
+ rawBody || "(empty)"
505
+ ].join("\n")
506
+ );
507
+ process.stderr.write(` (server response saved to ${file})
508
+ `);
509
+ } catch {
510
+ }
511
+ }
483
512
  async function apiCall(method, path, options = {}) {
484
513
  const authenticated = options.authenticated !== false;
485
514
  let creds = null;
@@ -537,19 +566,32 @@ async function apiCall(method, path, options = {}) {
537
566
  );
538
567
  }
539
568
  const status = res.statusCode;
540
- let parsed;
569
+ let rawBody;
541
570
  try {
542
- parsed = await res.body.json();
571
+ rawBody = await res.body.text();
543
572
  } catch {
544
- parsed = void 0;
573
+ rawBody = "";
574
+ }
575
+ let parsed;
576
+ if (rawBody) {
577
+ try {
578
+ parsed = JSON.parse(rawBody);
579
+ } catch {
580
+ parsed = void 0;
581
+ }
545
582
  }
546
583
  if (status >= 200 && status < 300) {
547
584
  const body = parsed;
548
585
  return { ok: true, status, data: body?.data ?? parsed };
549
586
  }
587
+ if (status >= 500) {
588
+ await dumpServerError(method, path, status, rawBody, headers).catch(() => void 0);
589
+ }
550
590
  const errBody = parsed;
551
591
  const code = errBody?.error?.code ?? `HTTP_${status}`;
552
- const message = errBody?.error?.message ?? `Request failed with status ${status}`;
592
+ const requestId = errBody?.error?.request_id;
593
+ const baseMessage = errBody?.error?.message ?? `Request failed with status ${status}`;
594
+ const message = requestId ? `${baseMessage} (request_id: ${requestId})` : baseMessage;
553
595
  if (status === 401 && (code === "UNAUTHORIZED" || code === "TOKEN_EXPIRED" || code === "INVALID_GRANT")) {
554
596
  await clearCredentials();
555
597
  throw new CliError(
@@ -591,10 +633,10 @@ async function apiCallOrThrow(method, path, options = {}) {
591
633
  }
592
634
 
593
635
  // src/config/local-config.ts
594
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
595
- import { homedir as homedir2 } from "os";
596
- import { dirname as dirname2, join as join2 } from "path";
597
- var CONFIG_PATH = join2(homedir2(), ".config", "task", "config.json");
636
+ import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
637
+ import { homedir as homedir3 } from "os";
638
+ import { dirname as dirname2, join as join3 } from "path";
639
+ var CONFIG_PATH = join3(homedir3(), ".config", "task", "config.json");
598
640
  var DEFAULT_CONFIG = {
599
641
  api_url: process.env["TASK_API_URL"] ?? "http://localhost:3400",
600
642
  default_project: null,
@@ -614,8 +656,8 @@ async function readLocalConfig() {
614
656
  }
615
657
  }
616
658
  async function writeLocalConfig(config) {
617
- await mkdir2(dirname2(CONFIG_PATH), { recursive: true });
618
- await writeFile2(CONFIG_PATH, JSON.stringify(config, null, 2));
659
+ await mkdir3(dirname2(CONFIG_PATH), { recursive: true });
660
+ await writeFile3(CONFIG_PATH, JSON.stringify(config, null, 2));
619
661
  }
620
662
  async function setConfigValue(key, value) {
621
663
  const cfg = await readLocalConfig();
@@ -734,14 +776,14 @@ function registerAuthRefresh(program2) {
734
776
  }
735
777
 
736
778
  // src/commands/link.ts
737
- import { readFile as readFile4, writeFile as writeFile4, appendFile, access } from "fs/promises";
779
+ import { readFile as readFile4, writeFile as writeFile5, appendFile, access } from "fs/promises";
738
780
  import { constants as fsConstants } from "fs";
739
- import { join as join4 } from "path";
781
+ import { join as join5 } from "path";
740
782
  import inquirer from "inquirer";
741
783
 
742
784
  // src/config/project.ts
743
- import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3, unlink as unlink2 } from "fs/promises";
744
- import { dirname as dirname3, join as join3, resolve } from "path";
785
+ import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
786
+ import { dirname as dirname3, join as join4, resolve } from "path";
745
787
  import { execSync } from "child_process";
746
788
  function findRepoRoot(start = process.cwd()) {
747
789
  try {
@@ -752,7 +794,7 @@ function findRepoRoot(start = process.cwd()) {
752
794
  }
753
795
  }
754
796
  function configPath(repoRoot) {
755
- return join3(repoRoot ?? findRepoRoot(), ".task", "config.json");
797
+ return join4(repoRoot ?? findRepoRoot(), ".task", "config.json");
756
798
  }
757
799
  async function readProjectConfig(repoRoot) {
758
800
  const path = configPath(repoRoot);
@@ -766,8 +808,8 @@ async function readProjectConfig(repoRoot) {
766
808
  }
767
809
  async function writeProjectConfig(config, repoRoot) {
768
810
  const path = configPath(repoRoot);
769
- await mkdir3(dirname3(path), { recursive: true });
770
- await writeFile3(path, JSON.stringify(config, null, 2));
811
+ await mkdir4(dirname3(path), { recursive: true });
812
+ await writeFile4(path, JSON.stringify(config, null, 2));
771
813
  }
772
814
  async function clearProjectConfig(repoRoot) {
773
815
  const path = configPath(repoRoot);
@@ -864,7 +906,7 @@ async function resolveProject(projects, opts) {
864
906
  return picked;
865
907
  }
866
908
  async function ensureGitignored(repoRoot) {
867
- const gitignorePath = join4(repoRoot, ".gitignore");
909
+ const gitignorePath = join5(repoRoot, ".gitignore");
868
910
  let existing = null;
869
911
  try {
870
912
  await access(gitignorePath, fsConstants.F_OK);
@@ -879,7 +921,7 @@ async function ensureGitignored(repoRoot) {
879
921
  await appendFile(gitignorePath, block);
880
922
  return "added";
881
923
  }
882
- await writeFile4(gitignorePath, "# task CLI link config\n.task/\n");
924
+ await writeFile5(gitignorePath, "# task CLI link config\n.task/\n");
883
925
  return "created";
884
926
  }
885
927
 
@@ -1108,9 +1150,9 @@ import inquirer2 from "inquirer";
1108
1150
 
1109
1151
  // src/agent/agent-service.ts
1110
1152
  import { spawn } from "child_process";
1111
- import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
1112
- import { homedir as homedir3 } from "os";
1113
- import { join as join5 } from "path";
1153
+ import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
1154
+ import { homedir as homedir4 } from "os";
1155
+ import { join as join6 } from "path";
1114
1156
 
1115
1157
  // src/agent/allowed-tools.ts
1116
1158
  var ALLOWED_TOOLS = CLI_ALLOWED_TOOLS;
@@ -1167,10 +1209,10 @@ async function runAgent(args) {
1167
1209
  let outputLogPath = null;
1168
1210
  let logHandle = null;
1169
1211
  if (args.silent) {
1170
- const dir = join5(homedir3(), ".cache", "task", "runs");
1171
- await mkdir4(dir, { recursive: true });
1172
- outputLogPath = join5(dir, `${args.runId}.log`);
1173
- await writeFile5(outputLogPath, "");
1212
+ const dir = join6(homedir4(), ".cache", "task", "runs");
1213
+ await mkdir5(dir, { recursive: true });
1214
+ outputLogPath = join6(dir, `${args.runId}.log`);
1215
+ await writeFile6(outputLogPath, "");
1174
1216
  const { createWriteStream } = await import("fs");
1175
1217
  logHandle = createWriteStream(outputLogPath, { flags: "a" });
1176
1218
  }
@@ -1706,9 +1748,9 @@ function autopilotExitCode(code, status) {
1706
1748
 
1707
1749
  // src/scan/llm.ts
1708
1750
  import { spawn as spawn2 } from "child_process";
1709
- import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
1710
- import { homedir as homedir4 } from "os";
1711
- import { join as join6 } from "path";
1751
+ import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
1752
+ import { homedir as homedir5 } from "os";
1753
+ import { join as join7 } from "path";
1712
1754
  var FIX_PROMPT_JSON_SCHEMA = {
1713
1755
  type: "object",
1714
1756
  required: ["summary", "suspected_files", "proposed_changes", "confidence"],
@@ -1772,7 +1814,6 @@ async function generateFixPromptJson(args) {
1772
1814
  ].join("\n");
1773
1815
  const cliArgs = [
1774
1816
  "--print",
1775
- "--bare",
1776
1817
  "--output-format",
1777
1818
  "json",
1778
1819
  "--tools",
@@ -1812,6 +1853,18 @@ async function generateFixPromptJson(args) {
1812
1853
  reject(new LlmGenerationError("aborted", "claude was aborted"));
1813
1854
  return;
1814
1855
  }
1856
+ const authFailure = detectAuthFailure(stdoutBuf);
1857
+ if (authFailure) {
1858
+ const dump = await maybeDumpDebug(args.ticketId, stdoutBuf, stderrBuf);
1859
+ reject(
1860
+ new LlmGenerationError(
1861
+ "non_zero_exit",
1862
+ `Claude is not logged in. Run \`claude /login\` once on this machine, then re-run \`task scan\`.${dump ? ` (raw output: ${dump})` : ""}`,
1863
+ dump ?? void 0
1864
+ )
1865
+ );
1866
+ return;
1867
+ }
1815
1868
  if (code !== 0) {
1816
1869
  const dump = await maybeDumpDebug(args.ticketId, stdoutBuf, stderrBuf);
1817
1870
  reject(
@@ -1848,6 +1901,19 @@ async function generateFixPromptJson(args) {
1848
1901
  child.stdin?.end();
1849
1902
  });
1850
1903
  }
1904
+ function detectAuthFailure(raw) {
1905
+ const trimmed = raw.trim();
1906
+ if (!trimmed) return false;
1907
+ try {
1908
+ const env = JSON.parse(trimmed);
1909
+ if (env.is_error === true && typeof env.result === "string") {
1910
+ const msg = env.result.toLowerCase();
1911
+ return msg.includes("not logged in") || msg.includes("please run /login") || msg.includes("please log in");
1912
+ }
1913
+ } catch {
1914
+ }
1915
+ return false;
1916
+ }
1851
1917
  function extractEnvelopeText(raw) {
1852
1918
  const trimmed = raw.trim();
1853
1919
  if (!trimmed) return raw;
@@ -1876,10 +1942,10 @@ function readEnvelopeTokens(raw, userPrompt, innerText) {
1876
1942
  async function maybeDumpDebug(ticketId, stdout, stderr) {
1877
1943
  if (!DEBUG && stdout.length === 0 && stderr.length === 0) return null;
1878
1944
  try {
1879
- const dir = join6(homedir4(), ".cache", "task", "scan-debug");
1880
- await mkdir5(dir, { recursive: true });
1881
- const path = join6(dir, `${ticketId}-${Date.now()}.log`);
1882
- await writeFile6(
1945
+ const dir = join7(homedir5(), ".cache", "task", "scan-debug");
1946
+ await mkdir6(dir, { recursive: true });
1947
+ const path = join7(dir, `${ticketId}-${Date.now()}.log`);
1948
+ await writeFile7(
1883
1949
  path,
1884
1950
  ["## ticket_id", ticketId, "", "## stdout", stdout, "", "## stderr", stderr].join("\n")
1885
1951
  );
@@ -2172,9 +2238,9 @@ import { randomUUID as randomUUID3 } from "crypto";
2172
2238
  import { platform as platform2 } from "os";
2173
2239
 
2174
2240
  // src/scheduler/launchd.ts
2175
- import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile7, unlink as unlink3, readdir } from "fs/promises";
2176
- import { homedir as homedir5 } from "os";
2177
- import { join as join7 } from "path";
2241
+ import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile8, unlink as unlink3, readdir } from "fs/promises";
2242
+ import { homedir as homedir6 } from "os";
2243
+ import { join as join8 } from "path";
2178
2244
  import { execFileSync as execFileSync5, spawn as spawn3 } from "child_process";
2179
2245
 
2180
2246
  // src/scheduler/cron-translate.ts
@@ -2276,14 +2342,14 @@ function expandField(field, min, max) {
2276
2342
  }
2277
2343
 
2278
2344
  // src/scheduler/launchd.ts
2279
- var PLIST_DIR = join7(homedir5(), "Library", "LaunchAgents");
2345
+ var PLIST_DIR = join8(homedir6(), "Library", "LaunchAgents");
2280
2346
  var LABEL_PREFIX = "com.inteeka.task.cli.";
2281
2347
  var SAFE_ID_RE = /^[0-9a-zA-Z._-]+$/;
2282
2348
  function plistPath(id) {
2283
2349
  if (!SAFE_ID_RE.test(id) || id.includes("..")) {
2284
2350
  throw new Error(`Refusing to compute plist path for unsafe id: ${id}`);
2285
2351
  }
2286
- return join7(PLIST_DIR, `${LABEL_PREFIX}${id}.plist`);
2352
+ return join8(PLIST_DIR, `${LABEL_PREFIX}${id}.plist`);
2287
2353
  }
2288
2354
  function buildPlist(entry) {
2289
2355
  const calendars = translateToLaunchd(entry.cron);
@@ -2319,9 +2385,9 @@ ${fields}
2319
2385
  ` <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>`,
2320
2386
  ` </dict>`,
2321
2387
  ` <key>StandardOutPath</key>`,
2322
- ` <string>${escapeXml(join7(homedir5(), ".cache", "task", "launchd-stdout.log"))}</string>`,
2388
+ ` <string>${escapeXml(join8(homedir6(), ".cache", "task", "launchd-stdout.log"))}</string>`,
2323
2389
  ` <key>StandardErrorPath</key>`,
2324
- ` <string>${escapeXml(join7(homedir5(), ".cache", "task", "launchd-stderr.log"))}</string>`,
2390
+ ` <string>${escapeXml(join8(homedir6(), ".cache", "task", "launchd-stderr.log"))}</string>`,
2325
2391
  !entry.enabled ? ` <key>Disabled</key>
2326
2392
  <true/>` : "",
2327
2393
  "</dict>",
@@ -2339,9 +2405,9 @@ function bootstrapDomain() {
2339
2405
  }
2340
2406
  var launchdAdapter = {
2341
2407
  async upsert(entry) {
2342
- await mkdir6(PLIST_DIR, { recursive: true });
2408
+ await mkdir7(PLIST_DIR, { recursive: true });
2343
2409
  const path = plistPath(entry.id);
2344
- await writeFile7(path, buildPlist(entry));
2410
+ await writeFile8(path, buildPlist(entry));
2345
2411
  try {
2346
2412
  execFileSync5("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
2347
2413
  } catch {
@@ -2370,7 +2436,7 @@ var launchdAdapter = {
2370
2436
  for (const file of ours) {
2371
2437
  const id = file.slice(LABEL_PREFIX.length, -".plist".length);
2372
2438
  try {
2373
- const xml = await readFile5(join7(PLIST_DIR, file), "utf8");
2439
+ const xml = await readFile5(join8(PLIST_DIR, file), "utf8");
2374
2440
  const cron = xml.match(/<key>StartCalendarInterval<\/key>[\s\S]*?<\/array>/)?.[0] ?? "";
2375
2441
  const command = xml.match(/<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/)?.[1] ?? "";
2376
2442
  const disabled = /<key>Disabled<\/key>\s*<true\/>/.test(xml);
@@ -2416,7 +2482,7 @@ var launchdAdapter = {
2416
2482
  }
2417
2483
  if (enabled) {
2418
2484
  xml = xml.replace(/\s*<key>Disabled<\/key>\s*<true\/>/, "");
2419
- await writeFile7(path, xml);
2485
+ await writeFile8(path, xml);
2420
2486
  try {
2421
2487
  execFileSync5("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
2422
2488
  } catch {
@@ -2428,7 +2494,7 @@ var launchdAdapter = {
2428
2494
  "</dict>\n</plist>",
2429
2495
  " <key>Disabled</key>\n <true/>\n</dict>\n</plist>"
2430
2496
  );
2431
- await writeFile7(path, xml);
2497
+ await writeFile8(path, xml);
2432
2498
  }
2433
2499
  try {
2434
2500
  execFileSync5("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
@@ -2781,10 +2847,10 @@ var unsupportedAdapter = {
2781
2847
  };
2782
2848
 
2783
2849
  // src/scheduler/registry.ts
2784
- import { mkdir as mkdir7, readFile as readFile6, writeFile as writeFile8 } from "fs/promises";
2785
- import { homedir as homedir6 } from "os";
2786
- import { dirname as dirname4, join as join8 } from "path";
2787
- var REGISTRY_PATH = join8(homedir6(), ".config", "task", "schedules.json");
2850
+ import { mkdir as mkdir8, readFile as readFile6, writeFile as writeFile9 } from "fs/promises";
2851
+ import { homedir as homedir7 } from "os";
2852
+ import { dirname as dirname4, join as join9 } from "path";
2853
+ var REGISTRY_PATH = join9(homedir7(), ".config", "task", "schedules.json");
2788
2854
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2789
2855
  function looksLikeRegistryRow(value) {
2790
2856
  if (!value || typeof value !== "object") return false;
@@ -2804,8 +2870,8 @@ async function readRegistry() {
2804
2870
  }
2805
2871
  }
2806
2872
  async function writeRegistry(rows) {
2807
- await mkdir7(dirname4(REGISTRY_PATH), { recursive: true });
2808
- await writeFile8(REGISTRY_PATH, JSON.stringify(rows, null, 2));
2873
+ await mkdir8(dirname4(REGISTRY_PATH), { recursive: true });
2874
+ await writeFile9(REGISTRY_PATH, JSON.stringify(rows, null, 2));
2809
2875
  }
2810
2876
  async function upsertRegistry(row) {
2811
2877
  if (!UUID_RE.test(row.id)) {
@@ -3045,8 +3111,8 @@ function stripAnsi(s) {
3045
3111
 
3046
3112
  // src/commands/runs.ts
3047
3113
  import { readFile as readFile7 } from "fs/promises";
3048
- import { homedir as homedir7 } from "os";
3049
- import { join as join9 } from "path";
3114
+ import { homedir as homedir8 } from "os";
3115
+ import { join as join10 } from "path";
3050
3116
  function registerRuns(program2) {
3051
3117
  const cmd = program2.command("runs").description("Inspect agentic CLI run history");
3052
3118
  cmd.command("list").description("List recent runs").option("--limit <n>", "Max rows", "50").option("--ticket <id>", "Filter by ticket").option("--schedule <id>", "Filter by schedule").action(async (opts) => {
@@ -3075,7 +3141,7 @@ function registerRuns(program2) {
3075
3141
  process.stdout.write(JSON.stringify(row, null, 2) + "\n");
3076
3142
  });
3077
3143
  cmd.command("logs <id>").description("Show captured agent output for a run, if available").action(async (id) => {
3078
- const localPath = join9(homedir7(), ".cache", "task", "runs", `${id}.log`);
3144
+ const localPath = join10(homedir8(), ".cache", "task", "runs", `${id}.log`);
3079
3145
  try {
3080
3146
  const text = await readFile7(localPath, "utf8");
3081
3147
  process.stdout.write(text);
@@ -3227,7 +3293,7 @@ function checkBinary(name, command) {
3227
3293
  }
3228
3294
 
3229
3295
  // src/commands/version.ts
3230
- var CLI_VERSION = true ? "0.1.3" : "0.0.0-dev";
3296
+ var CLI_VERSION = true ? "0.1.5" : "0.0.0-dev";
3231
3297
  function registerVersion(program2) {
3232
3298
  program2.command("version").description("Print the CLI version").action(() => {
3233
3299
  process.stdout.write(CLI_VERSION + "\n");