@towles/tool 0.0.95 → 0.0.103

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.
@@ -1,61 +1,50 @@
1
- import { Flags } from "@oclif/core";
1
+ import { defineCommand } from "citty";
2
2
  import consola from "consola";
3
3
  import { colors } from "consola/utils";
4
4
  import { Fzf } from "fzf";
5
5
  import prompts from "prompts";
6
6
  import type { Choice } from "prompts";
7
7
 
8
- import { BaseCommand } from "../base.js";
8
+ import { debugArg } from "../shared.js";
9
9
  import { buildIssueChoices, computeColumnLayout } from "../gh/branch.js";
10
10
  import { STEP_NAMES, fetchIssue, initConfig, runPipeline } from "../../lib/auto-claude/index.js";
11
11
  import type { StepName } from "../../lib/auto-claude/index.js";
12
12
  import { getIssues, isGithubCliInstalled } from "../../utils/git/gh-cli-wrapper.js";
13
13
  import { getTerminalColumns } from "../../utils/render.js";
14
14
 
15
- export default class AutoClaudeList extends BaseCommand {
16
- static override description = "Interactively pick an auto-claude issue to process";
17
-
18
- static override examples = [
19
- {
20
- description: "Browse auto-claude labeled issues",
21
- command: "<%= config.bin %> auto-claude list",
22
- },
23
- {
24
- description: "Pick an issue and run until plan step",
25
- command: "<%= config.bin %> auto-claude list --until plan",
26
- },
27
- ];
28
-
29
- static override flags = {
30
- ...BaseCommand.baseFlags,
31
- until: Flags.string({
32
- char: "u",
15
+ export default defineCommand({
16
+ meta: { name: "list", description: "Interactively pick an auto-claude issue to process" },
17
+ args: {
18
+ debug: debugArg,
19
+ until: {
20
+ type: "string" as const,
21
+ alias: "u",
33
22
  description: `Stop after this step (${STEP_NAMES.join(", ")})`,
34
- options: [...STEP_NAMES],
35
- }),
36
- label: Flags.string({
23
+ },
24
+ label: {
25
+ type: "string" as const,
37
26
  description: "Trigger label (default: auto-claude)",
38
- }),
39
- "main-branch": Flags.string({
27
+ },
28
+ "main-branch": {
29
+ type: "string" as const,
40
30
  description: "Override main branch detection",
41
- }),
42
- "scope-path": Flags.string({
31
+ },
32
+ "scope-path": {
33
+ type: "string" as const,
43
34
  description: "Path within repo to scope work (default: .)",
44
- }),
45
- };
46
-
47
- async run(): Promise<void> {
48
- const { flags } = await this.parse(AutoClaudeList);
49
-
35
+ },
36
+ },
37
+ async run({ args }) {
50
38
  const cfg = await initConfig({
51
- triggerLabel: flags.label,
52
- mainBranch: flags["main-branch"],
53
- scopePath: flags["scope-path"],
39
+ triggerLabel: args.label,
40
+ mainBranch: args["main-branch"],
41
+ scopePath: args["scope-path"],
54
42
  });
55
43
 
56
44
  const cliInstalled = await isGithubCliInstalled();
57
45
  if (!cliInstalled) {
58
- this.error("GitHub CLI (gh) is not installed");
46
+ consola.error("GitHub CLI (gh) is not installed");
47
+ process.exit(1);
59
48
  }
60
49
 
61
50
  const issues = await getIssues({ cwd: process.cwd(), label: cfg.triggerLabel });
@@ -89,7 +78,7 @@ export default class AutoClaudeList extends BaseCommand {
89
78
  {
90
79
  onCancel: () => {
91
80
  consola.info(colors.dim("Canceled"));
92
- this.exit(0);
81
+ process.exit(0);
93
82
  },
94
83
  },
95
84
  );
@@ -101,14 +90,15 @@ export default class AutoClaudeList extends BaseCommand {
101
90
 
102
91
  const ctx = await fetchIssue(result.issueNumber);
103
92
  if (!ctx) {
104
- this.error(`Could not fetch issue #${result.issueNumber}`);
93
+ consola.error(`Could not fetch issue #${result.issueNumber}`);
94
+ process.exit(1);
105
95
  }
106
96
 
107
- const untilStep = flags.until as StepName | undefined;
97
+ const untilStep = args.until as StepName | undefined;
108
98
  await runPipeline(ctx, untilStep);
109
99
  } catch (e) {
110
100
  consola.error(e);
111
- this.exit(1);
101
+ process.exit(1);
112
102
  }
113
- }
114
- }
103
+ },
104
+ });
@@ -5,6 +5,8 @@ import { tmpdir } from "node:os";
5
5
  import consola from "consola";
