@towles/tool 0.0.20 → 0.0.41

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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE.md +9 -10
  3. package/README.md +121 -78
  4. package/bin/run.ts +5 -0
  5. package/package.json +63 -53
  6. package/patches/prompts.patch +34 -0
  7. package/src/commands/base.ts +42 -0
  8. package/src/commands/config.test.ts +15 -0
  9. package/src/commands/config.ts +43 -0
  10. package/src/commands/doctor.ts +133 -0
  11. package/src/commands/gh/branch-clean.ts +110 -0
  12. package/src/commands/gh/branch.test.ts +124 -0
  13. package/src/commands/gh/branch.ts +132 -0
  14. package/src/commands/gh/pr.ts +168 -0
  15. package/src/commands/index.ts +55 -0
  16. package/src/commands/install.ts +148 -0
  17. package/src/commands/journal/daily-notes.ts +66 -0
  18. package/src/commands/journal/meeting.ts +83 -0
  19. package/src/commands/journal/note.ts +83 -0
  20. package/src/commands/journal/utils.ts +399 -0
  21. package/src/commands/observe/graph.test.ts +89 -0
  22. package/src/commands/observe/graph.ts +1640 -0
  23. package/src/commands/observe/report.ts +166 -0
  24. package/src/commands/observe/session.ts +385 -0
  25. package/src/commands/observe/setup.ts +180 -0
  26. package/src/commands/observe/status.ts +146 -0
  27. package/src/commands/ralph/lib/execution.ts +302 -0
  28. package/src/commands/ralph/lib/formatter.ts +298 -0
  29. package/src/commands/ralph/lib/index.ts +4 -0
  30. package/src/commands/ralph/lib/marker.ts +108 -0
  31. package/src/commands/ralph/lib/state.ts +191 -0
  32. package/src/commands/ralph/marker/create.ts +23 -0
  33. package/src/commands/ralph/plan.ts +73 -0
  34. package/src/commands/ralph/progress.ts +44 -0
  35. package/src/commands/ralph/ralph.test.ts +673 -0
  36. package/src/commands/ralph/run.ts +408 -0
  37. package/src/commands/ralph/task/add.ts +105 -0
  38. package/src/commands/ralph/task/done.ts +73 -0
  39. package/src/commands/ralph/task/list.test.ts +48 -0
  40. package/src/commands/ralph/task/list.ts +110 -0
  41. package/src/commands/ralph/task/remove.ts +62 -0
  42. package/src/config/context.ts +7 -0
  43. package/src/config/settings.ts +155 -0
  44. package/src/constants.ts +3 -0
  45. package/src/types/journal.ts +16 -0
  46. package/src/utils/anthropic/types.ts +158 -0
  47. package/src/utils/date-utils.test.ts +96 -0
  48. package/src/utils/date-utils.ts +54 -0
  49. package/src/utils/exec.ts +8 -0
  50. package/src/utils/git/gh-cli-wrapper.test.ts +14 -0
  51. package/src/utils/git/gh-cli-wrapper.ts +54 -0
  52. package/src/utils/git/git-wrapper.test.ts +26 -0
  53. package/src/utils/git/git-wrapper.ts +15 -0
  54. package/src/utils/git/git.ts +25 -0
  55. package/src/utils/render.test.ts +71 -0
  56. package/src/utils/render.ts +34 -0
  57. package/dist/index.d.mts +0 -1
  58. package/dist/index.mjs +0 -805
