@dynamicworks/br-openspec 1.3.1
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 +22 -0
- package/README.md +210 -0
- package/README.pt-BR.md +212 -0
- package/bin/openspec.js +3 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +484 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.js +278 -0
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +258 -0
- package/dist/commands/config.d.ts +36 -0
- package/dist/commands/config.js +553 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +184 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.js +869 -0
- package/dist/commands/show.d.ts +14 -0
- package/dist/commands/show.js +133 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.js +226 -0
- package/dist/commands/tools.d.ts +11 -0
- package/dist/commands/tools.js +252 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +295 -0
- package/dist/commands/workflow/index.d.ts +17 -0
- package/dist/commands/workflow/index.js +12 -0
- package/dist/commands/workflow/instructions.d.ts +29 -0
- package/dist/commands/workflow/instructions.js +328 -0
- package/dist/commands/workflow/new-change.d.ts +11 -0
- package/dist/commands/workflow/new-change.js +44 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.js +35 -0
- package/dist/commands/workflow/shared.d.ts +57 -0
- package/dist/commands/workflow/shared.js +117 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.js +76 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +70 -0
- package/dist/core/archive.d.ts +11 -0
- package/dist/core/archive.js +322 -0
- package/dist/core/artifact-graph/graph.d.ts +56 -0
- package/dist/core/artifact-graph/graph.js +141 -0
- package/dist/core/artifact-graph/index.d.ts +8 -0
- package/dist/core/artifact-graph/index.js +14 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
- package/dist/core/artifact-graph/instruction-loader.js +217 -0
- package/dist/core/artifact-graph/outputs.d.ts +14 -0
- package/dist/core/artifact-graph/outputs.js +39 -0
- package/dist/core/artifact-graph/resolver.d.ts +81 -0
- package/dist/core/artifact-graph/resolver.js +258 -0
- package/dist/core/artifact-graph/schema.d.ts +13 -0
- package/dist/core/artifact-graph/schema.js +108 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.js +31 -0
- package/dist/core/artifact-graph/types.d.ts +45 -0
- package/dist/core/artifact-graph/types.js +43 -0
- package/dist/core/available-tools.d.ts +17 -0
- package/dist/core/available-tools.js +43 -0
- package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
- package/dist/core/command-generation/adapters/amazon-q.js +26 -0
- package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
- package/dist/core/command-generation/adapters/antigravity.js +26 -0
- package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
- package/dist/core/command-generation/adapters/auggie.js +27 -0
- package/dist/core/command-generation/adapters/bob.d.ts +14 -0
- package/dist/core/command-generation/adapters/bob.js +45 -0
- package/dist/core/command-generation/adapters/claude.d.ts +13 -0
- package/dist/core/command-generation/adapters/claude.js +50 -0
- package/dist/core/command-generation/adapters/cline.d.ts +14 -0
- package/dist/core/command-generation/adapters/cline.js +27 -0
- package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
- package/dist/core/command-generation/adapters/codebuddy.js +28 -0
- package/dist/core/command-generation/adapters/codex.d.ts +16 -0
- package/dist/core/command-generation/adapters/codex.js +39 -0
- package/dist/core/command-generation/adapters/continue.d.ts +13 -0
- package/dist/core/command-generation/adapters/continue.js +28 -0
- package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
- package/dist/core/command-generation/adapters/costrict.js +27 -0
- package/dist/core/command-generation/adapters/crush.d.ts +13 -0
- package/dist/core/command-generation/adapters/crush.js +30 -0
- package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
- package/dist/core/command-generation/adapters/cursor.js +44 -0
- package/dist/core/command-generation/adapters/factory.d.ts +13 -0
- package/dist/core/command-generation/adapters/factory.js +27 -0
- package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
- package/dist/core/command-generation/adapters/gemini.js +26 -0
- package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
- package/dist/core/command-generation/adapters/github-copilot.js +26 -0
- package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
- package/dist/core/command-generation/adapters/iflow.js +29 -0
- package/dist/core/command-generation/adapters/index.d.ts +32 -0
- package/dist/core/command-generation/adapters/index.js +32 -0
- package/dist/core/command-generation/adapters/junie.d.ts +13 -0
- package/dist/core/command-generation/adapters/junie.js +26 -0
- package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/kilocode.js +23 -0
- package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
- package/dist/core/command-generation/adapters/kiro.js +26 -0
- package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
- package/dist/core/command-generation/adapters/lingma.js +30 -0
- package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
- package/dist/core/command-generation/adapters/opencode.js +29 -0
- package/dist/core/command-generation/adapters/pi.d.ts +18 -0
- package/dist/core/command-generation/adapters/pi.js +55 -0
- package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
- package/dist/core/command-generation/adapters/qoder.js +30 -0
- package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
- package/dist/core/command-generation/adapters/qwen.js +26 -0
- package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/roocode.js +27 -0
- package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
- package/dist/core/command-generation/adapters/windsurf.js +51 -0
- package/dist/core/command-generation/generator.d.ts +21 -0
- package/dist/core/command-generation/generator.js +27 -0
- package/dist/core/command-generation/index.d.ts +22 -0
- package/dist/core/command-generation/index.js +24 -0
- package/dist/core/command-generation/registry.d.ts +36 -0
- package/dist/core/command-generation/registry.js +98 -0
- package/dist/core/command-generation/types.d.ts +56 -0
- package/dist/core/command-generation/types.js +8 -0
- package/dist/core/completions/command-registry.d.ts +7 -0
- package/dist/core/completions/command-registry.js +462 -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 +64 -0
- package/dist/core/completions/factory.js +75 -0
- package/dist/core/completions/generators/bash-generator.d.ts +32 -0
- package/dist/core/completions/generators/bash-generator.js +174 -0
- package/dist/core/completions/generators/fish-generator.d.ts +32 -0
- package/dist/core/completions/generators/fish-generator.js +157 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +33 -0
- package/dist/core/completions/generators/powershell-generator.js +208 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +44 -0
- package/dist/core/completions/generators/zsh-generator.js +250 -0
- package/dist/core/completions/installers/bash-installer.d.ts +87 -0
- package/dist/core/completions/installers/bash-installer.js +319 -0
- package/dist/core/completions/installers/fish-installer.d.ts +43 -0
- package/dist/core/completions/installers/fish-installer.js +143 -0
- package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
- package/dist/core/completions/installers/powershell-installer.js +400 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +125 -0
- package/dist/core/completions/installers/zsh-installer.js +450 -0
- package/dist/core/completions/templates/bash-templates.d.ts +6 -0
- package/dist/core/completions/templates/bash-templates.js +24 -0
- package/dist/core/completions/templates/fish-templates.d.ts +7 -0
- package/dist/core/completions/templates/fish-templates.js +39 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
- package/dist/core/completions/templates/powershell-templates.js +25 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
- package/dist/core/completions/templates/zsh-templates.js +36 -0
- package/dist/core/completions/types.d.ts +79 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/config-prompts.d.ts +9 -0
- package/dist/core/config-prompts.js +34 -0
- package/dist/core/config-schema.d.ts +86 -0
- package/dist/core/config-schema.js +213 -0
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.js +38 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/global-config.d.ts +44 -0
- package/dist/core/global-config.js +125 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +3 -0
- package/dist/core/init.d.ts +37 -0
- package/dist/core/init.js +549 -0
- package/dist/core/is-project-initialized.d.ts +12 -0
- package/dist/core/is-project-initialized.js +18 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +515 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +172 -0
- package/dist/core/migration.d.ts +23 -0
- package/dist/core/migration.js +109 -0
- package/dist/core/parsers/change-parser.d.ts +13 -0
- package/dist/core/parsers/change-parser.js +197 -0
- package/dist/core/parsers/markdown-parser.d.ts +26 -0
- package/dist/core/parsers/markdown-parser.js +228 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.js +201 -0
- package/dist/core/parsers/spec-structure.d.ts +9 -0
- package/dist/core/parsers/spec-structure.js +88 -0
- package/dist/core/profile-sync-drift.d.ts +38 -0
- package/dist/core/profile-sync-drift.js +200 -0
- package/dist/core/profiles.d.ts +26 -0
- package/dist/core/profiles.js +40 -0
- package/dist/core/project-config.d.ts +64 -0
- package/dist/core/project-config.js +224 -0
- package/dist/core/schemas/base.schema.d.ts +13 -0
- package/dist/core/schemas/base.schema.js +13 -0
- package/dist/core/schemas/change.schema.d.ts +73 -0
- package/dist/core/schemas/change.schema.js +31 -0
- package/dist/core/schemas/index.d.ts +4 -0
- package/dist/core/schemas/index.js +4 -0
- package/dist/core/schemas/spec.schema.d.ts +18 -0
- package/dist/core/schemas/spec.schema.js +15 -0
- package/dist/core/shared/index.d.ts +8 -0
- package/dist/core/shared/index.js +8 -0
- package/dist/core/shared/skill-generation.d.ts +49 -0
- package/dist/core/shared/skill-generation.js +96 -0
- package/dist/core/shared/tool-detection.d.ts +71 -0
- package/dist/core/shared/tool-detection.js +158 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.js +393 -0
- package/dist/core/styles/palette.d.ts +7 -0
- package/dist/core/styles/palette.js +8 -0
- package/dist/core/templates/index.d.ts +8 -0
- package/dist/core/templates/index.js +9 -0
- package/dist/core/templates/skill-templates.d.ts +20 -0
- package/dist/core/templates/skill-templates.js +19 -0
- package/dist/core/templates/types.d.ts +19 -0
- package/dist/core/templates/types.js +5 -0
- package/dist/core/templates/workflows/apply-change.d.ts +10 -0
- package/dist/core/templates/workflows/apply-change.js +308 -0
- package/dist/core/templates/workflows/archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/archive-change.js +271 -0
- package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/bulk-archive-change.js +492 -0
- package/dist/core/templates/workflows/continue-change.d.ts +10 -0
- package/dist/core/templates/workflows/continue-change.js +232 -0
- package/dist/core/templates/workflows/explore.d.ts +10 -0
- package/dist/core/templates/workflows/explore.js +463 -0
- package/dist/core/templates/workflows/feedback.d.ts +9 -0
- package/dist/core/templates/workflows/feedback.js +108 -0
- package/dist/core/templates/workflows/ff-change.d.ts +10 -0
- package/dist/core/templates/workflows/ff-change.js +198 -0
- package/dist/core/templates/workflows/new-change.d.ts +10 -0
- package/dist/core/templates/workflows/new-change.js +21 -0
- package/dist/core/templates/workflows/onboard.d.ts +10 -0
- package/dist/core/templates/workflows/onboard.js +21 -0
- package/dist/core/templates/workflows/propose.d.ts +10 -0
- package/dist/core/templates/workflows/propose.js +216 -0
- package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
- package/dist/core/templates/workflows/sync-specs.js +272 -0
- package/dist/core/templates/workflows/upstream-sync.d.ts +10 -0
- package/dist/core/templates/workflows/upstream-sync.js +116 -0
- package/dist/core/templates/workflows/verify-change.d.ts +10 -0
- package/dist/core/templates/workflows/verify-change.js +21 -0
- package/dist/core/tools-manager.d.ts +56 -0
- package/dist/core/tools-manager.js +215 -0
- package/dist/core/update.d.ts +77 -0
- package/dist/core/update.js +538 -0
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/validator.d.ts +33 -0
- package/dist/core/validation/validator.js +419 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +169 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/messages/index.d.ts +867 -0
- package/dist/messages/index.js +1960 -0
- package/dist/prompts/searchable-multi-select.d.ts +28 -0
- package/dist/prompts/searchable-multi-select.js +160 -0
- package/dist/telemetry/config.d.ts +38 -0
- package/dist/telemetry/config.js +136 -0
- package/dist/telemetry/index.d.ts +31 -0
- package/dist/telemetry/index.js +165 -0
- package/dist/ui/ascii-patterns.d.ts +16 -0
- package/dist/ui/ascii-patterns.js +133 -0
- package/dist/ui/welcome-screen.d.ts +10 -0
- package/dist/ui/welcome-screen.js +147 -0
- package/dist/utils/change-metadata.d.ts +51 -0
- package/dist/utils/change-metadata.js +147 -0
- package/dist/utils/change-utils.d.ts +62 -0
- package/dist/utils/change-utils.js +121 -0
- package/dist/utils/command-references.d.ts +18 -0
- package/dist/utils/command-references.js +20 -0
- package/dist/utils/file-system.d.ts +41 -0
- package/dist/utils/file-system.js +302 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/interactive.d.ts +18 -0
- package/dist/utils/interactive.js +21 -0
- package/dist/utils/item-discovery.d.ts +4 -0
- package/dist/utils/item-discovery.js +72 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.js +41 -0
- package/dist/utils/task-progress.d.ts +8 -0
- package/dist/utils/task-progress.js +37 -0
- package/package.json +84 -0
- package/schemas/spec-driven/schema.yaml +153 -0
- package/schemas/spec-driven/templates/design.md +19 -0
- package/schemas/spec-driven/templates/proposal.md +23 -0
- package/schemas/spec-driven/templates/spec.md +8 -0
- package/schemas/spec-driven/templates/tasks.md +9 -0
- package/scripts/postinstall.js +83 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class ShowCommand {
|
|
2
|
+
execute(itemName?: string, options?: {
|
|
3
|
+
json?: boolean;
|
|
4
|
+
type?: string;
|
|
5
|
+
noInteractive?: boolean;
|
|
6
|
+
[k: string]: any;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
private normalizeType;
|
|
9
|
+
private runInteractiveByType;
|
|
10
|
+
private showDirect;
|
|
11
|
+
private printNonInteractiveHint;
|
|
12
|
+
private warnIrrelevantFlags;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=show.d.ts.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
2
|
+
import { getActiveChangeIds, getSpecIds } from '../utils/item-discovery.js';
|
|
3
|
+
import { ChangeCommand } from './change.js';
|
|
4
|
+
import { SpecCommand } from './spec.js';
|
|
5
|
+
import { nearestMatches } from '../utils/match.js';
|
|
6
|
+
import { SHOW_MESSAGES } from '../messages/index.js';
|
|
7
|
+
const CHANGE_FLAG_KEYS = new Set(['deltasOnly', 'requirementsOnly']);
|
|
8
|
+
const SPEC_FLAG_KEYS = new Set(['requirements', 'scenarios', 'requirement']);
|
|
9
|
+
export class ShowCommand {
|
|
10
|
+
async execute(itemName, options = {}) {
|
|
11
|
+
const interactive = isInteractive(options);
|
|
12
|
+
const typeOverride = this.normalizeType(options.type);
|
|
13
|
+
if (!itemName) {
|
|
14
|
+
if (interactive) {
|
|
15
|
+
const { select } = await import('@inquirer/prompts');
|
|
16
|
+
const type = await select({
|
|
17
|
+
message: SHOW_MESSAGES.whatToShow,
|
|
18
|
+
choices: [
|
|
19
|
+
{ name: SHOW_MESSAGES.optionChange, value: 'change' },
|
|
20
|
+
{ name: SHOW_MESSAGES.optionSpec, value: 'spec' },
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
await this.runInteractiveByType(type, options);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.printNonInteractiveHint();
|
|
27
|
+
process.exitCode = 1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
await this.showDirect(itemName, { typeOverride, options });
|
|
31
|
+
}
|
|
32
|
+
normalizeType(value) {
|
|
33
|
+
if (!value)
|
|
34
|
+
return undefined;
|
|
35
|
+
const v = value.toLowerCase();
|
|
36
|
+
if (v === 'change' || v === 'spec')
|
|
37
|
+
return v;
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
async runInteractiveByType(type, options) {
|
|
41
|
+
const { select } = await import('@inquirer/prompts');
|
|
42
|
+
if (type === 'change') {
|
|
43
|
+
const changes = await getActiveChangeIds();
|
|
44
|
+
if (changes.length === 0) {
|
|
45
|
+
console.error(SHOW_MESSAGES.noChangesFound);
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const picked = await select({ message: SHOW_MESSAGES.pickChange, choices: changes.map(id => ({ name: id, value: id })) });
|
|
50
|
+
const cmd = new ChangeCommand();
|
|
51
|
+
await cmd.show(picked, options);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const specs = await getSpecIds();
|
|
55
|
+
if (specs.length === 0) {
|
|
56
|
+
console.error(SHOW_MESSAGES.noSpecsFound);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const picked = await select({ message: SHOW_MESSAGES.pickSpec, choices: specs.map(id => ({ name: id, value: id })) });
|
|
61
|
+
const cmd = new SpecCommand();
|
|
62
|
+
await cmd.show(picked, options);
|
|
63
|
+
}
|
|
64
|
+
async showDirect(itemName, params) {
|
|
65
|
+
// Optimize lookups when type is pre-specified
|
|
66
|
+
let isChange = false;
|
|
67
|
+
let isSpec = false;
|
|
68
|
+
let changes = [];
|
|
69
|
+
let specs = [];
|
|
70
|
+
if (params.typeOverride === 'change') {
|
|
71
|
+
changes = await getActiveChangeIds();
|
|
72
|
+
isChange = changes.includes(itemName);
|
|
73
|
+
}
|
|
74
|
+
else if (params.typeOverride === 'spec') {
|
|
75
|
+
specs = await getSpecIds();
|
|
76
|
+
isSpec = specs.includes(itemName);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
[changes, specs] = await Promise.all([getActiveChangeIds(), getSpecIds()]);
|
|
80
|
+
isChange = changes.includes(itemName);
|
|
81
|
+
isSpec = specs.includes(itemName);
|
|
82
|
+
}
|
|
83
|
+
const resolvedType = params.typeOverride ?? (isChange ? 'change' : isSpec ? 'spec' : undefined);
|
|
84
|
+
if (!resolvedType) {
|
|
85
|
+
console.error(SHOW_MESSAGES.unknownItem(itemName));
|
|
86
|
+
const suggestions = nearestMatches(itemName, [...changes, ...specs]);
|
|
87
|
+
if (suggestions.length)
|
|
88
|
+
console.error(SHOW_MESSAGES.didYouMean(suggestions.join(', ')));
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!params.typeOverride && isChange && isSpec) {
|
|
93
|
+
console.error(SHOW_MESSAGES.ambiguousItem(itemName));
|
|
94
|
+
console.error(SHOW_MESSAGES.passTypeHint);
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
this.warnIrrelevantFlags(resolvedType, params.options);
|
|
99
|
+
if (resolvedType === 'change') {
|
|
100
|
+
const cmd = new ChangeCommand();
|
|
101
|
+
await cmd.show(itemName, params.options);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const cmd = new SpecCommand();
|
|
105
|
+
await cmd.show(itemName, params.options);
|
|
106
|
+
}
|
|
107
|
+
printNonInteractiveHint() {
|
|
108
|
+
console.error(SHOW_MESSAGES.nothingToShow);
|
|
109
|
+
console.error(SHOW_MESSAGES.showItemHint);
|
|
110
|
+
console.error(SHOW_MESSAGES.showChangeHint);
|
|
111
|
+
console.error(SHOW_MESSAGES.showSpecHint);
|
|
112
|
+
console.error(SHOW_MESSAGES.runInteractiveHint);
|
|
113
|
+
}
|
|
114
|
+
warnIrrelevantFlags(type, options) {
|
|
115
|
+
const irrelevant = [];
|
|
116
|
+
if (type === 'change') {
|
|
117
|
+
for (const k of SPEC_FLAG_KEYS)
|
|
118
|
+
if (k in options)
|
|
119
|
+
irrelevant.push(k);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
for (const k of CHANGE_FLAG_KEYS)
|
|
123
|
+
if (k in options)
|
|
124
|
+
irrelevant.push(k);
|
|
125
|
+
}
|
|
126
|
+
if (irrelevant.length > 0) {
|
|
127
|
+
console.error(SHOW_MESSAGES.ignoringFlags(type, irrelevant.join(', ')));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=show.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { program } from 'commander';
|
|
2
|
+
interface ShowOptions {
|
|
3
|
+
json?: boolean;
|
|
4
|
+
requirements?: boolean;
|
|
5
|
+
scenarios?: boolean;
|
|
6
|
+
requirement?: string;
|
|
7
|
+
noInteractive?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class SpecCommand {
|
|
10
|
+
private SPECS_DIR;
|
|
11
|
+
show(specId?: string, options?: ShowOptions): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function registerSpecCommand(rootProgram: typeof program): import("commander").Command;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=spec.d.ts.map
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { MarkdownParser } from '../core/parsers/markdown-parser.js';
|
|
4
|
+
import { Validator } from '../core/validation/validator.js';
|
|
5
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
6
|
+
import { getSpecIds } from '../utils/item-discovery.js';
|
|
7
|
+
import { CLI_DESCRIPTIONS, CLI_MESSAGES, SPEC_MESSAGES } from '../messages/index.js';
|
|
8
|
+
const SPECS_DIR = 'openspec/specs';
|
|
9
|
+
function parseSpecFromFile(specPath, specId) {
|
|
10
|
+
const content = readFileSync(specPath, 'utf-8');
|
|
11
|
+
const parser = new MarkdownParser(content);
|
|
12
|
+
return parser.parseSpec(specId);
|
|
13
|
+
}
|
|
14
|
+
function validateRequirementIndex(spec, requirementOpt) {
|
|
15
|
+
if (!requirementOpt)
|
|
16
|
+
return undefined;
|
|
17
|
+
const index = Number.parseInt(requirementOpt, 10);
|
|
18
|
+
if (!Number.isInteger(index) || index < 1 || index > spec.requirements.length) {
|
|
19
|
+
throw new Error(SPEC_MESSAGES.requirementNotFound(requirementOpt));
|
|
20
|
+
}
|
|
21
|
+
return index - 1; // convert to 0-based
|
|
22
|
+
}
|
|
23
|
+
function filterSpec(spec, options) {
|
|
24
|
+
const requirementIndex = validateRequirementIndex(spec, options.requirement);
|
|
25
|
+
const includeScenarios = options.scenarios !== false && !options.requirements;
|
|
26
|
+
const filteredRequirements = (requirementIndex !== undefined
|
|
27
|
+
? [spec.requirements[requirementIndex]]
|
|
28
|
+
: spec.requirements).map(req => ({
|
|
29
|
+
text: req.text,
|
|
30
|
+
scenarios: includeScenarios ? req.scenarios : [],
|
|
31
|
+
}));
|
|
32
|
+
const metadata = spec.metadata ?? { version: '1.0.0', format: 'openspec' };
|
|
33
|
+
return {
|
|
34
|
+
name: spec.name,
|
|
35
|
+
overview: spec.overview,
|
|
36
|
+
requirements: filteredRequirements,
|
|
37
|
+
metadata,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Print the raw markdown content for a spec file without any formatting.
|
|
42
|
+
* Raw-first behavior ensures text mode is a passthrough for deterministic output.
|
|
43
|
+
*/
|
|
44
|
+
function printSpecTextRaw(specPath) {
|
|
45
|
+
const content = readFileSync(specPath, 'utf-8');
|
|
46
|
+
console.log(content);
|
|
47
|
+
}
|
|
48
|
+
export class SpecCommand {
|
|
49
|
+
SPECS_DIR = 'openspec/specs';
|
|
50
|
+
async show(specId, options = {}) {
|
|
51
|
+
if (!specId) {
|
|
52
|
+
const canPrompt = isInteractive(options);
|
|
53
|
+
const specIds = await getSpecIds();
|
|
54
|
+
if (canPrompt && specIds.length > 0) {
|
|
55
|
+
const { select } = await import('@inquirer/prompts');
|
|
56
|
+
specId = await select({
|
|
57
|
+
message: SPEC_MESSAGES.selectSpecToShow,
|
|
58
|
+
choices: specIds.map(id => ({ name: id, value: id })),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw new Error(SPEC_MESSAGES.missingSpecId);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const specPath = join(this.SPECS_DIR, specId, 'spec.md');
|
|
66
|
+
if (!existsSync(specPath)) {
|
|
67
|
+
throw new Error(SPEC_MESSAGES.specNotFound(specId));
|
|
68
|
+
}
|
|
69
|
+
if (options.json) {
|
|
70
|
+
if (options.requirements && options.requirement) {
|
|
71
|
+
throw new Error(SPEC_MESSAGES.requirementsAndRequirementConflict);
|
|
72
|
+
}
|
|
73
|
+
const parsed = parseSpecFromFile(specPath, specId);
|
|
74
|
+
const filtered = filterSpec(parsed, options);
|
|
75
|
+
const output = {
|
|
76
|
+
id: specId,
|
|
77
|
+
title: parsed.name,
|
|
78
|
+
overview: parsed.overview,
|
|
79
|
+
requirementCount: filtered.requirements.length,
|
|
80
|
+
requirements: filtered.requirements,
|
|
81
|
+
metadata: parsed.metadata ?? { version: '1.0.0', format: 'openspec' },
|
|
82
|
+
};
|
|
83
|
+
console.log(JSON.stringify(output, null, 2));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
printSpecTextRaw(specPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function registerSpecCommand(rootProgram) {
|
|
90
|
+
const specCommand = rootProgram
|
|
91
|
+
.command('spec')
|
|
92
|
+
.description(CLI_DESCRIPTIONS.spec);
|
|
93
|
+
// Deprecation notice for noun-based commands
|
|
94
|
+
specCommand.hook('preAction', () => {
|
|
95
|
+
console.error(CLI_MESSAGES.specCommandsDeprecated);
|
|
96
|
+
});
|
|
97
|
+
specCommand
|
|
98
|
+
.command('show [spec-id]')
|
|
99
|
+
.description(CLI_DESCRIPTIONS.specShow)
|
|
100
|
+
.option('--json', CLI_DESCRIPTIONS.specShowJson)
|
|
101
|
+
.option('--requirements', CLI_DESCRIPTIONS.specShowRequirements)
|
|
102
|
+
.option('--no-scenarios', CLI_DESCRIPTIONS.specShowNoScenarios)
|
|
103
|
+
.option('-r, --requirement <id>', CLI_DESCRIPTIONS.specShowRequirement)
|
|
104
|
+
.option('--no-interactive', CLI_DESCRIPTIONS.specShowNoInteractive)
|
|
105
|
+
.action(async (specId, options) => {
|
|
106
|
+
try {
|
|
107
|
+
const cmd = new SpecCommand();
|
|
108
|
+
await cmd.show(specId, options);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.error(CLI_MESSAGES.error(error instanceof Error ? error.message : CLI_MESSAGES.unknownError));
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
specCommand
|
|
116
|
+
.command('list')
|
|
117
|
+
.description(CLI_DESCRIPTIONS.specList)
|
|
118
|
+
.option('--json', CLI_DESCRIPTIONS.specListJson)
|
|
119
|
+
.option('--long', CLI_DESCRIPTIONS.specListLong)
|
|
120
|
+
.action((options) => {
|
|
121
|
+
try {
|
|
122
|
+
if (!existsSync(SPECS_DIR)) {
|
|
123
|
+
console.log(SPEC_MESSAGES.noItemsFound);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const specs = readdirSync(SPECS_DIR, { withFileTypes: true })
|
|
127
|
+
.filter(dirent => dirent.isDirectory())
|
|
128
|
+
.map(dirent => {
|
|
129
|
+
const specPath = join(SPECS_DIR, dirent.name, 'spec.md');
|
|
130
|
+
if (existsSync(specPath)) {
|
|
131
|
+
try {
|
|
132
|
+
const spec = parseSpecFromFile(specPath, dirent.name);
|
|
133
|
+
return {
|
|
134
|
+
id: dirent.name,
|
|
135
|
+
title: spec.name,
|
|
136
|
+
requirementCount: spec.requirements.length
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return {
|
|
141
|
+
id: dirent.name,
|
|
142
|
+
title: dirent.name,
|
|
143
|
+
requirementCount: 0
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
})
|
|
149
|
+
.filter((spec) => spec !== null)
|
|
150
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
151
|
+
if (options.json) {
|
|
152
|
+
console.log(JSON.stringify(specs, null, 2));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
if (specs.length === 0) {
|
|
156
|
+
console.log(SPEC_MESSAGES.noItemsFound);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (!options.long) {
|
|
160
|
+
specs.forEach(spec => console.log(spec.id));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
specs.forEach(spec => {
|
|
164
|
+
console.log(`${spec.id}: ${spec.title} ${SPEC_MESSAGES.requirementCount(spec.requirementCount)}`);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.error(CLI_MESSAGES.error(error instanceof Error ? error.message : CLI_MESSAGES.unknownError));
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
specCommand
|
|
174
|
+
.command('validate [spec-id]')
|
|
175
|
+
.description(CLI_DESCRIPTIONS.specValidate)
|
|
176
|
+
.option('--strict', CLI_DESCRIPTIONS.specValidateStrict)
|
|
177
|
+
.option('--json', CLI_DESCRIPTIONS.specValidateJson)
|
|
178
|
+
.option('--no-interactive', CLI_DESCRIPTIONS.specValidateNoInteractive)
|
|
179
|
+
.action(async (specId, options) => {
|
|
180
|
+
try {
|
|
181
|
+
if (!specId) {
|
|
182
|
+
const canPrompt = isInteractive(options);
|
|
183
|
+
const specIds = await getSpecIds();
|
|
184
|
+
if (canPrompt && specIds.length > 0) {
|
|
185
|
+
const { select } = await import('@inquirer/prompts');
|
|
186
|
+
specId = await select({
|
|
187
|
+
message: SPEC_MESSAGES.selectSpecToValidate,
|
|
188
|
+
choices: specIds.map(id => ({ name: id, value: id })),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
throw new Error(SPEC_MESSAGES.missingSpecId);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const specPath = join(SPECS_DIR, specId, 'spec.md');
|
|
196
|
+
if (!existsSync(specPath)) {
|
|
197
|
+
throw new Error(SPEC_MESSAGES.specNotFound(specId));
|
|
198
|
+
}
|
|
199
|
+
const validator = new Validator(options.strict);
|
|
200
|
+
const report = await validator.validateSpec(specPath);
|
|
201
|
+
if (options.json) {
|
|
202
|
+
console.log(JSON.stringify(report, null, 2));
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
if (report.valid) {
|
|
206
|
+
console.log(SPEC_MESSAGES.specIsValid(specId));
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
console.error(SPEC_MESSAGES.specHasIssues(specId));
|
|
210
|
+
report.issues.forEach(issue => {
|
|
211
|
+
const label = issue.level === 'ERROR' ? 'ERROR' : issue.level;
|
|
212
|
+
const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ';
|
|
213
|
+
console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
process.exitCode = report.valid ? 0 : 1;
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
console.error(CLI_MESSAGES.error(error instanceof Error ? error.message : CLI_MESSAGES.unknownError));
|
|
221
|
+
process.exitCode = 1;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
return specCommand;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools Command
|
|
3
|
+
*
|
|
4
|
+
* `openspec tools [path]`
|
|
5
|
+
*
|
|
6
|
+
* Add or remove IDE/Code Agent BR-OpenSpec configuration files interactively.
|
|
7
|
+
* Requires the project to already be initialized with `openspec init`.
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
export declare function registerToolsCommand(program: Command): void;
|
|
11
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools Command
|
|
3
|
+
*
|
|
4
|
+
* `openspec tools [path]`
|
|
5
|
+
*
|
|
6
|
+
* Add or remove IDE/Code Agent BR-OpenSpec configuration files interactively.
|
|
7
|
+
* Requires the project to already be initialized with `openspec init`.
|
|
8
|
+
*/
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
import { isProjectInitialized } from '../core/is-project-initialized.js';
|
|
13
|
+
import { addTool, removeTool, getCurrentToolIds, getEligibleTools, resolveToolsArg, } from '../core/tools-manager.js';
|
|
14
|
+
import { AI_TOOLS } from '../core/config.js';
|
|
15
|
+
import { getToolStates } from '../core/shared/index.js';
|
|
16
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
17
|
+
import { TOOLS_MESSAGES, CLI_MESSAGES } from '../messages/index.js';
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
// Helpers
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
function requireInitialized(projectPath) {
|
|
22
|
+
if (!isProjectInitialized(projectPath)) {
|
|
23
|
+
ora().fail(TOOLS_MESSAGES.notInitialized);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
// Non-interactive add
|
|
29
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
async function runAdd(projectPath, toolsArg) {
|
|
31
|
+
const toolIds = resolveToolsArg(toolsArg);
|
|
32
|
+
if (toolIds.length === 0) {
|
|
33
|
+
console.log(chalk.dim(TOOLS_MESSAGES.noToolsToAdd));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const added = [];
|
|
37
|
+
const failed = [];
|
|
38
|
+
for (const toolId of toolIds) {
|
|
39
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
40
|
+
if (!tool)
|
|
41
|
+
continue;
|
|
42
|
+
const spinner = ora(TOOLS_MESSAGES.adding(tool.name)).start();
|
|
43
|
+
try {
|
|
44
|
+
await addTool(projectPath, tool);
|
|
45
|
+
spinner.succeed(TOOLS_MESSAGES.added(tool.name));
|
|
46
|
+
added.push(tool.name);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
spinner.fail(TOOLS_MESSAGES.failedToAdd(tool.name));
|
|
50
|
+
failed.push({ name: tool.name, error: err });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
console.log();
|
|
54
|
+
if (added.length > 0) {
|
|
55
|
+
console.log(TOOLS_MESSAGES.addedList(added.join(', ')));
|
|
56
|
+
}
|
|
57
|
+
if (failed.length > 0) {
|
|
58
|
+
console.log(chalk.red(TOOLS_MESSAGES.failedList(failed.map((f) => `${f.name} (${f.error.message})`).join(', '))));
|
|
59
|
+
}
|
|
60
|
+
if (added.length > 0) {
|
|
61
|
+
console.log();
|
|
62
|
+
console.log(chalk.white(TOOLS_MESSAGES.restartIDE));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Non-interactive remove
|
|
67
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
68
|
+
async function runRemove(projectPath, toolsArg) {
|
|
69
|
+
const toolIds = resolveToolsArg(toolsArg);
|
|
70
|
+
if (toolIds.length === 0) {
|
|
71
|
+
console.log(chalk.dim(TOOLS_MESSAGES.noToolsToRemove));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const removed = [];
|
|
75
|
+
const failed = [];
|
|
76
|
+
for (const toolId of toolIds) {
|
|
77
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
78
|
+
if (!tool)
|
|
79
|
+
continue;
|
|
80
|
+
const spinner = ora(TOOLS_MESSAGES.removing(tool.name)).start();
|
|
81
|
+
try {
|
|
82
|
+
const counts = await removeTool(projectPath, tool);
|
|
83
|
+
spinner.succeed(TOOLS_MESSAGES.removed(tool.name));
|
|
84
|
+
removed.push(tool.name);
|
|
85
|
+
if (counts.removedSkillCount > 0 || counts.removedCommandCount > 0) {
|
|
86
|
+
console.log(chalk.dim(TOOLS_MESSAGES.removedCounts(counts.removedSkillCount, counts.removedCommandCount)));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
spinner.fail(TOOLS_MESSAGES.failedToRemove(tool.name));
|
|
91
|
+
failed.push({ name: tool.name, error: err });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
console.log();
|
|
95
|
+
if (removed.length > 0) {
|
|
96
|
+
console.log(TOOLS_MESSAGES.removedList(removed.join(', ')));
|
|
97
|
+
}
|
|
98
|
+
if (failed.length > 0) {
|
|
99
|
+
console.log(chalk.red(TOOLS_MESSAGES.failedList(failed.map((f) => `${f.name} (${f.error.message})`).join(', '))));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
+
// Interactive mode
|
|
104
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
async function runInteractive(projectPath) {
|
|
106
|
+
const eligibleTools = getEligibleTools();
|
|
107
|
+
const toolStates = getToolStates(projectPath);
|
|
108
|
+
const currentlyConfigured = getCurrentToolIds(projectPath);
|
|
109
|
+
// Build choices for the multi-select, sorted: configured first, then the rest
|
|
110
|
+
const choices = eligibleTools
|
|
111
|
+
.map((tool) => {
|
|
112
|
+
const status = toolStates.get(tool.value);
|
|
113
|
+
const configured = status?.configured ?? false;
|
|
114
|
+
return {
|
|
115
|
+
name: tool.name,
|
|
116
|
+
value: tool.value,
|
|
117
|
+
configured,
|
|
118
|
+
preSelected: configured,
|
|
119
|
+
};
|
|
120
|
+
})
|
|
121
|
+
.sort((a, b) => {
|
|
122
|
+
if (a.configured && !b.configured)
|
|
123
|
+
return -1;
|
|
124
|
+
if (!a.configured && b.configured)
|
|
125
|
+
return 1;
|
|
126
|
+
return 0;
|
|
127
|
+
});
|
|
128
|
+
if (currentlyConfigured.size > 0) {
|
|
129
|
+
const names = [...currentlyConfigured]
|
|
130
|
+
.map((id) => AI_TOOLS.find((t) => t.value === id)?.name ?? id)
|
|
131
|
+
.join(', ');
|
|
132
|
+
console.log(TOOLS_MESSAGES.currentlyConfigured(names));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
console.log(chalk.dim(TOOLS_MESSAGES.noToolsConfigured));
|
|
136
|
+
}
|
|
137
|
+
console.log();
|
|
138
|
+
const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
|
|
139
|
+
const newSelection = await searchableMultiSelect({
|
|
140
|
+
message: TOOLS_MESSAGES.selectToolsToConfigure(eligibleTools.length),
|
|
141
|
+
pageSize: 15,
|
|
142
|
+
choices,
|
|
143
|
+
});
|
|
144
|
+
const newSet = new Set(newSelection);
|
|
145
|
+
// Compute diffs
|
|
146
|
+
const toAdd = newSelection.filter((id) => !currentlyConfigured.has(id));
|
|
147
|
+
const toRemove = [...currentlyConfigured].filter((id) => !newSet.has(id));
|
|
148
|
+
if (toAdd.length === 0 && toRemove.length === 0) {
|
|
149
|
+
console.log(chalk.dim('\n' + TOOLS_MESSAGES.noChanges));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
console.log();
|
|
153
|
+
const addedNames = [];
|
|
154
|
+
const removedNames = [];
|
|
155
|
+
const failed = [];
|
|
156
|
+
for (const toolId of toAdd) {
|
|
157
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
158
|
+
if (!tool)
|
|
159
|
+
continue;
|
|
160
|
+
const spinner = ora(TOOLS_MESSAGES.adding(tool.name)).start();
|
|
161
|
+
try {
|
|
162
|
+
await addTool(projectPath, tool);
|
|
163
|
+
spinner.succeed(TOOLS_MESSAGES.added(tool.name));
|
|
164
|
+
addedNames.push(tool.name);
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
spinner.fail(TOOLS_MESSAGES.failedToAdd(tool.name));
|
|
168
|
+
failed.push({ name: tool.name, error: err });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (const toolId of toRemove) {
|
|
172
|
+
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
173
|
+
if (!tool)
|
|
174
|
+
continue;
|
|
175
|
+
const spinner = ora(TOOLS_MESSAGES.removing(tool.name)).start();
|
|
176
|
+
try {
|
|
177
|
+
await removeTool(projectPath, tool);
|
|
178
|
+
spinner.succeed(TOOLS_MESSAGES.removed(tool.name));
|
|
179
|
+
removedNames.push(tool.name);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
spinner.fail(TOOLS_MESSAGES.failedToRemove(tool.name));
|
|
183
|
+
failed.push({ name: tool.name, error: err });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
console.log();
|
|
187
|
+
if (addedNames.length > 0) {
|
|
188
|
+
console.log(TOOLS_MESSAGES.addedList(addedNames.join(', ')));
|
|
189
|
+
}
|
|
190
|
+
if (removedNames.length > 0) {
|
|
191
|
+
console.log(TOOLS_MESSAGES.removedList(removedNames.join(', ')));
|
|
192
|
+
}
|
|
193
|
+
if (failed.length > 0) {
|
|
194
|
+
console.log(chalk.red(TOOLS_MESSAGES.failedList(failed.map((f) => `${f.name} (${f.error.message})`).join(', '))));
|
|
195
|
+
}
|
|
196
|
+
if (addedNames.length > 0) {
|
|
197
|
+
console.log();
|
|
198
|
+
console.log(chalk.white(TOOLS_MESSAGES.restartIDE));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
202
|
+
// Command registration
|
|
203
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
204
|
+
export function registerToolsCommand(program) {
|
|
205
|
+
program
|
|
206
|
+
.command('tools [path]')
|
|
207
|
+
.description(TOOLS_MESSAGES.description)
|
|
208
|
+
.option('--add <tools>', TOOLS_MESSAGES.addOption)
|
|
209
|
+
.option('--remove <tools>', TOOLS_MESSAGES.removeOption)
|
|
210
|
+
.action(async (targetPath = '.', options) => {
|
|
211
|
+
try {
|
|
212
|
+
const projectPath = path.resolve(targetPath);
|
|
213
|
+
requireInitialized(projectPath);
|
|
214
|
+
const hasAdd = typeof options?.add === 'string';
|
|
215
|
+
const hasRemove = typeof options?.remove === 'string';
|
|
216
|
+
if (hasAdd && hasRemove) {
|
|
217
|
+
// Check for overlap
|
|
218
|
+
const addIds = resolveToolsArg(options.add);
|
|
219
|
+
const removeIds = resolveToolsArg(options.remove);
|
|
220
|
+
const overlap = addIds.filter((id) => removeIds.includes(id));
|
|
221
|
+
if (overlap.length > 0) {
|
|
222
|
+
throw new Error(TOOLS_MESSAGES.cannotAddAndRemoveSame(overlap.join(', ')));
|
|
223
|
+
}
|
|
224
|
+
// Run both sequentially
|
|
225
|
+
if (addIds.length > 0)
|
|
226
|
+
await runAdd(projectPath, options.add);
|
|
227
|
+
if (removeIds.length > 0)
|
|
228
|
+
await runRemove(projectPath, options.remove);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (hasAdd) {
|
|
232
|
+
await runAdd(projectPath, options.add);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (hasRemove) {
|
|
236
|
+
await runRemove(projectPath, options.remove);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
// Interactive mode
|
|
240
|
+
if (!isInteractive()) {
|
|
241
|
+
throw new Error(TOOLS_MESSAGES.noFlagNonInteractive);
|
|
242
|
+
}
|
|
243
|
+
await runInteractive(projectPath);
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
console.log();
|
|
247
|
+
ora().fail(CLI_MESSAGES.error(error.message));
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface ExecuteOptions {
|
|
2
|
+
all?: boolean;
|
|
3
|
+
changes?: boolean;
|
|
4
|
+
specs?: boolean;
|
|
5
|
+
type?: string;
|
|
6
|
+
strict?: boolean;
|
|
7
|
+
json?: boolean;
|
|
8
|
+
noInteractive?: boolean;
|
|
9
|
+
interactive?: boolean;
|
|
10
|
+
concurrency?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class ValidateCommand {
|
|
13
|
+
execute(itemName: string | undefined, options?: ExecuteOptions): Promise<void>;
|
|
14
|
+
private normalizeType;
|
|
15
|
+
private runInteractiveSelector;
|
|
16
|
+
private printNonInteractiveHint;
|
|
17
|
+
private validateDirectItem;
|
|
18
|
+
private validateByType;
|
|
19
|
+
private printReport;
|
|
20
|
+
private printNextSteps;
|
|
21
|
+
private runBulkValidation;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=validate.d.ts.map
|