6
6
  import { describe, it, expect, vi, beforeEach } from "vitest";
7
7
 
8
+ import type { Mock } from "vitest";
9
+
8
10
  import type { ExecSafeFn } from "../../lib/auto-claude/labels.js";
9
11
  import { LABELS } from "../../lib/auto-claude/labels.js";
10
12
  import { retryIssues } from "./retry.js";
@@ -12,15 +14,17 @@ import { retryIssues } from "./retry.js";
12
14
  // Suppress consola output during tests
13
15
  consola.level = -999;
14
16
 
15
- const mockExecSafe: ExecSafeFn = vi.fn().mockResolvedValue({ stdout: "", ok: true });
17
+ const mockExecSafe = vi.fn().mockResolvedValue({ stdout: "", ok: true }) as Mock & ExecSafeFn;
16
18
 
17
19
  function getGhEditCalls() {
18
- return vi
19
- .mocked(mockExecSafe)
20
- .mock.calls.filter(
21
- ([cmd, args]) => cmd === "gh" && args?.[0] === "issue" && args?.[1] === "edit",
20
+ return mockExecSafe.mock.calls
21
+ .filter(
22
+ (call: unknown[]) =>
23
+ call[0] === "gh" &&
24
+ (call[1] as string[])?.[0] === "issue" &&
25
+ (call[1] as string[])?.[1] === "edit",
22
26
  )
23
- .map(([, args]) => args as string[]);
27
+ .map((call: unknown[]) => call[1] as string[]);
24
28
  }
25
29
 
26
30
  describe("retryIssues", () => {
@@ -1,12 +1,12 @@
1
1
  import { rmSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
- import { Flags } from "@oclif/core";
4
+ import { defineCommand } from "citty";
5
5
  import consola from "consola";
6
6
  import { colors } from "consola/utils";
7
7
  import prompts from "prompts";
8
8
 
9
- import { BaseCommand } from "../base.js";
9
+ import { debugArg } from "../shared.js";
10
10
  import { initConfig } from "../../lib/auto-claude/index.js";
11
11
  import { LABELS, removeLabel, setLabel } from "../../lib/auto-claude/labels.js";
12
12
  import type { ExecSafeFn } from "../../lib/auto-claude/labels.js";
@@ -47,54 +47,41 @@ export async function retryIssues(
47
47
  return selected.length;
48
48
  }
49
49
 
50
- export default class AutoClaudeRetry extends BaseCommand {
51
- static override description = "Retry failed auto-claude issues by swapping labels";
52
-
53
- static override examples = [
54
- {
55
- description: "Interactively pick failed issues to retry",
56
- command: "<%= config.bin %> auto-claude retry",
57
- },
58
- {
59
- description: "Retry a specific issue",
60
- command: "<%= config.bin %> auto-claude retry --issue 42",
61
- },
62
- {
63
- description: "Retry and clean local artifacts",
64
- command: "<%= config.bin %> auto-claude retry --issue 42 --clean",
65
- },
66
- ];
67
-
68
- static override flags = {
69
- ...BaseCommand.baseFlags,
70
- issue: Flags.integer({
71
- char: "i",
50
+ export default defineCommand({
51
+ meta: { name: "retry", description: "Retry failed auto-claude issues by swapping labels" },
52
+ args: {
53
+ debug: debugArg,
54
+ issue: {
55
+ type: "string" as const,
56
+ alias: "i",
72
57
  description: "Issue number to retry",
73
- }),
74
- clean: Flags.boolean({
58
+ },
59
+ clean: {
60
+ type: "boolean" as const,
75
61
  description: "Delete local .auto-claude/issue-{N}/ artifacts",
76
62
  default: false,
77
- }),
78
- };
79
-
80
- async run(): Promise<void> {
81
- const { flags } = await this.parse(AutoClaudeRetry);
63
+ },
64
+ },
65
+ async run({ args }) {
66
+ const issueNumber = args.issue ? Number(args.issue) : undefined;
82
67
 
83
68
  const cfg = await initConfig();
84
69
 
85
70
  const cliInstalled = await isGithubCliInstalled();
86
71
  if (!cliInstalled) {
87
- this.error("GitHub CLI (gh) is not installed");
72
+ consola.error("GitHub CLI (gh) is not installed");
73
+ process.exit(1);
88
74
  }
89
75
 
90
76
  const failedIssues = await getIssues({ cwd: process.cwd(), label: LABELS.failed });
91
77
 
92
78
  let selected: Issue[];
93
79
 
94
- if (flags.issue) {
95
- const match = failedIssues.find((i) => i.number === flags.issue);
80
+ if (issueNumber) {
81
+ const match = failedIssues.find((i) => i.number === issueNumber);
96
82
  if (!match) {
97
- this.error(`Issue #${flags.issue} not found with '${LABELS.failed}' label`);
83
+ consola.error(`Issue #${issueNumber} not found with '${LABELS.failed}' label`);
84
+ process.exit(1);
98
85
  }
99
86
  selected = [match];
100
87
  } else {
@@ -122,7 +109,7 @@ export default class AutoClaudeRetry extends BaseCommand {
122
109
  {
123
110
  onCancel: () => {
124
111
  consola.info(colors.dim("Canceled"));
125
- this.exit(0);
112
+ process.exit(0);
126
113
  },
127
114
  },
128
115
  );
@@ -135,7 +122,7 @@ export default class AutoClaudeRetry extends BaseCommand {
135
122
  selected = failedIssues.filter((i) => result.selected.includes(i.number));
136
123
  }
137
124
 
138
- const count = await retryIssues(cfg.repo, cfg.triggerLabel, selected, flags.clean);
125
+ const count = await retryIssues(cfg.repo, cfg.triggerLabel, selected, args.clean as boolean);
139
126
  consola.box(`Retried ${count} issue(s)`);
140
- }
141
- }
127
+ },
128
+ });
@@ -1,6 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
+ import { defineCommand } from "citty";
4
5
  import consola from "consola";
5
6
  import { colors } from "consola/utils";
6
7
 
@@ -8,7 +9,7 @@ import type { Issue } from "../../utils/git/gh-cli-wrapper.js";
8
9
  import { getIssues, isGithubCliInstalled } from "../../utils/git/gh-cli-wrapper.js";
9
10
  import { ARTIFACTS } from "../../lib/auto-claude/prompt-templates/index.js";
10
11
  import { LABELS } from "../../lib/auto-claude/labels.js";
11
- import { BaseCommand } from "../base.js";
12
+ import { debugArg } from "../shared.js";
12
13
 
13
14
  /** All labels that indicate an issue is part of the auto-claude pipeline. */
14
15
  const ALL_AC_LABELS = ["auto-claude", ...Object.values(LABELS)] as const;
@@ -86,22 +87,14 @@ export async function fetchAllAcIssues(cwd: string): Promise<Issue[]> {
86
87
  return [...issueMap.values()].sort((a, b) => a.number - b.number);
87
88
  }
88
89
 
89
- export default class AutoClaudeStatus extends BaseCommand {
90
- static override description = "Show pipeline status for auto-claude issues";
91
-
92
- static override aliases = ["ac:status"];
93
-
94
- static override examples = [
95
- {
96
- description: "Show status of all auto-claude issues",
97
- command: "<%= config.bin %> auto-claude status",
98
- },
99
- ];
100
-
101
- async run(): Promise<void> {
90
+ export default defineCommand({
91
+ meta: { name: "status", description: "Show pipeline status for auto-claude issues" },
92
+ args: { debug: debugArg },
93
+ async run() {
102
94
  const cliInstalled = await isGithubCliInstalled();
103
95
  if (!cliInstalled) {
104
- this.error("GitHub CLI (gh) is not installed");
96
+ consola.error("GitHub CLI (gh) is not installed");
97
+ process.exit(1);
105
98
  }
106
99
 
107
100
  const cwd = process.cwd();
@@ -119,5 +112,5 @@ export default class AutoClaudeStatus extends BaseCommand {
119
112
  const artifacts = checkArtifacts(issue.number, cwd);
120
113
  consola.log(formatIssueStatus(issue, artifacts));
121
114
  }
122
- }
123
- }
115
+ },
116
+ });
@@ -1,15 +1,9 @@
1
- /**
2
- * Integration tests for oclif config command
3
- * Note: consola outputs to stderr with different log levels
4
- */
5
1
  import { describe, it, expect } from "vitest";
6
- import { runCommand } from "@oclif/test";
2
+ import { runCommand } from "citty";
7
3
 
8
4
  describe("config command", () => {
9
- it("runs config and outputs settings info", async () => {
10
- const { stderr } = await runCommand(["config"]);
11
- // consola.warn outputs captured in stderr
12
- expect(stderr).toContain("User Config");
13
- expect(stderr).toContain("Working Directory");
5
+ it("runs config without throwing", async () => {
6
+ const { default: configCmd } = await import("./config.js");
7
+ await expect(runCommand(configCmd, { rawArgs: [] })).resolves.toBeDefined();
14
8
  });
15
9
  });
@@ -1,42 +1,28 @@
1
+ import { defineCommand } from "citty";
1
2
  import consola from "consola";
2
- import { BaseCommand } from "./base.js";
3
+ import { withSettings, debugArg } from "./shared.js";
3
4
 
4
- /**
5
- * Display current configuration settings
6
- */
7
- export default class Config extends BaseCommand {
8
- static override description = "Display current configuration settings";
9
-
10
- static override examples = [
11
- { description: "Display configuration", command: "<%= config.bin %> <%= command.id %>" },
12
- { description: "Use alias", command: "<%= config.bin %> cfg" },
13
- ];
14
-
15
- async run(): Promise<void> {
16
- await this.parse(Config);
5
+ export default defineCommand({
6
+ meta: { name: "config", description: "Display current configuration settings" },
7
+ args: { debug: debugArg },
8
+ async run({ args }) {
9
+ const { settingsFile, settings } = await withSettings(args.debug);
17
10
 
18
11
  consola.info("Configuration");
19
12
  consola.log("");
20
13
 
21
- consola.info(`Settings File: ${this.settingsFile.path}`);
14
+ consola.info(`Settings File: ${settingsFile.path}`);
22
15
  consola.log("");
23
16
 
24
17
  consola.warn("User Config:");
25
- consola.log(` Daily Path Template: ${this.userSettings.journalSettings.dailyPathTemplate}`);
26
- consola.log(
27
- ` Meeting Path Template: ${this.userSettings.journalSettings.meetingPathTemplate}`,
28
- );
29
- consola.log(` Note Path Template: ${this.userSettings.journalSettings.notePathTemplate}`);
30
- consola.log(` Editor: ${this.userSettings.preferredEditor}`);
18
+ consola.log(` Daily Path Template: ${settings.journalSettings.dailyPathTemplate}`);
19
+ consola.log(` Meeting Path Template: ${settings.journalSettings.meetingPathTemplate}`);
20
+ consola.log(` Note Path Template: ${settings.journalSettings.notePathTemplate}`);
21
+ consola.log(` Editor: ${settings.preferredEditor}`);
31
22
  consola.log("");
32
23
 
33
24
  consola.warn("Working Directory:");
34
25
  consola.log(` ${process.cwd()}`);
35
26
  consola.log("");
36
-
37
- consola.info("Shell Completions:");
38
- consola.log(" Run `tt completion` to generate shell completions");
39
- consola.log(" Bash/Zsh: tt completion >> ~/.bashrc (or ~/.zshrc)");
40
- consola.log(" Fish: tt completion > ~/.config/fish/completions/tt.fish");
41
- }
42
- }
27
+ },
28
+ });