@towles/tool 0.0.20 → 0.0.48
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/{LICENSE.md → LICENSE} +1 -1
- package/README.md +86 -85
- package/bin/run.ts +5 -0
- package/package.json +84 -64
- package/patches/prompts.patch +34 -0
- package/src/commands/base.ts +27 -0
- package/src/commands/config.test.ts +15 -0
- package/src/commands/config.ts +44 -0
- package/src/commands/doctor.ts +136 -0
- package/src/commands/gh/branch-clean.ts +116 -0
- package/src/commands/gh/branch.test.ts +124 -0
- package/src/commands/gh/branch.ts +135 -0
- package/src/commands/gh/pr.ts +175 -0
- package/src/commands/graph-template.html +1214 -0
- package/src/commands/graph.test.ts +176 -0
- package/src/commands/graph.ts +970 -0
- package/src/commands/install.ts +154 -0
- package/src/commands/journal/daily-notes.ts +70 -0
- package/src/commands/journal/meeting.ts +89 -0
- package/src/commands/journal/note.ts +89 -0
- package/src/commands/ralph/plan/add.ts +75 -0
- package/src/commands/ralph/plan/done.ts +82 -0
- package/src/commands/ralph/plan/list.test.ts +48 -0
- package/src/commands/ralph/plan/list.ts +99 -0
- package/src/commands/ralph/plan/remove.ts +71 -0
- package/src/commands/ralph/run.test.ts +521 -0
- package/src/commands/ralph/run.ts +345 -0
- package/src/commands/ralph/show.ts +88 -0
- package/src/config/settings.ts +136 -0
- package/src/lib/journal/utils.ts +399 -0
- package/src/lib/ralph/execution.ts +292 -0
- package/src/lib/ralph/formatter.ts +238 -0
- package/src/lib/ralph/index.ts +4 -0
- package/src/lib/ralph/state.ts +166 -0
- package/src/types/journal.ts +16 -0
- package/src/utils/date-utils.test.ts +97 -0
- package/src/utils/date-utils.ts +54 -0
- package/src/utils/git/gh-cli-wrapper.test.ts +14 -0
- package/src/utils/git/gh-cli-wrapper.ts +54 -0
- package/src/utils/git/git-wrapper.test.ts +26 -0
- package/src/utils/git/git-wrapper.ts +15 -0
- package/src/utils/render.test.ts +71 -0
- package/src/utils/render.ts +34 -0
- package/dist/index.d.mts +0 -1
- package/dist/index.mjs +0 -805
package/{LICENSE.md → LICENSE}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 Chris Towles
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,116 +1,117 @@
|
|
|
1
1
|
# Towles Tool
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
-
[![bundle][bundle-src]][bundle-href]
|
|
6
|
-
[![JSDocs][jsdocs-src]][jsdocs-href]
|
|
7
|
-
[![License][license-src]][license-href]
|
|
3
|
+
CLI tool with autonomous task runner and quality-of-life commands for daily development.
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
## Features
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
- **Ralph** - Autonomous task runner with session forking and context reuse
|
|
8
|
+
- **Observability** - Token usage visualization with interactive treemaps
|
|
9
|
+
- **Git workflows** - Branch creation, PR generation, and cleanup
|
|
10
|
+
- **Journaling** - Daily notes, meeting notes, and general notes
|
|
11
|
+
- **Claude Code plugins** - Personal plugin marketplace for Claude Code integration
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Installation
|
|
14
14
|
|
|
15
|
-
###
|
|
16
|
-
- **Purpose**: Weekly files with daily sections for ongoing work and notes
|
|
17
|
-
- **File structure**: `YYYY/daily-notes/YYYY-MM-DD-week-log.md` (Monday's date)
|
|
18
|
-
- **Template**: Includes sections for Monday through Friday with date headers
|
|
19
|
-
- **Use case**: Regular daily journaling, work logs, scratch pad for notes
|
|
15
|
+
### Claude Code Plugin
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- **Use case**: Meeting preparation, note-taking, and action item tracking
|
|
17
|
+
```bash
|
|
18
|
+
claude plugin marketplace add ChrisTowles/towles-tool
|
|
19
|
+
claude plugin install tt@towles-tool
|
|
20
|
+
```
|
|
26
21
|
|
|
27
|
-
###
|
|
28
|
-
- **Purpose**: General-purpose notes with structured sections
|
|
29
|
-
- **File structure**: `YYYY/notes/YYYY-MM-DD-HHMM-note-[title].md`
|
|
30
|
-
- **Template**: Includes Summary, Details, and References sections
|
|
31
|
-
- **Use case**: Research notes, documentation, general information capture
|
|
22
|
+
### From Source
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/ChrisTowles/towles-tool.git
|
|
26
|
+
cd towles-tool
|
|
27
|
+
pnpm install
|
|
28
|
+
pnpm start # Run directly with tsx
|
|
29
|
+
```
|
|
37
30
|
|
|
38
|
-
##
|
|
39
|
-
- [x] Journal system - creates and opens markdown files with templates for daily-notes, meetings, and notes
|
|
40
|
-
- [ ] use claude code to generate git commits with multiple options for the commit message.
|
|
31
|
+
## CLI Commands
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
### Ralph (autonomous runner)
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
| Command | Description |
|
|
36
|
+
| --------------------------- | --------------------------------------------- |
|
|
37
|
+
| `tt ralph plan add <desc>` | Add task to plan |
|
|
38
|
+
| `tt ralph plan list` | View tasks |
|
|
39
|
+
| `tt ralph plan done <id>` | Mark task complete |
|
|
40
|
+
| `tt ralph plan remove <id>` | Remove task |
|
|
41
|
+
| `tt ralph run` | Run autonomous loop (auto-commits by default) |
|
|
42
|
+
| `tt ralph show` | Show plan with mermaid graph |
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
tt
|
|
44
|
+
### Observability
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
| Command | Description |
|
|
47
|
+
| ------------------------- | ------------------------------------- |
|
|
48
|
+
| `tt graph` | Generate HTML treemap of all sessions |
|
|
49
|
+
| `tt graph --session <id>` | Single session treemap |
|
|
50
|
+
| `tt graph --open` | Auto-open in browser |
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
Treemap colors indicate input/output token ratio: green <2:1, yellow 2-5:1, red >5:1.
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
pnpm remove --global @towles/tool
|
|
58
|
-
```
|
|
54
|
+
### Git
|
|
59
55
|
|
|
60
|
-
|
|
56
|
+
| Command | Alias | Description |
|
|
57
|
+
| -------------------- | ------- | ------------------------------- |
|
|
58
|
+
| `tt gh branch` | | Create branch from GitHub issue |
|
|
59
|
+
| `tt gh pr` | `tt pr` | Create pull request |
|
|
60
|
+
| `tt gh branch-clean` | | Delete merged branches |
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
### Journaling
|
|
63
|
+
|
|
64
|
+
| Command | Alias | Description |
|
|
65
|
+
| ------------------------ | ---------- | -------------------------------- |
|
|
66
|
+
| `tt journal daily-notes` | `tt today` | Weekly files with daily sections |
|
|
67
|
+
| `tt journal meeting` | `tt m` | Meeting notes |
|
|
68
|
+
| `tt journal note` | `tt n` | General notes |
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
70
|
+
### Utilities
|
|
71
|
+
|
|
72
|
+
| Command | Alias | Description |
|
|
73
|
+
| ------------ | -------- | ------------------------------ |
|
|
74
|
+
| `tt config` | `tt cfg` | Show configuration |
|
|
75
|
+
| `tt doctor` | | Check dependencies |
|
|
76
|
+
| `tt install` | | Configure Claude Code settings |
|
|
77
|
+
|
|
78
|
+
## Claude Code Plugin Skills
|
|
79
|
+
|
|
80
|
+
Available via `/tt:<command>`:
|
|
81
|
+
|
|
82
|
+
| Command | Description |
|
|
83
|
+
| ------------- | --------------------------------------------- |
|
|
84
|
+
| `/tt:commit` | AI-powered conventional commit messages |
|
|
85
|
+
| `/tt:plan` | Interview user and create implementation plan |
|
|
86
|
+
| `/tt:improve` | Explore codebase and suggest improvements |
|
|
87
|
+
| `/tt:refine` | Fix grammar/spelling in files |
|
|
88
|
+
|
|
89
|
+
## Development
|
|
86
90
|
|
|
87
91
|
```bash
|
|
88
|
-
|
|
92
|
+
pnpm start # Run CLI with tsx
|
|
93
|
+
pnpm test # Run tests
|
|
94
|
+
pnpm lint # Run oxlint
|
|
95
|
+
pnpm format # Format with oxfmt
|
|
96
|
+
pnpm typecheck # Type check
|
|
89
97
|
```
|
|
90
98
|
|
|
91
|
-
|
|
99
|
+
### Releasing
|
|
92
100
|
|
|
93
|
-
|
|
101
|
+
```bash
|
|
102
|
+
gh workflow run release.yml -f bump_type=patch # or minor/major
|
|
103
|
+
gh run watch
|
|
104
|
+
```
|
|
94
105
|
|
|
95
|
-
|
|
106
|
+
## Resources
|
|
96
107
|
|
|
97
|
-
|
|
108
|
+
### Claude Code Plugin Development
|
|
98
109
|
|
|
99
|
-
|
|
110
|
+
- [Claude Code Plugins Announcement](https://www.anthropic.com/news/claude-code-plugins)
|
|
111
|
+
- [Official Claude Code Plugins](https://github.com/anthropics/claude-code/tree/main/plugins)
|
|
112
|
+
- [Skills Guide](https://docs.claude.com/en/api/skills-guide)
|
|
113
|
+
- [Best Practices](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practices)
|
|
100
114
|
|
|
101
115
|
## License
|
|
102
116
|
|
|
103
117
|
[MIT](./LICENSE) License © [Chris Towles](https://github.com/ChrisTowles)
|
|
104
|
-
|
|
105
|
-
<!-- Badges -->
|
|
106
|
-
|
|
107
|
-
[npm-version-src]: https://img.shields.io/npm/v/@towles/tool?style=flat&colorA=080f12&colorB=1fa669
|
|
108
|
-
[npm-version-href]: https://npmjs.com/package/@towles/tool
|
|
109
|
-
[npm-downloads-src]: https://img.shields.io/npm/dm/@towles/tool?style=flat&colorA=080f12&colorB=1fa669
|
|
110
|
-
[npm-downloads-href]: https://npmjs.com/package/@towles/tool
|
|
111
|
-
[bundle-src]: https://img.shields.io/bundlephobia/minzip/@towles/tool?style=flat&colorA=080f12&colorB=1fa669&label=minzip
|
|
112
|
-
[bundle-href]: https://bundlephobia.com/result?p=@towles/tool
|
|
113
|
-
[license-src]: https://img.shields.io/github/license/ChrisTowles/towles-tool.svg?style=flat&colorA=080f12&colorB=1fa669
|
|
114
|
-
[license-href]: https://github.com/ChrisTowles/towles-tool/blob/main/LICENSE.md
|
|
115
|
-
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
|
|
116
|
-
[jsdocs-href]: https://www.jsdocs.io/package/@towles/tool
|
package/bin/run.ts
ADDED
package/package.json
CHANGED
|
@@ -1,91 +1,111 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@towles/tool",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
3
|
+
"version": "0.0.48",
|
|
4
|
+
"description": "CLI tool with autonomous task runner (ralph), observability, and quality-of-life commands for daily development.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"autonomic",
|
|
7
|
+
"claude",
|
|
8
|
+
"cli",
|
|
9
|
+
"git",
|
|
10
|
+
"journal",
|
|
11
|
+
"oclif",
|
|
12
|
+
"ralph"
|
|
13
|
+
],
|
|
11
14
|
"homepage": "https://github.com/ChrisTowles/towles-tool#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/ChrisTowles/towles-tool/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Chris Towles <Chris.Towles.Dev@gmail.com>",
|
|
12
20
|
"repository": {
|
|
13
21
|
"type": "git",
|
|
14
22
|
"url": "git+https://github.com/ChrisTowles/towles-tool.git"
|
|
15
23
|
},
|
|
16
|
-
"
|
|
17
|
-
"url": "https://github.com/ChrisTowles
|
|
18
|
-
},
|
|
19
|
-
"keywords": [],
|
|
20
|
-
"sideEffects": false,
|
|
21
|
-
"exports": {
|
|
22
|
-
".": "./dist/index.mjs",
|
|
23
|
-
"./package.json": "./package.json"
|
|
24
|
+
"funding": {
|
|
25
|
+
"url": "https://github.com/sponsors/ChrisTowles"
|
|
24
26
|
},
|
|
25
|
-
"main": "./dist/index.mjs",
|
|
26
|
-
"module": "./dist/index.mjs",
|
|
27
|
-
"types": "./dist/index.d.mts",
|
|
28
27
|
"bin": {
|
|
29
|
-
"towles-tool": "./
|
|
30
|
-
"tt": "./
|
|
28
|
+
"towles-tool": "./bin/run.ts",
|
|
29
|
+
"tt": "./bin/run.ts"
|
|
31
30
|
},
|
|
32
31
|
"files": [
|
|
33
|
-
"
|
|
32
|
+
"bin",
|
|
33
|
+
"patches",
|
|
34
|
+
"src"
|
|
34
35
|
],
|
|
36
|
+
"type": "module",
|
|
37
|
+
"main": "bin/run.ts",
|
|
38
|
+
"scripts": {
|
|
39
|
+
"start": "tsx ./bin/run.ts",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"lint": "oxlint",
|
|
42
|
+
"lint:fix": "oxlint --fix",
|
|
43
|
+
"format": "oxfmt --write",
|
|
44
|
+
"format:check": "oxfmt --check",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest watch",
|
|
47
|
+
"prepare": "simple-git-hooks",
|
|
48
|
+
"version:sync": "tsx scripts/sync-versions.ts"
|
|
49
|
+
},
|
|
35
50
|
"dependencies": {
|
|
36
|
-
"@
|
|
37
|
-
"@anthropic-ai/sdk": "^0.56.0",
|
|
38
|
-
"@clack/prompts": "^0.11.0",
|
|
39
|
-
"comment-json": "^4.2.5",
|
|
51
|
+
"@oclif/core": "^4.3.16",
|
|
40
52
|
"consola": "^3.4.2",
|
|
53
|
+
"d3-hierarchy": "^3.1.2",
|
|
41
54
|
"fzf": "^0.5.2",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
55
|
+
"globby": "^14.1.0",
|
|
56
|
+
"luxon": "^3.5.0",
|
|
57
|
+
"open": "^10.1.1",
|
|
58
|
+
"picocolors": "^1.1.1",
|
|
44
59
|
"prompts": "^2.4.2",
|
|
45
|
-
"strip-ansi": "^7.1.
|
|
46
|
-
"tinyexec": "^0.
|
|
47
|
-
"
|
|
48
|
-
"zod": "^4.0.5"
|
|
60
|
+
"strip-ansi": "^7.1.2",
|
|
61
|
+
"tinyexec": "^1.0.2",
|
|
62
|
+
"zod": "^3.25.67"
|
|
49
63
|
},
|
|
50
64
|
"devDependencies": {
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
65
|
+
"@oclif/test": "^4.1.13",
|
|
66
|
+
"@total-typescript/tsconfig": "^1.0.4",
|
|
67
|
+
"@tsconfig/strictest": "^2.0.5",
|
|
68
|
+
"@types/d3-hierarchy": "^3.1.7",
|
|
69
|
+
"@types/luxon": "^3.4.2",
|
|
70
|
+
"@types/node": "^22.10.10",
|
|
54
71
|
"@types/prompts": "^2.4.9",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"oxlint": "^1.
|
|
59
|
-
"simple-git-hooks": "^2.
|
|
60
|
-
"tsx": "^4.
|
|
72
|
+
"bumpp": "^10.4.0",
|
|
73
|
+
"lint-staged": "^15.5.1",
|
|
74
|
+
"oxfmt": "^0.24.0",
|
|
75
|
+
"oxlint": "^1.2.0",
|
|
76
|
+
"simple-git-hooks": "^2.11.1",
|
|
77
|
+
"tsx": "^4.19.2",
|
|
61
78
|
"typescript": "^5.8.3",
|
|
62
|
-
"
|
|
63
|
-
"vite": "^6.3.5",
|
|
64
|
-
"vitest": "^3.2.4",
|
|
65
|
-
"vitest-package-exports": "^0.1.1",
|
|
66
|
-
"yaml": "^2.8.0"
|
|
79
|
+
"vitest": "^3.1.3"
|
|
67
80
|
},
|
|
68
81
|
"simple-git-hooks": {
|
|
69
|
-
"pre-commit": "pnpm
|
|
82
|
+
"pre-commit": "pnpm lint-staged && pnpm typecheck"
|
|
70
83
|
},
|
|
71
84
|
"lint-staged": {
|
|
72
|
-
"
|
|
85
|
+
"package.json": "oxfmt --write",
|
|
86
|
+
"*.{ts,tsx,mts,cts,js,cjs,mjs}": [
|
|
87
|
+
"oxfmt --write",
|
|
88
|
+
"oxlint --fix"
|
|
89
|
+
],
|
|
90
|
+
"*.{json,md,yaml,yml}": "oxfmt --write"
|
|
73
91
|
},
|
|
74
|
-
"
|
|
75
|
-
"
|
|
92
|
+
"oclif": {
|
|
93
|
+
"bin": "tt",
|
|
94
|
+
"commands": {
|
|
95
|
+
"strategy": "pattern",
|
|
96
|
+
"target": "./src/commands"
|
|
97
|
+
},
|
|
98
|
+
"dirname": "towles-tool",
|
|
99
|
+
"plugins": [],
|
|
100
|
+
"topicSeparator": " "
|
|
76
101
|
},
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"release": "bumpp && echo \"github action will run and publish to npm\"",
|
|
86
|
-
"start": "tsx src/index.ts",
|
|
87
|
-
"test": "vitest --run",
|
|
88
|
-
"test:watch": "CI=DisableCallingClaude vitest --watch",
|
|
89
|
-
"typecheck": "tsc --noEmit"
|
|
102
|
+
"engines": {
|
|
103
|
+
"node": ">=18.0.0"
|
|
104
|
+
},
|
|
105
|
+
"packageManager": "pnpm@10.11.0",
|
|
106
|
+
"pnpm": {
|
|
107
|
+
"patchedDependencies": {
|
|
108
|
+
"prompts@2.4.2": "patches/prompts.patch"
|
|
109
|
+
}
|
|
90
110
|
}
|
|
91
|
-
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# prompts patch
|
|
2
|
+
#
|
|
3
|
+
# Used by: src/commands/gh/branch.ts (autocomplete with fzf fuzzy filtering)
|
|
4
|
+
#
|
|
5
|
+
# Fix: On exit (Ctrl+C/Ctrl+D), reject the promise instead of resolving.
|
|
6
|
+
# Without this, exiting a prompt resolves with undefined and code continues
|
|
7
|
+
# as if user submitted empty input. This lets us catch cancellation properly.
|
|
8
|
+
|
|
9
|
+
diff --git a/dist/prompts.js b/dist/prompts.js
|
|
10
|
+
index 31f2648a7d215d61aff736424a6f2d66d07e3273..2b8a0ac36fd22ed17f6096ce4f52a4db95ba2633 100644
|
|
11
|
+
--- a/dist/prompts.js
|
|
12
|
+
+++ b/dist/prompts.js
|
|
13
|
+
@@ -14,7 +14,7 @@ function toPrompt(type, args, opts = {}) {
|
|
14
|
+
const onExit = opts.onExit || noop;
|
|
15
|
+
p.on('state', args.onState || noop);
|
|
16
|
+
p.on('submit', x => res(onSubmit(x)));
|
|
17
|
+
- p.on('exit', x => res(onExit(x)));
|
|
18
|
+
+ p.on('exit', x => rej(onExit(x)));
|
|
19
|
+
p.on('abort', x => rej(onAbort(x)));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
diff --git a/lib/prompts.js b/lib/prompts.js
|
|
23
|
+
index 9f625564601da8f79040698de197a6ff2fec3859..e08923fc9f66ab0700788a62455da89ad23c5f35 100644
|
|
24
|
+
--- a/lib/prompts.js
|
|
25
|
+
+++ b/lib/prompts.js
|
|
26
|
+
@@ -11,7 +11,7 @@ function toPrompt(type, args, opts={}) {
|
|
27
|
+
const onExit = opts.onExit || noop;
|
|
28
|
+
p.on('state', args.onState || noop);
|
|
29
|
+
p.on('submit', x => res(onSubmit(x)));
|
|
30
|
+
- p.on('exit', x => res(onExit(x)));
|
|
31
|
+
+ p.on('exit', x => rej(onExit(x)));
|
|
32
|
+
p.on('abort', x => rej(onAbort(x)));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Command, Flags } from "@oclif/core";
|
|
2
|
+
import type { SettingsFile } from "../config/settings.js";
|
|
3
|
+
import { loadSettings } from "../config/settings.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base command that all towles-tool commands extend.
|
|
7
|
+
* Provides shared functionality like settings loading and debug flag.
|
|
8
|
+
*/
|
|
9
|
+
export abstract class BaseCommand extends Command {
|
|
10
|
+
static baseFlags = {
|
|
11
|
+
debug: Flags.boolean({
|
|
12
|
+
char: "d",
|
|
13
|
+
description: "Enable debug output",
|
|
14
|
+
default: false,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
protected settings!: SettingsFile;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Called before run(). Loads user settings.
|
|
22
|
+
*/
|
|
23
|
+
async init(): Promise<void> {
|
|
24
|
+
await super.init();
|
|
25
|
+
this.settings = await loadSettings();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for oclif config command
|
|
3
|
+
* Note: consola outputs to stderr with different log levels
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect } from "vitest";
|
|
6
|
+
import { runCommand } from "@oclif/test";
|
|
7
|
+
|
|
8
|
+
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");
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import consola from "consola";
|
|
2
|
+
import { BaseCommand } from "./base.js";
|
|
3
|
+
|
|
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);
|
|
17
|
+
|
|
18
|
+
consola.info("Configuration");
|
|
19
|
+
consola.log("");
|
|
20
|
+
|
|
21
|
+
consola.info(`Settings File: ${this.settings.path}`);
|
|
22
|
+
consola.log("");
|
|
23
|
+
|
|
24
|
+
consola.warn("User Config:");
|
|
25
|
+
consola.log(
|
|
26
|
+
` Daily Path Template: ${this.settings.settings.journalSettings.dailyPathTemplate}`,
|
|
27
|
+
);
|
|
28
|
+
consola.log(
|
|
29
|
+
` Meeting Path Template: ${this.settings.settings.journalSettings.meetingPathTemplate}`,
|
|
30
|
+
);
|
|
31
|
+
consola.log(` Note Path Template: ${this.settings.settings.journalSettings.notePathTemplate}`);
|
|
32
|
+
consola.log(` Editor: ${this.settings.settings.preferredEditor}`);
|
|
33
|
+
consola.log("");
|
|
34
|
+
|
|
35
|
+
consola.warn("Working Directory:");
|
|
36
|
+
consola.log(` ${process.cwd()}`);
|
|
37
|
+
consola.log("");
|
|
38
|
+
|
|
39
|
+
consola.info("Shell Completions:");
|
|
40
|
+
consola.log(" Run `tt completion` to generate shell completions");
|
|
41
|
+
consola.log(" Bash/Zsh: tt completion >> ~/.bashrc (or ~/.zshrc)");
|
|
42
|
+
consola.log(" Fish: tt completion > ~/.config/fish/completions/tt.fish");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { x } from "tinyexec";
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
import { BaseCommand } from "./base.js";
|
|
6
|
+
|
|
7
|
+
interface CheckResult {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string | null;
|
|
10
|
+
ok: boolean;
|
|
11
|
+
warning?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check system dependencies and environment
|
|
16
|
+
*/
|
|
17
|
+
export default class Doctor extends BaseCommand {
|
|
18
|
+
static override description = "Check system dependencies and environment";
|
|
19
|
+
|
|
20
|
+
static override examples = [
|
|
21
|
+
{ description: "Check system dependencies", command: "<%= config.bin %> <%= command.id %>" },
|
|
22
|
+
{ description: "Verify environment after setup", command: "<%= config.bin %> doctor" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
async run(): Promise<void> {
|
|
26
|
+
await this.parse(Doctor);
|
|
27
|
+
|
|
28
|
+
this.log("Checking dependencies...\n");
|
|
29
|
+
|
|
30
|
+
const checks: CheckResult[] = await Promise.all([
|
|
31
|
+
this.checkCommand("git", ["--version"], /git version ([\d.]+)/),
|
|
32
|
+
this.checkCommand("gh", ["--version"], /gh version ([\d.]+)/),
|
|
33
|
+
this.checkCommand("node", ["--version"], /v?([\d.]+)/),
|
|
34
|
+
this.checkCommand("bun", ["--version"], /([\d.]+)/),
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// Display results
|
|
38
|
+
for (const check of checks) {
|
|
39
|
+
const icon = check.ok ? pc.green("✓") : pc.red("✗");
|
|
40
|
+
const version = check.version ?? "not found";
|
|
41
|
+
this.log(`${icon} ${check.name}: ${version}`);
|
|
42
|
+
if (check.warning) {
|
|
43
|
+
this.log(` ${pc.yellow("⚠")} ${check.warning}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check gh auth
|
|
48
|
+
this.log("");
|
|
49
|
+
const ghAuth = await this.checkGhAuth();
|
|
50
|
+
const authIcon = ghAuth.ok ? pc.green("✓") : pc.yellow("⚠");
|
|
51
|
+
this.log(`${authIcon} gh auth: ${ghAuth.ok ? "authenticated" : "not authenticated"}`);
|
|
52
|
+
if (!ghAuth.ok) {
|
|
53
|
+
this.log(` ${pc.dim("Run: gh auth login")}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Node version check
|
|
57
|
+
const nodeCheck = checks.find((c) => c.name === "node");
|
|
58
|
+
if (nodeCheck?.version) {
|
|
59
|
+
const major = Number.parseInt(nodeCheck.version.split(".")[0], 10);
|
|
60
|
+
if (major < 18) {
|
|
61
|
+
this.log("");
|
|
62
|
+
this.log(`${pc.yellow("⚠")} Node.js 18+ recommended (found ${nodeCheck.version})`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check ralph files in .gitignore
|
|
67
|
+
this.log("");
|
|
68
|
+
const gitignoreCheck = this.checkRalphGitignore();
|
|
69
|
+
const gitignoreIcon = gitignoreCheck.ok ? pc.green("✓") : pc.yellow("⚠");
|
|
70
|
+
this.log(
|
|
71
|
+
`${gitignoreIcon} .gitignore: ${gitignoreCheck.ok ? "ralph-* excluded" : "ralph-* NOT excluded"}`,
|
|
72
|
+
);
|
|
73
|
+
if (!gitignoreCheck.ok) {
|
|
74
|
+
this.log(` ${pc.dim('Add "ralph-*" to .gitignore to exclude local ralph state files')}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Summary
|
|
78
|
+
const allOk = checks.every((c) => c.ok) && ghAuth.ok && gitignoreCheck.ok;
|
|
79
|
+
this.log("");
|
|
80
|
+
if (allOk) {
|
|
81
|
+
this.log(pc.green("All checks passed!"));
|
|
82
|
+
} else {
|
|
83
|
+
this.log(pc.yellow("Some checks failed. See above for details."));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async checkCommand(
|
|
88
|
+
name: string,
|
|
89
|
+
args: string[],
|
|
90
|
+
versionPattern: RegExp,
|
|
91
|
+
): Promise<CheckResult> {
|
|
92
|
+
try {
|
|
93
|
+
// tinyexec is safe - uses execFile internally, no shell injection risk
|
|
94
|
+
const result = await x(name, args);
|
|
95
|
+
const output = result.stdout + result.stderr;
|
|
96
|
+
const match = output.match(versionPattern);
|
|
97
|
+
return {
|
|
98
|
+
name,
|
|
99
|
+
version: match?.[1] ?? output.trim().slice(0, 20),
|
|
100
|
+
ok: true,
|
|
101
|
+
};
|
|
102
|
+
} catch {
|
|
103
|
+
return { name, version: null, ok: false };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async checkGhAuth(): Promise<{ ok: boolean }> {
|
|
108
|
+
try {
|
|
109
|
+
// tinyexec is safe - uses execFile internally, no shell injection risk
|
|
110
|
+
const result = await x("gh", ["auth", "status"]);
|
|
111
|
+
return { ok: result.exitCode === 0 };
|
|
112
|
+
} catch {
|
|
113
|
+
return { ok: false };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private checkRalphGitignore(): { ok: boolean } {
|
|
118
|
+
const gitignorePath = path.join(process.cwd(), ".gitignore");
|
|
119
|
+
try {
|
|
120
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
121
|
+
return { ok: false };
|
|
122
|
+
}
|
|
123
|
+
const content = fs.readFileSync(gitignorePath, "utf-8");
|
|
124
|
+
// Check for ralph-* pattern or specific ralph files
|
|
125
|
+
const hasRalphPattern = content.split("\n").some((line) => {
|
|
126
|
+
const trimmed = line.trim();
|
|
127
|
+
return (
|
|
128
|
+
trimmed === "ralph-*" || trimmed === "ralph-*.json" || trimmed === "ralph-state.json"
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
return { ok: hasRalphPattern };
|
|
132
|
+
} catch {
|
|
133
|
+
return { ok: false };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|