runbook-cli 1.0.0
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/.runbook +4 -0
- package/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/commands.d.ts +11 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +56 -0
- package/dist/commands.js.map +1 -0
- package/dist/git.d.ts +3 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +29 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
- package/src/commands.ts +59 -0
- package/src/git.ts +31 -0
- package/src/index.ts +135 -0
- package/tsconfig.json +20 -0
package/.runbook
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Brian Munene Mwirigi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# runbook
|
|
2
|
+
|
|
3
|
+
Remember project commands. Run them from anywhere.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
Every project runs differently. You can't remember. You check the README every time.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Is it...
|
|
11
|
+
npm run dev
|
|
12
|
+
# or
|
|
13
|
+
npm start
|
|
14
|
+
# or
|
|
15
|
+
pnpm dev
|
|
16
|
+
# or
|
|
17
|
+
python manage.py runserver
|
|
18
|
+
# or
|
|
19
|
+
go run main.go
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Stop guessing. Stop checking.**
|
|
23
|
+
|
|
24
|
+
## The Solution
|
|
25
|
+
|
|
26
|
+
Set it once. Run it forever.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Set commands
|
|
30
|
+
runbook set dev "npm run dev"
|
|
31
|
+
runbook set test "npm test"
|
|
32
|
+
runbook set build "npm run build"
|
|
33
|
+
|
|
34
|
+
# Run from anywhere in the project
|
|
35
|
+
runbook dev
|
|
36
|
+
|
|
37
|
+
# That's it.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install -g runbook-cli
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Set Commands
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Basic
|
|
52
|
+
runbook set dev "npm run dev"
|
|
53
|
+
|
|
54
|
+
# With install
|
|
55
|
+
runbook set dev "npm install && npm run dev"
|
|
56
|
+
|
|
57
|
+
# Backend
|
|
58
|
+
runbook set start "python manage.py runserver"
|
|
59
|
+
|
|
60
|
+
# Docker
|
|
61
|
+
runbook set up "docker-compose up -d"
|
|
62
|
+
|
|
63
|
+
# Multi-step
|
|
64
|
+
runbook set deploy "npm run build && npm run test && git push"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Run Commands
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Run a command
|
|
71
|
+
runbook dev
|
|
72
|
+
|
|
73
|
+
# Works from any subfolder
|
|
74
|
+
cd src/components
|
|
75
|
+
runbook dev # Still works!
|
|
76
|
+
|
|
77
|
+
# Short alias
|
|
78
|
+
rb dev
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### List Commands
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# See all commands
|
|
85
|
+
runbook list
|
|
86
|
+
|
|
87
|
+
# Or just
|
|
88
|
+
runbook
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Delete Commands
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
runbook delete dev
|
|
95
|
+
# or
|
|
96
|
+
runbook rm dev
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Project Info
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
runbook info
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## How It Works
|
|
106
|
+
|
|
107
|
+
- Finds your git root automatically
|
|
108
|
+
- Stores commands in `.runbook` in project root
|
|
109
|
+
- Works from any subfolder
|
|
110
|
+
- Team shares via git (commit `.runbook`)
|
|
111
|
+
|
|
112
|
+
## Example Workflow
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# New project
|
|
116
|
+
cd my-project
|
|
117
|
+
runbook set dev "npm install && npm run dev"
|
|
118
|
+
runbook set test "npm test"
|
|
119
|
+
|
|
120
|
+
# 3 months later, can't remember
|
|
121
|
+
runbook
|
|
122
|
+
# Shows: dev, test
|
|
123
|
+
|
|
124
|
+
runbook dev
|
|
125
|
+
# Runs instantly
|
|
126
|
+
|
|
127
|
+
# In subfolder
|
|
128
|
+
cd src/pages
|
|
129
|
+
runbook dev
|
|
130
|
+
# Still works!
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Team Usage
|
|
134
|
+
|
|
135
|
+
Commit `.runbook` to git:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
git add .runbook
|
|
139
|
+
git commit -m "Add runbook commands"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Now everyone runs:
|
|
143
|
+
```bash
|
|
144
|
+
runbook dev # Works for everyone
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Commands Reference
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
runbook set <name> <command> # Set a command
|
|
151
|
+
runbook <name> # Run a command
|
|
152
|
+
runbook list # List all commands
|
|
153
|
+
runbook delete <name> # Delete a command
|
|
154
|
+
runbook info # Show project info
|
|
155
|
+
runbook # Show available commands
|
|
156
|
+
rb # Short alias
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Examples
|
|
160
|
+
|
|
161
|
+
### Node.js
|
|
162
|
+
```bash
|
|
163
|
+
runbook set dev "npm install && npm run dev"
|
|
164
|
+
runbook set build "npm run build"
|
|
165
|
+
runbook set test "npm test"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Python/Django
|
|
169
|
+
```bash
|
|
170
|
+
runbook set dev "pip install -r requirements.txt && python manage.py runserver"
|
|
171
|
+
runbook set migrate "python manage.py migrate"
|
|
172
|
+
runbook set shell "python manage.py shell"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Go
|
|
176
|
+
```bash
|
|
177
|
+
runbook set dev "go run main.go"
|
|
178
|
+
runbook set build "go build -o bin/app"
|
|
179
|
+
runbook set test "go test ./..."
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Docker
|
|
183
|
+
```bash
|
|
184
|
+
runbook set up "docker-compose up -d"
|
|
185
|
+
runbook set down "docker-compose down"
|
|
186
|
+
runbook set logs "docker-compose logs -f"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Full Stack
|
|
190
|
+
```bash
|
|
191
|
+
runbook set dev "docker-compose up -d && npm run dev"
|
|
192
|
+
runbook set backend "cd backend && python manage.py runserver"
|
|
193
|
+
runbook set frontend "cd frontend && npm start"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Why runbook?
|
|
197
|
+
|
|
198
|
+
- **No more README diving** - Commands right there
|
|
199
|
+
- **Works everywhere** - Any subfolder, always works
|
|
200
|
+
- **Team onboarding** - New dev runs `runbook dev`, done
|
|
201
|
+
- **Cross-language** - Node, Python, Go, Rust - doesn't matter
|
|
202
|
+
- **Zero config** - Just set and run
|
|
203
|
+
|
|
204
|
+
## Data Storage
|
|
205
|
+
|
|
206
|
+
Commands stored in `.runbook` in project root (JSON format).
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"dev": "npm run dev",
|
|
211
|
+
"test": "npm test",
|
|
212
|
+
"build": "npm run build"
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
|
219
|
+
|
|
220
|
+
## Author
|
|
221
|
+
|
|
222
|
+
Built by [Brian Mwirigi](https://github.com/brian-mwirigi)
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
**Stop thinking. Start running.**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface RunbookCommands {
|
|
2
|
+
[key: string]: string;
|
|
3
|
+
}
|
|
4
|
+
export declare function getRunbookPath(): string;
|
|
5
|
+
export declare function loadCommands(): RunbookCommands;
|
|
6
|
+
export declare function saveCommands(commands: RunbookCommands): void;
|
|
7
|
+
export declare function setCommand(name: string, command: string): void;
|
|
8
|
+
export declare function getCommand(name: string): string | undefined;
|
|
9
|
+
export declare function deleteCommand(name: string): boolean;
|
|
10
|
+
export declare function listCommands(): RunbookCommands;
|
|
11
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAID,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAED,wBAAgB,YAAY,IAAI,eAAe,CAa9C;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAG5D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI9D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQnD;AAED,wBAAgB,YAAY,IAAI,eAAe,CAE9C"}
|
package/dist/commands.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRunbookPath = getRunbookPath;
|
|
4
|
+
exports.loadCommands = loadCommands;
|
|
5
|
+
exports.saveCommands = saveCommands;
|
|
6
|
+
exports.setCommand = setCommand;
|
|
7
|
+
exports.getCommand = getCommand;
|
|
8
|
+
exports.deleteCommand = deleteCommand;
|
|
9
|
+
exports.listCommands = listCommands;
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const git_1 = require("./git");
|
|
13
|
+
const RUNBOOK_FILE = '.runbook';
|
|
14
|
+
function getRunbookPath() {
|
|
15
|
+
const projectRoot = (0, git_1.getProjectRoot)();
|
|
16
|
+
return (0, path_1.join)(projectRoot, RUNBOOK_FILE);
|
|
17
|
+
}
|
|
18
|
+
function loadCommands() {
|
|
19
|
+
const runbookPath = getRunbookPath();
|
|
20
|
+
if (!(0, fs_1.existsSync)(runbookPath)) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const content = (0, fs_1.readFileSync)(runbookPath, 'utf-8');
|
|
25
|
+
return JSON.parse(content);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function saveCommands(commands) {
|
|
32
|
+
const runbookPath = getRunbookPath();
|
|
33
|
+
(0, fs_1.writeFileSync)(runbookPath, JSON.stringify(commands, null, 2), 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
function setCommand(name, command) {
|
|
36
|
+
const commands = loadCommands();
|
|
37
|
+
commands[name] = command;
|
|
38
|
+
saveCommands(commands);
|
|
39
|
+
}
|
|
40
|
+
function getCommand(name) {
|
|
41
|
+
const commands = loadCommands();
|
|
42
|
+
return commands[name];
|
|
43
|
+
}
|
|
44
|
+
function deleteCommand(name) {
|
|
45
|
+
const commands = loadCommands();
|
|
46
|
+
if (!(name in commands)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
delete commands[name];
|
|
50
|
+
saveCommands(commands);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
function listCommands() {
|
|
54
|
+
return loadCommands();
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":";;AAUA,wCAGC;AAED,oCAaC;AAED,oCAGC;AAED,gCAIC;AAED,gCAGC;AAED,sCAQC;AAED,oCAEC;AA1DD,2BAA6D;AAC7D,+BAA4B;AAC5B,+BAAuC;AAMvC,MAAM,YAAY,GAAG,UAAU,CAAC;AAEhC,SAAgB,cAAc;IAC5B,MAAM,WAAW,GAAG,IAAA,oBAAc,GAAE,CAAC;IACrC,OAAO,IAAA,WAAI,EAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AACzC,CAAC;AAED,SAAgB,YAAY;IAC1B,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,IAAI,CAAC,IAAA,eAAU,EAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAgB,YAAY,CAAC,QAAyB;IACpD,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,IAAA,kBAAa,EAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACzE,CAAC;AAED,SAAgB,UAAU,CAAC,IAAY,EAAE,OAAe;IACtD,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;IACzB,YAAY,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC;AAED,SAAgB,UAAU,CAAC,IAAY;IACrC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,SAAgB,aAAa,CAAC,IAAY;IACxC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC,IAAI,IAAI,QAAQ,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtB,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,YAAY;IAC1B,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC"}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAgB5E;AAED,wBAAgB,cAAc,IAAI,MAAM,CAQvC"}
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findGitRoot = findGitRoot;
|
|
4
|
+
exports.getProjectRoot = getProjectRoot;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
function findGitRoot(startPath = process.cwd()) {
|
|
8
|
+
let currentPath = startPath;
|
|
9
|
+
while (true) {
|
|
10
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(currentPath, '.git'))) {
|
|
11
|
+
return currentPath;
|
|
12
|
+
}
|
|
13
|
+
const parentPath = (0, path_1.join)(currentPath, '..');
|
|
14
|
+
if (parentPath === currentPath) {
|
|
15
|
+
// Reached root without finding .git
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
currentPath = parentPath;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function getProjectRoot() {
|
|
22
|
+
const gitRoot = findGitRoot();
|
|
23
|
+
if (gitRoot) {
|
|
24
|
+
return gitRoot;
|
|
25
|
+
}
|
|
26
|
+
// Fallback to current directory if not a git repo
|
|
27
|
+
return process.cwd();
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=git.js.map
|
package/dist/git.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":";;AAIA,kCAgBC;AAED,wCAQC;AA7BD,2BAAgC;AAChC,+BAA4B;AAE5B,SAAgB,WAAW,CAAC,YAAoB,OAAO,CAAC,GAAG,EAAE;IAC3D,IAAI,WAAW,GAAG,SAAS,CAAC;IAE5B,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,WAAW,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YAC1C,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YAC/B,oCAAoC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,WAAW,GAAG,UAAU,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,SAAgB,cAAc;IAC5B,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,kDAAkD;IAClD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;AACvB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const commands_1 = require("./commands");
|
|
12
|
+
const git_1 = require("./git");
|
|
13
|
+
const program = new commander_1.Command();
|
|
14
|
+
program
|
|
15
|
+
.name('runbook')
|
|
16
|
+
.description('Remember project commands. Run them from anywhere.')
|
|
17
|
+
.version('1.0.0');
|
|
18
|
+
// Set command
|
|
19
|
+
program
|
|
20
|
+
.command('set')
|
|
21
|
+
.description('Set a command for this project')
|
|
22
|
+
.argument('<name>', 'Command name (e.g., dev, test, build)')
|
|
23
|
+
.argument('<command>', 'Command to run (e.g., "npm run dev")')
|
|
24
|
+
.action((name, command) => {
|
|
25
|
+
(0, commands_1.setCommand)(name, command);
|
|
26
|
+
console.log(chalk_1.default.green(`\n✓ Set '${name}' → ${command}`));
|
|
27
|
+
console.log(chalk_1.default.gray(` Run with: runbook ${name}\n`));
|
|
28
|
+
});
|
|
29
|
+
// Delete command
|
|
30
|
+
program
|
|
31
|
+
.command('delete')
|
|
32
|
+
.alias('rm')
|
|
33
|
+
.description('Delete a command')
|
|
34
|
+
.argument('<name>', 'Command name to delete')
|
|
35
|
+
.action((name) => {
|
|
36
|
+
if ((0, commands_1.deleteCommand)(name)) {
|
|
37
|
+
console.log(chalk_1.default.green(`\n✓ Deleted '${name}'\n`));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log(chalk_1.default.yellow(`\n⚠ Command '${name}' not found\n`));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// List commands
|
|
44
|
+
program
|
|
45
|
+
.command('list')
|
|
46
|
+
.alias('ls')
|
|
47
|
+
.description('List all commands for this project')
|
|
48
|
+
.action(() => {
|
|
49
|
+
const commands = (0, commands_1.listCommands)();
|
|
50
|
+
const entries = Object.entries(commands);
|
|
51
|
+
if (entries.length === 0) {
|
|
52
|
+
console.log(chalk_1.default.yellow('\nNo commands set for this project.'));
|
|
53
|
+
console.log(chalk_1.default.gray('Set one with: runbook set <name> <command>\n'));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
console.log(chalk_1.default.bold.cyan(`\nCommands for ${(0, git_1.getProjectRoot)()}\n`));
|
|
57
|
+
const table = new cli_table3_1.default({
|
|
58
|
+
head: [chalk_1.default.cyan.bold('Name'), chalk_1.default.cyan.bold('Command')],
|
|
59
|
+
style: { head: [], border: [] },
|
|
60
|
+
colWidths: [15, 65],
|
|
61
|
+
wordWrap: true,
|
|
62
|
+
});
|
|
63
|
+
for (const [name, command] of entries) {
|
|
64
|
+
table.push([chalk_1.default.green(name), chalk_1.default.white(command)]);
|
|
65
|
+
}
|
|
66
|
+
console.log(table.toString());
|
|
67
|
+
console.log(chalk_1.default.gray(`\nRun with: runbook <name>`));
|
|
68
|
+
console.log(chalk_1.default.gray(`Stored in: ${(0, commands_1.getRunbookPath)()}\n`));
|
|
69
|
+
});
|
|
70
|
+
// Info command
|
|
71
|
+
program
|
|
72
|
+
.command('info')
|
|
73
|
+
.description('Show project info')
|
|
74
|
+
.action(() => {
|
|
75
|
+
console.log(chalk_1.default.cyan('\nProject Info\n'));
|
|
76
|
+
console.log(chalk_1.default.white('Root: ') + chalk_1.default.gray((0, git_1.getProjectRoot)()));
|
|
77
|
+
console.log(chalk_1.default.white('Runbook: ') + chalk_1.default.gray((0, commands_1.getRunbookPath)()));
|
|
78
|
+
const commands = (0, commands_1.listCommands)();
|
|
79
|
+
console.log(chalk_1.default.white('Commands: ') + chalk_1.default.gray(Object.keys(commands).length.toString()));
|
|
80
|
+
console.log();
|
|
81
|
+
});
|
|
82
|
+
// Run command (default action)
|
|
83
|
+
program
|
|
84
|
+
.argument('[name]', 'Command name to run')
|
|
85
|
+
.action((name) => {
|
|
86
|
+
if (!name) {
|
|
87
|
+
// Show list if no command specified
|
|
88
|
+
const commands = (0, commands_1.listCommands)();
|
|
89
|
+
const entries = Object.entries(commands);
|
|
90
|
+
if (entries.length === 0) {
|
|
91
|
+
console.log(chalk_1.default.yellow('\nNo commands set for this project.'));
|
|
92
|
+
console.log(chalk_1.default.gray('Set one with: runbook set <name> <command>\n'));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(chalk_1.default.bold.cyan('\nAvailable commands:\n'));
|
|
96
|
+
for (const [cmdName] of entries) {
|
|
97
|
+
console.log(chalk_1.default.green(` runbook ${cmdName}`));
|
|
98
|
+
}
|
|
99
|
+
console.log();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const command = (0, commands_1.getCommand)(name);
|
|
103
|
+
if (!command) {
|
|
104
|
+
console.log(chalk_1.default.red(`\n✗ Command '${name}' not found`));
|
|
105
|
+
console.log(chalk_1.default.gray('Set it with: runbook set ' + name + ' <command>\n'));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
console.log(chalk_1.default.cyan(`\n→ Running: ${command}\n`));
|
|
109
|
+
// Run command in shell
|
|
110
|
+
const child = (0, child_process_1.spawn)(command, [], {
|
|
111
|
+
shell: true,
|
|
112
|
+
stdio: 'inherit',
|
|
113
|
+
cwd: (0, git_1.getProjectRoot)(),
|
|
114
|
+
});
|
|
115
|
+
child.on('exit', (code) => {
|
|
116
|
+
process.exit(code || 0);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
program.parse();
|
|
120
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "runbook-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Remember project commands. Run them from anywhere.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"runbook": "./dist/index.js",
|
|
8
|
+
"rb": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"prepare": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"commands",
|
|
18
|
+
"project",
|
|
19
|
+
"cli",
|
|
20
|
+
"dev",
|
|
21
|
+
"workflow",
|
|
22
|
+
"automation"
|
|
23
|
+
],
|
|
24
|
+
"author": "Brian Mwirigi",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"chalk": "^4.1.2",
|
|
28
|
+
"commander": "^11.1.0",
|
|
29
|
+
"cli-table3": "^0.6.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.10.6",
|
|
33
|
+
"tsx": "^4.7.0",
|
|
34
|
+
"typescript": "^5.3.3"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/commands.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { getProjectRoot } from './git';
|
|
4
|
+
|
|
5
|
+
export interface RunbookCommands {
|
|
6
|
+
[key: string]: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const RUNBOOK_FILE = '.runbook';
|
|
10
|
+
|
|
11
|
+
export function getRunbookPath(): string {
|
|
12
|
+
const projectRoot = getProjectRoot();
|
|
13
|
+
return join(projectRoot, RUNBOOK_FILE);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function loadCommands(): RunbookCommands {
|
|
17
|
+
const runbookPath = getRunbookPath();
|
|
18
|
+
|
|
19
|
+
if (!existsSync(runbookPath)) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const content = readFileSync(runbookPath, 'utf-8');
|
|
25
|
+
return JSON.parse(content);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function saveCommands(commands: RunbookCommands): void {
|
|
32
|
+
const runbookPath = getRunbookPath();
|
|
33
|
+
writeFileSync(runbookPath, JSON.stringify(commands, null, 2), 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setCommand(name: string, command: string): void {
|
|
37
|
+
const commands = loadCommands();
|
|
38
|
+
commands[name] = command;
|
|
39
|
+
saveCommands(commands);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getCommand(name: string): string | undefined {
|
|
43
|
+
const commands = loadCommands();
|
|
44
|
+
return commands[name];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function deleteCommand(name: string): boolean {
|
|
48
|
+
const commands = loadCommands();
|
|
49
|
+
if (!(name in commands)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
delete commands[name];
|
|
53
|
+
saveCommands(commands);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function listCommands(): RunbookCommands {
|
|
58
|
+
return loadCommands();
|
|
59
|
+
}
|
package/src/git.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
export function findGitRoot(startPath: string = process.cwd()): string | null {
|
|
6
|
+
let currentPath = startPath;
|
|
7
|
+
|
|
8
|
+
while (true) {
|
|
9
|
+
if (existsSync(join(currentPath, '.git'))) {
|
|
10
|
+
return currentPath;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const parentPath = join(currentPath, '..');
|
|
14
|
+
if (parentPath === currentPath) {
|
|
15
|
+
// Reached root without finding .git
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
currentPath = parentPath;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getProjectRoot(): string {
|
|
24
|
+
const gitRoot = findGitRoot();
|
|
25
|
+
if (gitRoot) {
|
|
26
|
+
return gitRoot;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Fallback to current directory if not a git repo
|
|
30
|
+
return process.cwd();
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import Table from 'cli-table3';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import { setCommand, getCommand, deleteCommand, listCommands, getRunbookPath } from './commands';
|
|
8
|
+
import { getProjectRoot } from './git';
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('runbook')
|
|
14
|
+
.description('Remember project commands. Run them from anywhere.')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
|
|
17
|
+
// Set command
|
|
18
|
+
program
|
|
19
|
+
.command('set')
|
|
20
|
+
.description('Set a command for this project')
|
|
21
|
+
.argument('<name>', 'Command name (e.g., dev, test, build)')
|
|
22
|
+
.argument('<command>', 'Command to run (e.g., "npm run dev")')
|
|
23
|
+
.action((name: string, command: string) => {
|
|
24
|
+
setCommand(name, command);
|
|
25
|
+
console.log(chalk.green(`\n✓ Set '${name}' → ${command}`));
|
|
26
|
+
console.log(chalk.gray(` Run with: runbook ${name}\n`));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Delete command
|
|
30
|
+
program
|
|
31
|
+
.command('delete')
|
|
32
|
+
.alias('rm')
|
|
33
|
+
.description('Delete a command')
|
|
34
|
+
.argument('<name>', 'Command name to delete')
|
|
35
|
+
.action((name: string) => {
|
|
36
|
+
if (deleteCommand(name)) {
|
|
37
|
+
console.log(chalk.green(`\n✓ Deleted '${name}'\n`));
|
|
38
|
+
} else {
|
|
39
|
+
console.log(chalk.yellow(`\n⚠ Command '${name}' not found\n`));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// List commands
|
|
44
|
+
program
|
|
45
|
+
.command('list')
|
|
46
|
+
.alias('ls')
|
|
47
|
+
.description('List all commands for this project')
|
|
48
|
+
.action(() => {
|
|
49
|
+
const commands = listCommands();
|
|
50
|
+
const entries = Object.entries(commands);
|
|
51
|
+
|
|
52
|
+
if (entries.length === 0) {
|
|
53
|
+
console.log(chalk.yellow('\nNo commands set for this project.'));
|
|
54
|
+
console.log(chalk.gray('Set one with: runbook set <name> <command>\n'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(chalk.bold.cyan(`\nCommands for ${getProjectRoot()}\n`));
|
|
59
|
+
|
|
60
|
+
const table = new Table({
|
|
61
|
+
head: [chalk.cyan.bold('Name'), chalk.cyan.bold('Command')],
|
|
62
|
+
style: { head: [], border: [] },
|
|
63
|
+
colWidths: [15, 65],
|
|
64
|
+
wordWrap: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
for (const [name, command] of entries) {
|
|
68
|
+
table.push([chalk.green(name), chalk.white(command)]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(table.toString());
|
|
72
|
+
console.log(chalk.gray(`\nRun with: runbook <name>`));
|
|
73
|
+
console.log(chalk.gray(`Stored in: ${getRunbookPath()}\n`));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Info command
|
|
77
|
+
program
|
|
78
|
+
.command('info')
|
|
79
|
+
.description('Show project info')
|
|
80
|
+
.action(() => {
|
|
81
|
+
console.log(chalk.cyan('\nProject Info\n'));
|
|
82
|
+
console.log(chalk.white('Root: ') + chalk.gray(getProjectRoot()));
|
|
83
|
+
console.log(chalk.white('Runbook: ') + chalk.gray(getRunbookPath()));
|
|
84
|
+
|
|
85
|
+
const commands = listCommands();
|
|
86
|
+
console.log(chalk.white('Commands: ') + chalk.gray(Object.keys(commands).length.toString()));
|
|
87
|
+
console.log();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Run command (default action)
|
|
91
|
+
program
|
|
92
|
+
.argument('[name]', 'Command name to run')
|
|
93
|
+
.action((name?: string) => {
|
|
94
|
+
if (!name) {
|
|
95
|
+
// Show list if no command specified
|
|
96
|
+
const commands = listCommands();
|
|
97
|
+
const entries = Object.entries(commands);
|
|
98
|
+
|
|
99
|
+
if (entries.length === 0) {
|
|
100
|
+
console.log(chalk.yellow('\nNo commands set for this project.'));
|
|
101
|
+
console.log(chalk.gray('Set one with: runbook set <name> <command>\n'));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log(chalk.bold.cyan('\nAvailable commands:\n'));
|
|
106
|
+
for (const [cmdName] of entries) {
|
|
107
|
+
console.log(chalk.green(` runbook ${cmdName}`));
|
|
108
|
+
}
|
|
109
|
+
console.log();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const command = getCommand(name);
|
|
114
|
+
|
|
115
|
+
if (!command) {
|
|
116
|
+
console.log(chalk.red(`\n✗ Command '${name}' not found`));
|
|
117
|
+
console.log(chalk.gray('Set it with: runbook set ' + name + ' <command>\n'));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(chalk.cyan(`\n→ Running: ${command}\n`));
|
|
122
|
+
|
|
123
|
+
// Run command in shell
|
|
124
|
+
const child = spawn(command, [], {
|
|
125
|
+
shell: true,
|
|
126
|
+
stdio: 'inherit',
|
|
127
|
+
cwd: getProjectRoot(),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
child.on('exit', (code) => {
|
|
131
|
+
process.exit(code || 0);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
program.parse();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"moduleResolution": "node"
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|