feat-forge 1.0.3 → 1.1.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/README.md +10 -0
- package/dist/cli.js +33 -1
- package/dist/commands/AliasCommands.js +245 -0
- package/dist/commands/BranchCommands.js +12 -0
- package/dist/commands/CompletionCommands.js +830 -290
- package/dist/commands/SubBranchCommands.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# FeatForge
|
|
2
2
|
|
|
3
|
+
[](https://github.com/lixus3d/feat-forge-cli/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/feat-forge)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://vitest.dev/)
|
|
7
|
+
[](https://github.com/lixus3d/feat-forge-cli/pulls)
|
|
8
|
+
|
|
3
9
|
**FeatForge** is a feature-first workflow and CLI (`forge`) to help you build software at scale, with (or without) AI agents.
|
|
4
10
|
|
|
5
11
|
Its goal is to make the specification of features explicit and separate the thinking/specifying phase from the coding/implementation phase, across multiple agents and repositories, while keeping everything organized and traceable.
|
|
@@ -348,3 +354,7 @@ For all commands and options:
|
|
|
348
354
|
```bash
|
|
349
355
|
forge --help
|
|
350
356
|
```
|
|
357
|
+
|
|
358
|
+
# License
|
|
359
|
+
|
|
360
|
+
[](./LICENSE)
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import 'reflect-metadata';
|
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import { AgentCommands } from './commands/AgentCommands.js';
|
|
6
6
|
import { MaintenanceCommands } from './commands/MaintenanceCommands.js';
|
|
7
|
+
import { AliasCommands } from './commands/AliasCommands.js';
|
|
7
8
|
import { CompletionCommands } from './commands/CompletionCommands.js';
|
|
8
9
|
import { FeatureCommands } from './commands/FeatureCommands.js';
|
|
9
10
|
import { InitCommands } from './commands/InitCommands.js';
|
|
@@ -91,6 +92,11 @@ function genericBranchTypeCommands(baseCommand, handlers, subType = null) {
|
|
|
91
92
|
.argument(`[${argName}]`, argDesc)
|
|
92
93
|
.description(`Open the given ${subType ? subType + ' ' : ''}branch in the configured IDE (if no ${subType ? subType + ' ' : ''}branch is given, opens the nearest ${subType ? subType + ' ' : ''}branch)`)
|
|
93
94
|
.action(handlers.open.bind(handlers));
|
|
95
|
+
baseCommand
|
|
96
|
+
.command('path')
|
|
97
|
+
.argument(`[${argName}]`, argDesc)
|
|
98
|
+
.description(`Print the worktree path of a ${subType ? subType + ' ' : ''}branch (useful for shell integration: cd "$(forge path <name>)")`)
|
|
99
|
+
.action(handlers.path.bind(handlers));
|
|
94
100
|
baseCommand
|
|
95
101
|
.command('merge')
|
|
96
102
|
.argument(`<${argName}>`, argDesc)
|
|
@@ -221,6 +227,29 @@ function registerProxyCommands(program, context) {
|
|
|
221
227
|
await handlers.start(options);
|
|
222
228
|
});
|
|
223
229
|
}
|
|
230
|
+
/*
|
|
231
|
+
* Register alias commands with the CLI.
|
|
232
|
+
* This command works without config.
|
|
233
|
+
*/
|
|
234
|
+
function registerAliasCommands(program) {
|
|
235
|
+
const handlers = new AliasCommands();
|
|
236
|
+
const validShells = [ShellName.Bash, ShellName.Zsh, ShellName.Fish, ShellName.PowerShell, ShellName.Pwsh];
|
|
237
|
+
function isValidShellName(value) {
|
|
238
|
+
return validShells.includes(value);
|
|
239
|
+
}
|
|
240
|
+
program
|
|
241
|
+
.command('alias <shell>')
|
|
242
|
+
.description(`Generate shell aliases for feat-forge (${validShells.join(', ')})`)
|
|
243
|
+
.option('--prefix <prefix>', 'Prefix for alias names', 'ff')
|
|
244
|
+
.action((shell, options) => {
|
|
245
|
+
if (!isValidShellName(shell)) {
|
|
246
|
+
console.error(`Error: Unsupported shell type "${shell}". Supported shells: ${validShells.join(', ')}`);
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
handlers.generate(shell, options.prefix);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
224
253
|
/*
|
|
225
254
|
* Register completion commands with the CLI.
|
|
226
255
|
* This command works both with and without config.
|
|
@@ -263,13 +292,16 @@ async function main() {
|
|
|
263
292
|
program.name('forge').description('Feat-Forge workflow CLI').version(packageJson.version);
|
|
264
293
|
// Init command doesn't need config
|
|
265
294
|
registerInitCommands(program);
|
|
295
|
+
// Alias command doesn't need config
|
|
296
|
+
registerAliasCommands(program);
|
|
266
297
|
// Completion command should work with or without config
|
|
267
298
|
// Register it early so it's available even without .feat-forge.json
|
|
268
299
|
let context;
|
|
269
300
|
// Load config for other commands
|
|
270
301
|
const isInitCommand = process.argv[2] === 'init';
|
|
302
|
+
const isAliasCommand = process.argv[2] === 'alias';
|
|
271
303
|
const isCompletionCommand = process.argv[2] === 'completion';
|
|
272
|
-
if (!isInitCommand) {
|
|
304
|
+
if (!isInitCommand && !isAliasCommand) {
|
|
273
305
|
try {
|
|
274
306
|
context = await loadForgeContext();
|
|
275
307
|
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { ShellName } from '../foundation/types/ShellName.js';
|
|
2
|
+
/**
|
|
3
|
+
* Commands for generating shell aliases for feat-forge.
|
|
4
|
+
*/
|
|
5
|
+
export class AliasCommands {
|
|
6
|
+
/**
|
|
7
|
+
* Generate and print shell aliases for the specified shell.
|
|
8
|
+
*/
|
|
9
|
+
generate(shell, prefix) {
|
|
10
|
+
let output;
|
|
11
|
+
switch (shell) {
|
|
12
|
+
case ShellName.Bash:
|
|
13
|
+
output = this.generateBashAliases(prefix);
|
|
14
|
+
break;
|
|
15
|
+
case ShellName.Zsh:
|
|
16
|
+
output = this.generateZshAliases(prefix);
|
|
17
|
+
break;
|
|
18
|
+
case ShellName.Fish:
|
|
19
|
+
output = this.generateFishAliases(prefix);
|
|
20
|
+
break;
|
|
21
|
+
case ShellName.PowerShell:
|
|
22
|
+
case ShellName.Pwsh:
|
|
23
|
+
output = this.generatePowerShellAliases(prefix);
|
|
24
|
+
break;
|
|
25
|
+
default:
|
|
26
|
+
throw new Error(`Unsupported shell: ${shell}`);
|
|
27
|
+
}
|
|
28
|
+
console.log(output);
|
|
29
|
+
}
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// PRIVATE METHODS
|
|
32
|
+
// ============================================================================
|
|
33
|
+
generateBashAliases(p) {
|
|
34
|
+
return `# feat-forge aliases (generated by: forge alias bash --prefix ${p})
|
|
35
|
+
# Add to your ~/.bashrc: source <(forge alias bash${p !== 'ff' ? ` --prefix ${p}` : ''})
|
|
36
|
+
|
|
37
|
+
# Group aliases
|
|
38
|
+
alias ${p}='forge'
|
|
39
|
+
alias ${p}f='forge feature'
|
|
40
|
+
alias ${p}i='forge fix'
|
|
41
|
+
alias ${p}r='forge release'
|
|
42
|
+
|
|
43
|
+
# Command shortcuts
|
|
44
|
+
alias ${p}s='forge start'
|
|
45
|
+
alias ${p}x='forge stop'
|
|
46
|
+
alias ${p}c='forge create'
|
|
47
|
+
alias ${p}o='forge open'
|
|
48
|
+
alias ${p}l='forge list'
|
|
49
|
+
alias ${p}m='forge merge'
|
|
50
|
+
alias ${p}a='forge archive'
|
|
51
|
+
|
|
52
|
+
# Feature shortcuts
|
|
53
|
+
alias ${p}fs='forge feature start'
|
|
54
|
+
alias ${p}fx='forge feature stop'
|
|
55
|
+
alias ${p}fc='forge feature create'
|
|
56
|
+
alias ${p}fo='forge feature open'
|
|
57
|
+
alias ${p}fl='forge feature list'
|
|
58
|
+
alias ${p}fm='forge feature merge'
|
|
59
|
+
alias ${p}fa='forge feature archive'
|
|
60
|
+
|
|
61
|
+
# Fix shortcuts
|
|
62
|
+
alias ${p}is='forge fix start'
|
|
63
|
+
alias ${p}ix='forge fix stop'
|
|
64
|
+
alias ${p}ic='forge fix create'
|
|
65
|
+
alias ${p}io='forge fix open'
|
|
66
|
+
alias ${p}il='forge fix list'
|
|
67
|
+
alias ${p}im='forge fix merge'
|
|
68
|
+
alias ${p}ia='forge fix archive'
|
|
69
|
+
|
|
70
|
+
# Release shortcuts
|
|
71
|
+
alias ${p}rs='forge release start'
|
|
72
|
+
alias ${p}rx='forge release stop'
|
|
73
|
+
alias ${p}rc='forge release create'
|
|
74
|
+
alias ${p}ro='forge release open'
|
|
75
|
+
alias ${p}rl='forge release list'
|
|
76
|
+
alias ${p}rm='forge release merge'
|
|
77
|
+
alias ${p}ra='forge release archive'
|
|
78
|
+
|
|
79
|
+
# cd into branch worktree
|
|
80
|
+
${p}cd() { cd "$(forge path "$@")"; }
|
|
81
|
+
${p}fcd() { cd "$(forge feature path "$@")"; }
|
|
82
|
+
${p}icd() { cd "$(forge fix path "$@")"; }
|
|
83
|
+
${p}rcd() { cd "$(forge release path "$@")"; }
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
generateZshAliases(p) {
|
|
87
|
+
return `# feat-forge aliases (generated by: forge alias zsh --prefix ${p})
|
|
88
|
+
# Add to your ~/.zshrc: source <(forge alias zsh${p !== 'ff' ? ` --prefix ${p}` : ''})
|
|
89
|
+
|
|
90
|
+
# Group aliases
|
|
91
|
+
alias ${p}='forge'
|
|
92
|
+
alias ${p}f='forge feature'
|
|
93
|
+
alias ${p}i='forge fix'
|
|
94
|
+
alias ${p}r='forge release'
|
|
95
|
+
|
|
96
|
+
# Command shortcuts
|
|
97
|
+
alias ${p}s='forge start'
|
|
98
|
+
alias ${p}x='forge stop'
|
|
99
|
+
alias ${p}c='forge create'
|
|
100
|
+
alias ${p}o='forge open'
|
|
101
|
+
alias ${p}l='forge list'
|
|
102
|
+
alias ${p}m='forge merge'
|
|
103
|
+
alias ${p}a='forge archive'
|
|
104
|
+
|
|
105
|
+
# Feature shortcuts
|
|
106
|
+
alias ${p}fs='forge feature start'
|
|
107
|
+
alias ${p}fx='forge feature stop'
|
|
108
|
+
alias ${p}fc='forge feature create'
|
|
109
|
+
alias ${p}fo='forge feature open'
|
|
110
|
+
alias ${p}fl='forge feature list'
|
|
111
|
+
alias ${p}fm='forge feature merge'
|
|
112
|
+
alias ${p}fa='forge feature archive'
|
|
113
|
+
|
|
114
|
+
# Fix shortcuts
|
|
115
|
+
alias ${p}is='forge fix start'
|
|
116
|
+
alias ${p}ix='forge fix stop'
|
|
117
|
+
alias ${p}ic='forge fix create'
|
|
118
|
+
alias ${p}io='forge fix open'
|
|
119
|
+
alias ${p}il='forge fix list'
|
|
120
|
+
alias ${p}im='forge fix merge'
|
|
121
|
+
alias ${p}ia='forge fix archive'
|
|
122
|
+
|
|
123
|
+
# Release shortcuts
|
|
124
|
+
alias ${p}rs='forge release start'
|
|
125
|
+
alias ${p}rx='forge release stop'
|
|
126
|
+
alias ${p}rc='forge release create'
|
|
127
|
+
alias ${p}ro='forge release open'
|
|
128
|
+
alias ${p}rl='forge release list'
|
|
129
|
+
alias ${p}rm='forge release merge'
|
|
130
|
+
alias ${p}ra='forge release archive'
|
|
131
|
+
|
|
132
|
+
# cd into branch worktree
|
|
133
|
+
${p}cd() { cd "$(forge path "$@")"; }
|
|
134
|
+
${p}fcd() { cd "$(forge feature path "$@")"; }
|
|
135
|
+
${p}icd() { cd "$(forge fix path "$@")"; }
|
|
136
|
+
${p}rcd() { cd "$(forge release path "$@")"; }
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
generateFishAliases(p) {
|
|
140
|
+
return `# feat-forge aliases (generated by: forge alias fish --prefix ${p})
|
|
141
|
+
# Add to your ~/.config/fish/config.fish: forge alias fish${p !== 'ff' ? ` --prefix ${p}` : ''} | source
|
|
142
|
+
|
|
143
|
+
# Group aliases
|
|
144
|
+
abbr -a ${p} forge
|
|
145
|
+
abbr -a ${p}f 'forge feature'
|
|
146
|
+
abbr -a ${p}i 'forge fix'
|
|
147
|
+
abbr -a ${p}r 'forge release'
|
|
148
|
+
|
|
149
|
+
# Command shortcuts
|
|
150
|
+
abbr -a ${p}s 'forge start'
|
|
151
|
+
abbr -a ${p}x 'forge stop'
|
|
152
|
+
abbr -a ${p}c 'forge create'
|
|
153
|
+
abbr -a ${p}o 'forge open'
|
|
154
|
+
abbr -a ${p}l 'forge list'
|
|
155
|
+
abbr -a ${p}m 'forge merge'
|
|
156
|
+
abbr -a ${p}a 'forge archive'
|
|
157
|
+
|
|
158
|
+
# Feature shortcuts
|
|
159
|
+
abbr -a ${p}fs 'forge feature start'
|
|
160
|
+
abbr -a ${p}fx 'forge feature stop'
|
|
161
|
+
abbr -a ${p}fc 'forge feature create'
|
|
162
|
+
abbr -a ${p}fo 'forge feature open'
|
|
163
|
+
abbr -a ${p}fl 'forge feature list'
|
|
164
|
+
abbr -a ${p}fm 'forge feature merge'
|
|
165
|
+
abbr -a ${p}fa 'forge feature archive'
|
|
166
|
+
|
|
167
|
+
# Fix shortcuts
|
|
168
|
+
abbr -a ${p}is 'forge fix start'
|
|
169
|
+
abbr -a ${p}ix 'forge fix stop'
|
|
170
|
+
abbr -a ${p}ic 'forge fix create'
|
|
171
|
+
abbr -a ${p}io 'forge fix open'
|
|
172
|
+
abbr -a ${p}il 'forge fix list'
|
|
173
|
+
abbr -a ${p}im 'forge fix merge'
|
|
174
|
+
abbr -a ${p}ia 'forge fix archive'
|
|
175
|
+
|
|
176
|
+
# Release shortcuts
|
|
177
|
+
abbr -a ${p}rs 'forge release start'
|
|
178
|
+
abbr -a ${p}rx 'forge release stop'
|
|
179
|
+
abbr -a ${p}rc 'forge release create'
|
|
180
|
+
abbr -a ${p}ro 'forge release open'
|
|
181
|
+
abbr -a ${p}rl 'forge release list'
|
|
182
|
+
abbr -a ${p}rm 'forge release merge'
|
|
183
|
+
abbr -a ${p}ra 'forge release archive'
|
|
184
|
+
|
|
185
|
+
# cd into branch worktree
|
|
186
|
+
function ${p}cd; cd (forge path $argv); end
|
|
187
|
+
function ${p}fcd; cd (forge feature path $argv); end
|
|
188
|
+
function ${p}icd; cd (forge fix path $argv); end
|
|
189
|
+
function ${p}rcd; cd (forge release path $argv); end
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
generatePowerShellAliases(p) {
|
|
193
|
+
return `# feat-forge aliases (generated by: forge alias powershell --prefix ${p})
|
|
194
|
+
# Add to your $PROFILE: forge alias powershell${p !== 'ff' ? ` --prefix ${p}` : ''} | Out-String | Invoke-Expression
|
|
195
|
+
|
|
196
|
+
# Group functions
|
|
197
|
+
function ${p} { forge @args }
|
|
198
|
+
function ${p}f { forge feature @args }
|
|
199
|
+
function ${p}i { forge fix @args }
|
|
200
|
+
function ${p}r { forge release @args }
|
|
201
|
+
|
|
202
|
+
# Command shortcuts
|
|
203
|
+
function ${p}s { forge start @args }
|
|
204
|
+
function ${p}x { forge stop @args }
|
|
205
|
+
function ${p}c { forge create @args }
|
|
206
|
+
function ${p}o { forge open @args }
|
|
207
|
+
function ${p}l { forge list @args }
|
|
208
|
+
function ${p}m { forge merge @args }
|
|
209
|
+
function ${p}a { forge archive @args }
|
|
210
|
+
|
|
211
|
+
# Feature shortcuts
|
|
212
|
+
function ${p}fs { forge feature start @args }
|
|
213
|
+
function ${p}fx { forge feature stop @args }
|
|
214
|
+
function ${p}fc { forge feature create @args }
|
|
215
|
+
function ${p}fo { forge feature open @args }
|
|
216
|
+
function ${p}fl { forge feature list @args }
|
|
217
|
+
function ${p}fm { forge feature merge @args }
|
|
218
|
+
function ${p}fa { forge feature archive @args }
|
|
219
|
+
|
|
220
|
+
# Fix shortcuts
|
|
221
|
+
function ${p}is { forge fix start @args }
|
|
222
|
+
function ${p}ix { forge fix stop @args }
|
|
223
|
+
function ${p}ic { forge fix create @args }
|
|
224
|
+
function ${p}io { forge fix open @args }
|
|
225
|
+
function ${p}il { forge fix list @args }
|
|
226
|
+
function ${p}im { forge fix merge @args }
|
|
227
|
+
function ${p}ia { forge fix archive @args }
|
|
228
|
+
|
|
229
|
+
# Release shortcuts
|
|
230
|
+
function ${p}rs { forge release start @args }
|
|
231
|
+
function ${p}rx { forge release stop @args }
|
|
232
|
+
function ${p}rc { forge release create @args }
|
|
233
|
+
function ${p}ro { forge release open @args }
|
|
234
|
+
function ${p}rl { forge release list @args }
|
|
235
|
+
function ${p}rm { forge release merge @args }
|
|
236
|
+
function ${p}ra { forge release archive @args }
|
|
237
|
+
|
|
238
|
+
# cd into branch worktree
|
|
239
|
+
function ${p}cd { Set-Location (forge path @args) }
|
|
240
|
+
function ${p}fcd { Set-Location (forge feature path @args) }
|
|
241
|
+
function ${p}icd { Set-Location (forge fix path @args) }
|
|
242
|
+
function ${p}rcd { Set-Location (forge release path @args) }
|
|
243
|
+
`;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -192,6 +192,18 @@ export class BranchCommands extends AbstractCommands {
|
|
|
192
192
|
process.exitCode = 1;
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Print the path of a branch worktree directory.
|
|
197
|
+
* Outputs only the path to stdout, suitable for shell capture: cd "$(forge path <branch>)"
|
|
198
|
+
*
|
|
199
|
+
* @param rawSlug - Optional branch slug. If not provided, finds the nearest branch context.
|
|
200
|
+
*/
|
|
201
|
+
async path(rawSlug) {
|
|
202
|
+
const branchContext = rawSlug
|
|
203
|
+
? await this.context.loadBranchContext(await confirmSlugOrThrow(rawSlug))
|
|
204
|
+
: await BranchContext.findNearestBranchContext(this.context);
|
|
205
|
+
process.stdout.write(branchContext.path);
|
|
206
|
+
}
|
|
195
207
|
async merge(rawSlug) {
|
|
196
208
|
const branchName = await confirmSlugOrThrow(rawSlug);
|
|
197
209
|
let targetRepos;
|