@fission-ai/openspec 0.16.0 → 0.17.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 +21 -14
- package/dist/cli/index.js +67 -2
- package/dist/commands/change.js +4 -3
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +221 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +198 -0
- package/dist/commands/show.js +3 -2
- package/dist/commands/spec.js +4 -3
- package/dist/commands/validate.js +21 -2
- package/dist/core/archive.js +4 -1
- package/dist/core/completions/command-registry.d.ts +7 -0
- package/dist/core/completions/command-registry.js +362 -0
- package/dist/core/completions/completion-provider.d.ts +60 -0
- package/dist/core/completions/completion-provider.js +102 -0
- package/dist/core/completions/factory.d.ts +51 -0
- package/dist/core/completions/factory.js +57 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +58 -0
- package/dist/core/completions/generators/zsh-generator.js +319 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +136 -0
- package/dist/core/completions/installers/zsh-installer.js +449 -0
- package/dist/core/completions/types.d.ts +78 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/config-schema.d.ts +76 -0
- package/dist/core/config-schema.js +200 -0
- package/dist/core/configurators/slash/opencode.js +0 -3
- package/dist/core/global-config.d.ts +29 -0
- package/dist/core/global-config.js +87 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -1
- package/dist/utils/file-system.js +19 -3
- package/dist/utils/interactive.d.ts +12 -1
- package/dist/utils/interactive.js +7 -2
- package/dist/utils/item-discovery.d.ts +1 -0
- package/dist/utils/item-discovery.js +23 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.js +41 -0
- package/package.json +4 -1
- package/scripts/postinstall.js +147 -0
package/README.md
CHANGED
|
@@ -85,42 +85,49 @@ See the full comparison in [How OpenSpec Compares](#how-openspec-compares).
|
|
|
85
85
|
|
|
86
86
|
### Supported AI Tools
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
<details>
|
|
89
|
+
<summary><strong>Native Slash Commands</strong> (click to expand)</summary>
|
|
90
|
+
|
|
89
91
|
These tools have built-in OpenSpec commands. Select the OpenSpec integration when prompted.
|
|
90
92
|
|
|
91
93
|
| Tool | Commands |
|
|
92
94
|
|------|----------|
|
|
95
|
+
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
|
|
96
|
+
| **Antigravity** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.agent/workflows/`) |
|
|
97
|
+
| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.augment/commands/`) |
|
|
93
98
|
| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` |
|
|
99
|
+
| **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/openspec-*.md`) |
|
|
94
100
|
| **CodeBuddy Code (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.codebuddy/commands/`) — see [docs](https://www.codebuddy.ai/cli) |
|
|
101
|
+
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
|
|
95
102
|
| **CoStrict** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.cospec/openspec/commands/`) — see [docs](https://costrict.ai)|
|
|
96
|
-
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
|
97
|
-
| **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/openspec-*.md`) |
|
|
98
103
|
| **Crush** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.crush/commands/openspec/`) |
|
|
99
|
-
| **
|
|
104
|
+
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
|
100
105
|
| **Factory Droid** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.factory/commands/`) |
|
|
101
106
|
| **Gemini CLI** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.gemini/commands/openspec/`) |
|
|
102
|
-
| **
|
|
107
|
+
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
|
|
108
|
+
| **iFlow (iflow-cli)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.iflow/commands/`) |
|
|
103
109
|
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/workflows/`) |
|
|
110
|
+
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
|
104
111
|
| **Qoder (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.qoder/commands/openspec/`) — see [docs](https://qoder.com/cli) |
|
|
105
|
-
| **Antigravity** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.agent/workflows/`) |
|
|
106
|
-
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
|
|
107
|
-
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
|
|
108
|
-
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
|
|
109
|
-
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
|
|
110
|
-
| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.augment/commands/`) |
|
|
111
112
|
| **Qwen Code** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.qwen/commands/`) |
|
|
112
|
-
| **
|
|
113
|
-
|
|
113
|
+
| **RooCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.roo/commands/`) |
|
|
114
|
+
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
|
|
114
115
|
|
|
115
116
|
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.
|
|
116
117
|
|
|
117
|
-
|
|
118
|
+
</details>
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary><strong>AGENTS.md Compatible</strong> (click to expand)</summary>
|
|
122
|
+
|
|
118
123
|
These tools automatically read workflow instructions from `openspec/AGENTS.md`. Ask them to follow the OpenSpec workflow if they need a reminder. Learn more about the [AGENTS.md convention](https://agents.md/).
|
|
119
124
|
|
|
120
125
|
| Tools |
|
|
121
126
|
|-------|
|
|
122
127
|
| Amp • Jules • Others |
|
|
123
128
|
|
|
129
|
+
</details>
|
|
130
|
+
|
|
124
131
|
### Install & Initialize
|
|
125
132
|
|
|
126
133
|
#### Prerequisites
|
package/dist/cli/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import { createRequire } from 'module';
|
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { promises as fs } from 'fs';
|
|
6
|
-
import { InitCommand } from '../core/init.js';
|
|
7
6
|
import { AI_TOOLS } from '../core/config.js';
|
|
8
7
|
import { UpdateCommand } from '../core/update.js';
|
|
9
8
|
import { ListCommand } from '../core/list.js';
|
|
@@ -13,6 +12,8 @@ import { registerSpecCommand } from '../commands/spec.js';
|
|
|
13
12
|
import { ChangeCommand } from '../commands/change.js';
|
|
14
13
|
import { ValidateCommand } from '../commands/validate.js';
|
|
15
14
|
import { ShowCommand } from '../commands/show.js';
|
|
15
|
+
import { CompletionCommand } from '../commands/completion.js';
|
|
16
|
+
import { registerConfigCommand } from '../commands/config.js';
|
|
16
17
|
const program = new Command();
|
|
17
18
|
const require = createRequire(import.meta.url);
|
|
18
19
|
const { version } = require('../../package.json');
|
|
@@ -25,7 +26,7 @@ program.option('--no-color', 'Disable color output');
|
|
|
25
26
|
// Apply global flags before any command runs
|
|
26
27
|
program.hook('preAction', (thisCommand) => {
|
|
27
28
|
const opts = thisCommand.opts();
|
|
28
|
-
if (opts.
|
|
29
|
+
if (opts.color === false) {
|
|
29
30
|
process.env.NO_COLOR = '1';
|
|
30
31
|
}
|
|
31
32
|
});
|
|
@@ -57,6 +58,7 @@ program
|
|
|
57
58
|
throw new Error(`Cannot access path "${targetPath}": ${error.message}`);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
61
|
+
const { InitCommand } = await import('../core/init.js');
|
|
60
62
|
const initCommand = new InitCommand({
|
|
61
63
|
tools: options?.tools,
|
|
62
64
|
});
|
|
@@ -192,6 +194,7 @@ program
|
|
|
192
194
|
}
|
|
193
195
|
});
|
|
194
196
|
registerSpecCommand(program);
|
|
197
|
+
registerConfigCommand(program);
|
|
195
198
|
// Top-level validate command
|
|
196
199
|
program
|
|
197
200
|
.command('validate [item-name]')
|
|
@@ -242,5 +245,67 @@ program
|
|
|
242
245
|
process.exit(1);
|
|
243
246
|
}
|
|
244
247
|
});
|
|
248
|
+
// Completion command with subcommands
|
|
249
|
+
const completionCmd = program
|
|
250
|
+
.command('completion')
|
|
251
|
+
.description('Manage shell completions for OpenSpec CLI');
|
|
252
|
+
completionCmd
|
|
253
|
+
.command('generate [shell]')
|
|
254
|
+
.description('Generate completion script for a shell (outputs to stdout)')
|
|
255
|
+
.action(async (shell) => {
|
|
256
|
+
try {
|
|
257
|
+
const completionCommand = new CompletionCommand();
|
|
258
|
+
await completionCommand.generate({ shell });
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.log();
|
|
262
|
+
ora().fail(`Error: ${error.message}`);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
completionCmd
|
|
267
|
+
.command('install [shell]')
|
|
268
|
+
.description('Install completion script for a shell')
|
|
269
|
+
.option('--verbose', 'Show detailed installation output')
|
|
270
|
+
.action(async (shell, options) => {
|
|
271
|
+
try {
|
|
272
|
+
const completionCommand = new CompletionCommand();
|
|
273
|
+
await completionCommand.install({ shell, verbose: options?.verbose });
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
console.log();
|
|
277
|
+
ora().fail(`Error: ${error.message}`);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
completionCmd
|
|
282
|
+
.command('uninstall [shell]')
|
|
283
|
+
.description('Uninstall completion script for a shell')
|
|
284
|
+
.option('-y, --yes', 'Skip confirmation prompts')
|
|
285
|
+
.action(async (shell, options) => {
|
|
286
|
+
try {
|
|
287
|
+
const completionCommand = new CompletionCommand();
|
|
288
|
+
await completionCommand.uninstall({ shell, yes: options?.yes });
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
console.log();
|
|
292
|
+
ora().fail(`Error: ${error.message}`);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
// Hidden command for machine-readable completion data
|
|
297
|
+
program
|
|
298
|
+
.command('__complete <type>', { hidden: true })
|
|
299
|
+
.description('Output completion data in machine-readable format (internal use)')
|
|
300
|
+
.action(async (type) => {
|
|
301
|
+
try {
|
|
302
|
+
const completionCommand = new CompletionCommand();
|
|
303
|
+
await completionCommand.complete({ type });
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
// Silently fail for graceful shell completion experience
|
|
307
|
+
process.exitCode = 1;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
245
310
|
program.parse();
|
|
246
311
|
//# sourceMappingURL=index.js.map
|
package/dist/commands/change.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { select } from '@inquirer/prompts';
|
|
4
3
|
import { JsonConverter } from '../core/converters/json-converter.js';
|
|
5
4
|
import { Validator } from '../core/validation/validator.js';
|
|
6
5
|
import { ChangeParser } from '../core/parsers/change-parser.js';
|
|
@@ -24,9 +23,10 @@ export class ChangeCommand {
|
|
|
24
23
|
async show(changeName, options) {
|
|
25
24
|
const changesPath = path.join(process.cwd(), 'openspec', 'changes');
|
|
26
25
|
if (!changeName) {
|
|
27
|
-
const canPrompt = isInteractive(options
|
|
26
|
+
const canPrompt = isInteractive(options);
|
|
28
27
|
const changes = await this.getActiveChanges(changesPath);
|
|
29
28
|
if (canPrompt && changes.length > 0) {
|
|
29
|
+
const { select } = await import('@inquirer/prompts');
|
|
30
30
|
const selected = await select({
|
|
31
31
|
message: 'Select a change to show',
|
|
32
32
|
choices: changes.map(id => ({ name: id, value: id })),
|
|
@@ -172,9 +172,10 @@ export class ChangeCommand {
|
|
|
172
172
|
async validate(changeName, options) {
|
|
173
173
|
const changesPath = path.join(process.cwd(), 'openspec', 'changes');
|
|
174
174
|
if (!changeName) {
|
|
175
|
-
const canPrompt = isInteractive(options
|
|
175
|
+
const canPrompt = isInteractive(options);
|
|
176
176
|
const changes = await getActiveChangeIds();
|
|
177
177
|
if (canPrompt && changes.length > 0) {
|
|
178
|
+
const { select } = await import('@inquirer/prompts');
|
|
178
179
|
const selected = await select({
|
|
179
180
|
message: 'Select a change to validate',
|
|
180
181
|
choices: changes.map(id => ({ name: id, value: id })),
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
interface GenerateOptions {
|
|
2
|
+
shell?: string;
|
|
3
|
+
}
|
|
4
|
+
interface InstallOptions {
|
|
5
|
+
shell?: string;
|
|
6
|
+
verbose?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface UninstallOptions {
|
|
9
|
+
shell?: string;
|
|
10
|
+
yes?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface CompleteOptions {
|
|
13
|
+
type: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Command for managing shell completions for OpenSpec CLI
|
|
17
|
+
*/
|
|
18
|
+
export declare class CompletionCommand {
|
|
19
|
+
private completionProvider;
|
|
20
|
+
constructor();
|
|
21
|
+
/**
|
|
22
|
+
* Resolve shell parameter or exit with error
|
|
23
|
+
*
|
|
24
|
+
* @param shell - The shell parameter (may be undefined)
|
|
25
|
+
* @param operationName - Name of the operation (for error messages)
|
|
26
|
+
* @returns Resolved shell or null if should exit
|
|
27
|
+
*/
|
|
28
|
+
private resolveShellOrExit;
|
|
29
|
+
/**
|
|
30
|
+
* Generate completion script and output to stdout
|
|
31
|
+
*
|
|
32
|
+
* @param options - Options for generation (shell type)
|
|
33
|
+
*/
|
|
34
|
+
generate(options?: GenerateOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Install completion script to the appropriate location
|
|
37
|
+
*
|
|
38
|
+
* @param options - Options for installation (shell type, verbose output)
|
|
39
|
+
*/
|
|
40
|
+
install(options?: InstallOptions): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Uninstall completion script from the installation location
|
|
43
|
+
*
|
|
44
|
+
* @param options - Options for uninstallation (shell type, yes flag)
|
|
45
|
+
*/
|
|
46
|
+
uninstall(options?: UninstallOptions): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Generate completion script for a specific shell
|
|
49
|
+
*/
|
|
50
|
+
private generateForShell;
|
|
51
|
+
/**
|
|
52
|
+
* Install completion script for a specific shell
|
|
53
|
+
*/
|
|
54
|
+
private installForShell;
|
|
55
|
+
/**
|
|
56
|
+
* Uninstall completion script for a specific shell
|
|
57
|
+
*/
|
|
58
|
+
private uninstallForShell;
|
|
59
|
+
/**
|
|
60
|
+
* Output machine-readable completion data for shell consumption
|
|
61
|
+
* Format: tab-separated "id\tdescription" per line
|
|
62
|
+
*
|
|
63
|
+
* @param options - Options specifying completion type
|
|
64
|
+
*/
|
|
65
|
+
complete(options: CompleteOptions): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Normalize shell parameter to lowercase
|
|
68
|
+
*/
|
|
69
|
+
private normalizeShell;
|
|
70
|
+
}
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=completion.d.ts.map
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import ora from 'ora';
|
|
2
|
+
import { CompletionFactory } from '../core/completions/factory.js';
|
|
3
|
+
import { COMMAND_REGISTRY } from '../core/completions/command-registry.js';
|
|
4
|
+
import { detectShell } from '../utils/shell-detection.js';
|
|
5
|
+
import { CompletionProvider } from '../core/completions/completion-provider.js';
|
|
6
|
+
import { getArchivedChangeIds } from '../utils/item-discovery.js';
|
|
7
|
+
/**
|
|
8
|
+
* Command for managing shell completions for OpenSpec CLI
|
|
9
|
+
*/
|
|
10
|
+
export class CompletionCommand {
|
|
11
|
+
completionProvider;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.completionProvider = new CompletionProvider();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve shell parameter or exit with error
|
|
17
|
+
*
|
|
18
|
+
* @param shell - The shell parameter (may be undefined)
|
|
19
|
+
* @param operationName - Name of the operation (for error messages)
|
|
20
|
+
* @returns Resolved shell or null if should exit
|
|
21
|
+
*/
|
|
22
|
+
resolveShellOrExit(shell, operationName) {
|
|
23
|
+
const normalizedShell = this.normalizeShell(shell);
|
|
24
|
+
if (!normalizedShell) {
|
|
25
|
+
const detectionResult = detectShell();
|
|
26
|
+
if (detectionResult.shell && CompletionFactory.isSupported(detectionResult.shell)) {
|
|
27
|
+
return detectionResult.shell;
|
|
28
|
+
}
|
|
29
|
+
// Shell was detected but not supported
|
|
30
|
+
if (detectionResult.detected && !detectionResult.shell) {
|
|
31
|
+
console.error(`Error: Shell '${detectionResult.detected}' is not supported yet. Currently supported: ${CompletionFactory.getSupportedShells().join(', ')}`);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// No shell specified and cannot auto-detect
|
|
36
|
+
console.error('Error: Could not auto-detect shell. Please specify shell explicitly.');
|
|
37
|
+
console.error(`Usage: openspec completion ${operationName} [shell]`);
|
|
38
|
+
console.error(`Currently supported: ${CompletionFactory.getSupportedShells().join(', ')}`);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (!CompletionFactory.isSupported(normalizedShell)) {
|
|
43
|
+
console.error(`Error: Shell '${normalizedShell}' is not supported yet. Currently supported: ${CompletionFactory.getSupportedShells().join(', ')}`);
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return normalizedShell;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generate completion script and output to stdout
|
|
51
|
+
*
|
|
52
|
+
* @param options - Options for generation (shell type)
|
|
53
|
+
*/
|
|
54
|
+
async generate(options = {}) {
|
|
55
|
+
const shell = this.resolveShellOrExit(options.shell, 'generate');
|
|
56
|
+
if (!shell)
|
|
57
|
+
return;
|
|
58
|
+
await this.generateForShell(shell);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Install completion script to the appropriate location
|
|
62
|
+
*
|
|
63
|
+
* @param options - Options for installation (shell type, verbose output)
|
|
64
|
+
*/
|
|
65
|
+
async install(options = {}) {
|
|
66
|
+
const shell = this.resolveShellOrExit(options.shell, 'install');
|
|
67
|
+
if (!shell)
|
|
68
|
+
return;
|
|
69
|
+
await this.installForShell(shell, options.verbose || false);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Uninstall completion script from the installation location
|
|
73
|
+
*
|
|
74
|
+
* @param options - Options for uninstallation (shell type, yes flag)
|
|
75
|
+
*/
|
|
76
|
+
async uninstall(options = {}) {
|
|
77
|
+
const shell = this.resolveShellOrExit(options.shell, 'uninstall');
|
|
78
|
+
if (!shell)
|
|
79
|
+
return;
|
|
80
|
+
await this.uninstallForShell(shell, options.yes || false);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Generate completion script for a specific shell
|
|
84
|
+
*/
|
|
85
|
+
async generateForShell(shell) {
|
|
86
|
+
const generator = CompletionFactory.createGenerator(shell);
|
|
87
|
+
const script = generator.generate(COMMAND_REGISTRY);
|
|
88
|
+
console.log(script);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Install completion script for a specific shell
|
|
92
|
+
*/
|
|
93
|
+
async installForShell(shell, verbose) {
|
|
94
|
+
const generator = CompletionFactory.createGenerator(shell);
|
|
95
|
+
const installer = CompletionFactory.createInstaller(shell);
|
|
96
|
+
const spinner = ora(`Installing ${shell} completion script...`).start();
|
|
97
|
+
try {
|
|
98
|
+
// Generate the completion script
|
|
99
|
+
const script = generator.generate(COMMAND_REGISTRY);
|
|
100
|
+
// Install it
|
|
101
|
+
const result = await installer.install(script);
|
|
102
|
+
spinner.stop();
|
|
103
|
+
if (result.success) {
|
|
104
|
+
console.log(`✓ ${result.message}`);
|
|
105
|
+
if (verbose && result.installedPath) {
|
|
106
|
+
console.log(` Installed to: ${result.installedPath}`);
|
|
107
|
+
if (result.backupPath) {
|
|
108
|
+
console.log(` Backup created: ${result.backupPath}`);
|
|
109
|
+
}
|
|
110
|
+
if (result.zshrcConfigured) {
|
|
111
|
+
console.log(` ~/.zshrc configured automatically`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Print instructions (only shown if .zshrc wasn't auto-configured)
|
|
115
|
+
if (result.instructions && result.instructions.length > 0) {
|
|
116
|
+
console.log('');
|
|
117
|
+
for (const instruction of result.instructions) {
|
|
118
|
+
console.log(instruction);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (result.zshrcConfigured) {
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log('Restart your shell or run: exec zsh');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.error(`✗ ${result.message}`);
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
spinner.stop();
|
|
133
|
+
console.error(`✗ Failed to install completion script: ${error instanceof Error ? error.message : String(error)}`);
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Uninstall completion script for a specific shell
|
|
139
|
+
*/
|
|
140
|
+
async uninstallForShell(shell, skipConfirmation) {
|
|
141
|
+
const installer = CompletionFactory.createInstaller(shell);
|
|
142
|
+
// Prompt for confirmation unless --yes flag is provided
|
|
143
|
+
if (!skipConfirmation) {
|
|
144
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
145
|
+
const confirmed = await confirm({
|
|
146
|
+
message: 'Remove OpenSpec configuration from ~/.zshrc?',
|
|
147
|
+
default: false,
|
|
148
|
+
});
|
|
149
|
+
if (!confirmed) {
|
|
150
|
+
console.log('Uninstall cancelled.');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const spinner = ora(`Uninstalling ${shell} completion script...`).start();
|
|
155
|
+
try {
|
|
156
|
+
const result = await installer.uninstall();
|
|
157
|
+
spinner.stop();
|
|
158
|
+
if (result.success) {
|
|
159
|
+
console.log(`✓ ${result.message}`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.error(`✗ ${result.message}`);
|
|
163
|
+
process.exitCode = 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
spinner.stop();
|
|
168
|
+
console.error(`✗ Failed to uninstall completion script: ${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
+
process.exitCode = 1;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Output machine-readable completion data for shell consumption
|
|
174
|
+
* Format: tab-separated "id\tdescription" per line
|
|
175
|
+
*
|
|
176
|
+
* @param options - Options specifying completion type
|
|
177
|
+
*/
|
|
178
|
+
async complete(options) {
|
|
179
|
+
const type = options.type.toLowerCase();
|
|
180
|
+
try {
|
|
181
|
+
switch (type) {
|
|
182
|
+
case 'changes': {
|
|
183
|
+
const changeIds = await this.completionProvider.getChangeIds();
|
|
184
|
+
for (const id of changeIds) {
|
|
185
|
+
console.log(`${id}\tactive change`);
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case 'specs': {
|
|
190
|
+
const specIds = await this.completionProvider.getSpecIds();
|
|
191
|
+
for (const id of specIds) {
|
|
192
|
+
console.log(`${id}\tspecification`);
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case 'archived-changes': {
|
|
197
|
+
const archivedIds = await getArchivedChangeIds();
|
|
198
|
+
for (const id of archivedIds) {
|
|
199
|
+
console.log(`${id}\tarchived change`);
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
default:
|
|
204
|
+
// Invalid type - silently exit with no output for graceful shell completion failure
|
|
205
|
+
process.exitCode = 1;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Silently fail for graceful shell completion experience
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Normalize shell parameter to lowercase
|
|
216
|
+
*/
|
|
217
|
+
normalizeShell(shell) {
|
|
218
|
+
return shell?.toLowerCase();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Register the config command and all its subcommands.
|
|
4
|
+
*
|
|
5
|
+
* @param program - The Commander program instance
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerConfigCommand(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=config.d.ts.map
|