@@ -0,0 +1,54 @@
1
+ import stripAnsi from "strip-ansi";
2
+ import { x } from "tinyexec";
3
+
4
+ export const isGithubCliInstalled = async (): Promise<boolean> => {
5
+ try {
6
+ const proc = await x(`gh`, ["--version"]);
7
+ return proc.stdout.indexOf("https://github.com/cli/cli") > 0;
8
+ } catch (e) {
9
+ return false;
10
+ }
11
+ };
12
+
13
+ export interface Issue {
14
+ labels: {
15
+ name: string;
16
+ color: string;
17
+ }[];
18
+ number: number;
19
+ title: string;
20
+ state: string;
21
+ }
22
+
23
+ export const getIssues = async ({
24
+ assignedToMe,
25
+ cwd,
26
+ }: {
27
+ assignedToMe: boolean;
28
+ cwd: string;
29
+ }): Promise<Issue[]> => {
30
+ let issues: Issue[] = [];
31
+
32
+ const flags = ["issue", "list", "--json", "labels,number,title,state"];
33
+
34
+ if (assignedToMe) {
35
+ flags.push("--assignee");
36
+ flags.push("@me");
37
+ }
38
+
39
+ //console.log('Current working directory:', cwd.stdout.trim())
40
+
41
+ const result = await x(`gh`, flags);
42
+ // Setting NO_COLOR=1 didn't remove colors so had to use stripAnsi
43
+ const stripped = stripAnsi(result.stdout);
44
+
45
+ try {
46
+ issues = JSON.parse(stripped);
47
+ } catch {
48
+ throw new Error(
49
+ `Failed to parse GitHub CLI output as JSON. Raw output: ${stripped.slice(0, 200)}${stripped.length > 200 ? "..." : ""}`,
50
+ );
51
+ }
52
+
53
+ return issues;
54
+ };
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { isGitDirectory } from "./git-wrapper";
3
+
4
+ describe.skip("git-wrapper", () => {
5
+ it("isGitDirectory", async () => {
6
+ const result = await isGitDirectory();
7
+ expect(result).toBe(true);
8
+ });
9
+
10
+ // it('getMergedBranches', async () => {
11
+ // const result = await getMergedBranches()
12
+
13
+ // if (result.length > 0) // may not have branches to cleanup so just check for no errors
14
+ // expect(result[0].trim()).toBe(result[0])
15
+ // })
16
+
17
+ // it('getLocalBranchNames', async () => {
18
+ // const result = await getLocalBranchNames()
19
+ // expect(result.includes('main')).toBeDefined()
20
+ // })
21
+
22
+ // it('getDefaultMainBranchName', async () => {
23
+ // const result = await getDefaultMainBranchName()
24
+ // expect(result).toBe('main')
25
+ // })
26
+ });
@@ -0,0 +1,15 @@
1
+ import { exec } from "tinyexec";
2
+
3
+ export const isGitDirectory = async (): Promise<boolean> => {
4
+ try {
5
+ const result = await exec(`git`, ["status"]);
6
+ return result.stdout.includes("On branch");
7
+ } catch (e) {
8
+ return false;
9
+ }
10
+ };
11
+
12
+ export const createBranch = async ({ branchName }: { branchName: string }): Promise<string> => {
13
+ const result = await exec(`git`, ["checkout", "-b", branchName]);
14
+ return result.stdout;
15
+ };
@@ -0,0 +1,25 @@
1
+ // unJs/changelogen has some nice utilities for git
2
+ import { execCommand } from "../exec";
3
+
4
+ // using logic from https://github.com/unjs/changelogen/blob/main/src/git.ts
5
+
6
+ export async function getGitDiff(cwd: string): Promise<string> {
7
+ // https://git-scm.com/docs/pretty-formats
8
+ let r = execCommand(
9
+ // `git --no-pager log "${from ? `${from}...` : ''}${to}" `,
10
+ // using --cached because staged didn't work on MacOS
11
+ `git --no-pager diff --cached`,
12
+ // --name-status
13
+ // --pretty="----%n%s|%h|%an|%ae%n%b"
14
+ cwd,
15
+ );
16
+
17
+ // Limit output to 1000 lines maximum
18
+ const lines = r.split("\n");
19
+ if (lines.length > 1000) {
20
+ r = lines.slice(0, 1000).join("\n");
21
+ r += `\n\n[Output truncated to 1000 lines]`;
22
+ }
23
+
24
+ return r;
25
+ }
@@ -0,0 +1,71 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { printWithHexColor } from "./render";
3
+
4
+ describe("printWithHexColor", () => {
5
+ it("should handle hex colors with # prefix", () => {
6
+ const result = printWithHexColor({ msg: "test", hex: "#ff0000" });
7
+ expect(result).toBe("\x1B[38;2;255;0;0mtest\x1B[0m");
8
+ });
9
+
10
+ it("should handle hex colors without # prefix", () => {
11
+ const result = printWithHexColor({ msg: "test", hex: "ff0000" });
12
+ expect(result).toBe("\x1B[38;2;255;0;0mtest\x1B[0m");
13
+ });
14
+
15
+ it("should handle green color correctly", () => {
16
+ const result = printWithHexColor({ msg: "green", hex: "#00ff00" });
17
+ expect(result).toBe("\x1B[38;2;0;255;0mgreen\x1B[0m");
18
+ });
19
+
20
+ it("should handle blue color correctly", () => {
21
+ const result = printWithHexColor({ msg: "blue", hex: "0000ff" });
22
+ expect(result).toBe("\x1B[38;2;0;0;255mblue\x1B[0m");
23
+ });
24
+
25
+ it("should handle mixed RGB values", () => {
26
+ const result = printWithHexColor({ msg: "purple", hex: "#800080" });
27
+ expect(result).toBe("\x1B[38;2;128;0;128mpurple\x1B[0m");
28
+ });
29
+
30
+ it("should handle GitHub-style label colors", () => {
31
+ // GitHub red label color
32
+ const result = printWithHexColor({ msg: "bug", hex: "d73a49" });
33
+ expect(result).toBe("\x1B[38;2;215;58;73mbug\x1B[0m");
34
+ });
35
+
36
+ it("should handle lowercase hex values", () => {
37
+ const result = printWithHexColor({ msg: "test", hex: "abc123" });
38
+ expect(result).toBe("\x1B[38;2;171;193;35mtest\x1B[0m");
39
+ });
40
+
41
+ it("should handle uppercase hex values", () => {
42
+ const result = printWithHexColor({ msg: "test", hex: "ABC123" });
43
+ expect(result).toBe("\x1B[38;2;171;193;35mtest\x1B[0m");
44
+ });
45
+
46
+ it("should handle empty message", () => {
47
+ const result = printWithHexColor({ msg: "", hex: "#ff0000" });
48
+ expect(result).toBe("\x1B[38;2;255;0;0m\x1B[0m");
49
+ });
50
+
51
+ it("should handle message with spaces", () => {
52
+ const result = printWithHexColor({ msg: "hello world", hex: "#ffffff" });
53
+ expect(result).toBe("\x1B[38;2;255;255;255mhello world\x1B[0m");
54
+ });
55
+
56
+ it("should handle black color (000000)", () => {
57
+ const result = printWithHexColor({ msg: "black", hex: "000000" });
58
+ expect(result).toBe("\x1B[38;2;0;0;0mblack\x1B[0m");
59
+ });
60
+
61
+ it("should handle white color (ffffff)", () => {
62
+ const result = printWithHexColor({ msg: "white", hex: "ffffff" });
63
+ expect(result).toBe("\x1B[38;2;255;255;255mwhite\x1B[0m");
64
+ });
65
+
66
+ it("should preserve message content exactly", () => {
67
+ const specialMessage = "special-chars_123!@#";
68
+ const result = printWithHexColor({ msg: specialMessage, hex: "#123456" });
69
+ expect(result).toBe(`\x1B[38;2;18;52;86m${specialMessage}\x1B[0m`);
70
+ });
71
+ });
@@ -0,0 +1,34 @@
1
+ import { colors } from "consola/utils";
2
+
3
+ export const getTerminalColumns = () => process.stdout?.columns || 80;
4
+
5
+ export const limitText = (text: string, maxWidth: number): string => {
6
+ if (text.length <= maxWidth) return text;
7
+ // subtract 1 so room for the ellipsis
8
+ return `${text.slice(0, maxWidth - 1)}${colors.dim("…")}`;
9
+ };
10
+
11
+ /**
12
+ * Convert hex color to RGB values
13
+ */
14
+ function hexToRgb(hex: string): { r: number; g: number; b: number } {
15
+ const cleanHex = hex.replace("#", "");
16
+ const r = Number.parseInt(cleanHex.slice(0, 2), 16);
17
+ const g = Number.parseInt(cleanHex.slice(2, 4), 16);
18
+ const b = Number.parseInt(cleanHex.slice(4, 6), 16);
19
+ return { r, g, b };
20
+ }
21
+
22
+ /**
23
+ * Apply hex color to text using ANSI 24-bit color codes
24
+ */
25
+ export function printWithHexColor({ msg, hex }: { msg: string; hex: string }): string {
26
+ const colorWithHex = hex.startsWith("#") ? hex : `#${hex}`;
27
+ const { r, g, b } = hexToRgb(colorWithHex);
28
+
29
+ // Use ANSI 24-bit color: \x1B[38;2;r;g;bm for foreground color
30
+ const colorStart = `\x1B[38;2;${r};${g};${b}m`;
31
+ const colorEnd = "\x1B[0m"; // Reset color
32
+
33
+ return `${colorStart}${msg}${colorEnd}`;
34
+ }
package/dist/index.d.mts DELETED
@@ -1 +0,0 @@
1
-