@codewalla_india/openspec 1.0.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 +225 -0
- package/bin/openspec.js +5 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/index.js +548 -0
- package/dist/commands/change.d.ts +39 -0
- package/dist/commands/change.js +279 -0
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +264 -0
- package/dist/commands/config.d.ts +36 -0
- package/dist/commands/config.js +552 -0
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.js +155 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.js +163 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +183 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.js +869 -0
- package/dist/commands/shared-gather.d.ts +14 -0
- package/dist/commands/shared-gather.js +31 -0
- package/dist/commands/shared-output.d.ts +18 -0
- package/dist/commands/shared-output.js +61 -0
- package/dist/commands/show.d.ts +19 -0
- package/dist/commands/show.js +177 -0
- package/dist/commands/spec.d.ts +19 -0
- package/dist/commands/spec.js +236 -0
- package/dist/commands/store.d.ts +3 -0
- package/dist/commands/store.js +547 -0
- package/dist/commands/validate.d.ts +26 -0
- package/dist/commands/validate.js +330 -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 +45 -0
- package/dist/commands/workflow/instructions.js +500 -0
- package/dist/commands/workflow/new-change.d.ts +20 -0
- package/dist/commands/workflow/new-change.js +106 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.js +34 -0
- package/dist/commands/workflow/shared.d.ts +84 -0
- package/dist/commands/workflow/shared.js +133 -0
- package/dist/commands/workflow/status.d.ts +16 -0
- package/dist/commands/workflow/status.js +92 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +69 -0
- package/dist/commands/workset-input.d.ts +19 -0
- package/dist/commands/workset-input.js +112 -0
- package/dist/commands/workset-prompts.d.ts +12 -0
- package/dist/commands/workset-prompts.js +143 -0
- package/dist/commands/workset.d.ts +25 -0
- package/dist/commands/workset.js +446 -0
- package/dist/core/archive.d.ts +22 -0
- package/dist/core/archive.js +471 -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 +9 -0
- package/dist/core/artifact-graph/index.js +14 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +188 -0
- package/dist/core/artifact-graph/instruction-loader.js +233 -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 +257 -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 +40 -0
- package/dist/core/artifact-graph/types.js +29 -0
- package/dist/core/available-tools.d.ts +17 -0
- package/dist/core/available-tools.js +43 -0
- package/dist/core/change-metadata/index.d.ts +2 -0
- package/dist/core/change-metadata/index.js +2 -0
- package/dist/core/change-metadata/schema.d.ts +19 -0
- package/dist/core/change-metadata/schema.js +30 -0
- package/dist/core/change-status-policy.d.ts +37 -0
- package/dist/core/change-status-policy.js +35 -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 +32 -0
- package/dist/core/command-generation/adapters/claude.d.ts +13 -0
- package/dist/core/command-generation/adapters/claude.js +37 -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 +31 -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 +42 -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 +38 -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/command-generation/yaml.d.ts +22 -0
- package/dist/core/command-generation/yaml.js +38 -0
- package/dist/core/completions/command-registry.d.ts +3 -0
- package/dist/core/completions/command-registry.js +778 -0
- package/dist/core/completions/completion-provider.d.ts +71 -0
- package/dist/core/completions/completion-provider.js +129 -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 +35 -0
- package/dist/core/completions/generators/bash-generator.js +230 -0
- package/dist/core/completions/generators/fish-generator.d.ts +32 -0
- package/dist/core/completions/generators/fish-generator.js +160 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +36 -0
- package/dist/core/completions/generators/powershell-generator.js +266 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +47 -0
- package/dist/core/completions/generators/zsh-generator.js +276 -0
- package/dist/core/completions/installers/bash-installer.d.ts +87 -0
- package/dist/core/completions/installers/bash-installer.js +321 -0
- package/dist/core/completions/installers/fish-installer.d.ts +43 -0
- package/dist/core/completions/installers/fish-installer.js +151 -0
- package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
- package/dist/core/completions/installers/powershell-installer.js +415 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +117 -0
- package/dist/core/completions/installers/zsh-installer.js +424 -0
- package/dist/core/completions/shared-flags.d.ts +13 -0
- package/dist/core/completions/shared-flags.js +33 -0
- package/dist/core/completions/templates/bash-templates.d.ts +6 -0
- package/dist/core/completions/templates/bash-templates.js +30 -0
- package/dist/core/completions/templates/fish-templates.d.ts +7 -0
- package/dist/core/completions/templates/fish-templates.js +45 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
- package/dist/core/completions/templates/powershell-templates.js +34 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
- package/dist/core/completions/templates/zsh-templates.js +45 -0
- package/dist/core/completions/types.d.ts +101 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/comprehension/config.d.ts +20 -0
- package/dist/core/comprehension/config.js +23 -0
- package/dist/core/comprehension/fingerprint.d.ts +5 -0
- package/dist/core/comprehension/fingerprint.js +25 -0
- package/dist/core/comprehension/index.d.ts +49 -0
- package/dist/core/comprehension/index.js +78 -0
- package/dist/core/comprehension/pass-record.d.ts +29 -0
- package/dist/core/comprehension/pass-record.js +64 -0
- package/dist/core/comprehension/stats.d.ts +18 -0
- package/dist/core/comprehension/stats.js +41 -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 +87 -0
- package/dist/core/config-schema.js +239 -0
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.js +39 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/file-state.d.ts +36 -0
- package/dist/core/file-state.js +112 -0
- package/dist/core/global-config.d.ts +51 -0
- package/dist/core/global-config.js +124 -0
- package/dist/core/id.d.ts +17 -0
- package/dist/core/id.js +30 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +7 -0
- package/dist/core/init.d.ts +37 -0
- package/dist/core/init.js +613 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +514 -0
- package/dist/core/list.d.ts +11 -0
- package/dist/core/list.js +185 -0
- package/dist/core/migration.d.ts +23 -0
- package/dist/core/migration.js +108 -0
- package/dist/core/openers.d.ts +77 -0
- package/dist/core/openers.js +251 -0
- package/dist/core/openspec-root.d.ts +45 -0
- package/dist/core/openspec-root.js +192 -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 +227 -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/planning-home.d.ts +16 -0
- package/dist/core/planning-home.js +67 -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 +120 -0
- package/dist/core/project-config.js +406 -0
- package/dist/core/references.d.ts +63 -0
- package/dist/core/references.js +310 -0
- package/dist/core/relationship-health.d.ts +65 -0
- package/dist/core/relationship-health.js +64 -0
- package/dist/core/root-selection.d.ts +122 -0
- package/dist/core/root-selection.js +337 -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 +78 -0
- package/dist/core/specs-apply.js +394 -0
- package/dist/core/store/errors.d.ts +20 -0
- package/dist/core/store/errors.js +22 -0
- package/dist/core/store/foundation.d.ts +56 -0
- package/dist/core/store/foundation.js +251 -0
- package/dist/core/store/git.d.ts +23 -0
- package/dist/core/store/git.js +137 -0
- package/dist/core/store/index.d.ts +5 -0
- package/dist/core/store/index.js +5 -0
- package/dist/core/store/operations.d.ts +114 -0
- package/dist/core/store/operations.js +783 -0
- package/dist/core/store/registry.d.ts +58 -0
- package/dist/core/store/registry.js +275 -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 +19 -0
- package/dist/core/templates/skill-templates.js +18 -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 +337 -0
- package/dist/core/templates/workflows/archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/archive-change.js +278 -0
- package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/bulk-archive-change.js +493 -0
- package/dist/core/templates/workflows/comprehension-guidance.d.ts +9 -0
- package/dist/core/templates/workflows/comprehension-guidance.js +58 -0
- package/dist/core/templates/workflows/continue-change.d.ts +10 -0
- package/dist/core/templates/workflows/continue-change.js +239 -0
- package/dist/core/templates/workflows/explore.d.ts +10 -0
- package/dist/core/templates/workflows/explore.js +464 -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 +205 -0
- package/dist/core/templates/workflows/mcp-guidance.d.ts +13 -0
- package/dist/core/templates/workflows/mcp-guidance.js +116 -0
- package/dist/core/templates/workflows/new-change.d.ts +10 -0
- package/dist/core/templates/workflows/new-change.js +148 -0
- package/dist/core/templates/workflows/onboard.d.ts +10 -0
- package/dist/core/templates/workflows/onboard.js +566 -0
- package/dist/core/templates/workflows/propose.d.ts +10 -0
- package/dist/core/templates/workflows/propose.js +228 -0
- package/dist/core/templates/workflows/store-selection.d.ts +8 -0
- package/dist/core/templates/workflows/store-selection.js +8 -0
- package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
- package/dist/core/templates/workflows/sync-specs.js +291 -0
- package/dist/core/templates/workflows/verify-change.d.ts +10 -0
- package/dist/core/templates/workflows/verify-change.js +346 -0
- package/dist/core/update.d.ts +82 -0
- package/dist/core/update.js +557 -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 +44 -0
- package/dist/core/validation/validator.js +435 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +168 -0
- package/dist/core/working-set.d.ts +47 -0
- package/dist/core/working-set.js +43 -0
- package/dist/core/worksets.d.ts +75 -0
- package/dist/core/worksets.js +245 -0
- package/dist/core/zod-issues.d.ts +4 -0
- package/dist/core/zod-issues.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/prompts/searchable-multi-select.d.ts +28 -0
- package/dist/prompts/searchable-multi-select.js +159 -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 +164 -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 +146 -0
- package/dist/utils/change-metadata.d.ts +55 -0
- package/dist/utils/change-metadata.js +141 -0
- package/dist/utils/change-utils.d.ts +71 -0
- package/dist/utils/change-utils.js +138 -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 +320 -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 +36 -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
|
+
import { type RegistrySnapshot } from '../core/store/registry.js';
|
|
2
|
+
import { type ProjectConfig } from '../core/project-config.js';
|
|
3
|
+
import { type ReferenceIndexEntry } from '../core/references.js';
|
|
4
|
+
import { type OpenSpecRootInspection } from '../core/openspec-root.js';
|
|
5
|
+
import type { ResolvedOpenSpecRoot } from '../core/root-selection.js';
|
|
6
|
+
export interface RelationshipData {
|
|
7
|
+
registrySnapshot: RegistrySnapshot;
|
|
8
|
+
projectConfig: ProjectConfig | null;
|
|
9
|
+
storeConfigPath: string;
|
|
10
|
+
referenceEntries: ReferenceIndexEntry[];
|
|
11
|
+
rootInspection: OpenSpecRootInspection;
|
|
12
|
+
}
|
|
13
|
+
export declare function gatherRelationshipData(root: ResolvedOpenSpecRoot): Promise<RelationshipData>;
|
|
14
|
+
//# sourceMappingURL=shared-gather.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relationship-data gather shared by doctor and context (4.1): one
|
|
3
|
+
* registry snapshot, the health-mode reference index, and the root
|
|
4
|
+
* inspection. Doctor layers its health-only inputs (store facts,
|
|
5
|
+
* wrong-turn detection) on top.
|
|
6
|
+
*/
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import { readRegistrySnapshot } from '../core/store/registry.js';
|
|
9
|
+
import { readProjectConfig, resolveConfigFilePath, } from '../core/project-config.js';
|
|
10
|
+
import { assembleReferenceIndex } from '../core/references.js';
|
|
11
|
+
import { inspectOpenSpecRoot } from '../core/openspec-root.js';
|
|
12
|
+
export async function gatherRelationshipData(root) {
|
|
13
|
+
const registrySnapshot = await readRegistrySnapshot();
|
|
14
|
+
const projectConfig = readProjectConfig(root.path);
|
|
15
|
+
const storeConfigPath = resolveConfigFilePath(root.path) ?? path.join(root.path, 'openspec', 'config.yaml');
|
|
16
|
+
const referenceEntries = await assembleReferenceIndex({
|
|
17
|
+
references: projectConfig?.references ?? [],
|
|
18
|
+
resolvedRoot: root,
|
|
19
|
+
includeSpecs: false,
|
|
20
|
+
registryEntries: registrySnapshot.entries,
|
|
21
|
+
});
|
|
22
|
+
const rootInspection = await inspectOpenSpecRoot(root.path);
|
|
23
|
+
return {
|
|
24
|
+
registrySnapshot,
|
|
25
|
+
projectConfig,
|
|
26
|
+
storeConfigPath,
|
|
27
|
+
referenceEntries,
|
|
28
|
+
rootInspection,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=shared-gather.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON/failure output plumbing for command groups whose errors
|
|
3
|
+
* carry the StoreDiagnostic envelope. One definition of the failure
|
|
4
|
+
* contract: exit code 1, Error:/Fix: lines in human mode, a status
|
|
5
|
+
* array in JSON mode.
|
|
6
|
+
*/
|
|
7
|
+
import { type StoreDiagnostic } from '../core/store/errors.js';
|
|
8
|
+
export declare function printJson(payload: unknown): void;
|
|
9
|
+
export declare function asErrorMessage(error: unknown): string;
|
|
10
|
+
/**
|
|
11
|
+
* @inquirer prompts reject with ExitPromptError on Ctrl-C; commands
|
|
12
|
+
* translate that to `Cancelled.` + exit 130 (third caller extracted
|
|
13
|
+
* this here in slice 7.1).
|
|
14
|
+
*/
|
|
15
|
+
export declare function isPromptCancellationError(error: unknown): boolean;
|
|
16
|
+
export declare function asStatus(error: unknown, fallbackCode: string): StoreDiagnostic;
|
|
17
|
+
export declare function emitFailure(json: boolean | undefined, payload: Record<string, unknown>, error: unknown, fallbackCode: string): void;
|
|
18
|
+
//# sourceMappingURL=shared-output.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON/failure output plumbing for command groups whose errors
|
|
3
|
+
* carry the StoreDiagnostic envelope. One definition of the failure
|
|
4
|
+
* contract: exit code 1, Error:/Fix: lines in human mode, a status
|
|
5
|
+
* array in JSON mode.
|
|
6
|
+
*/
|
|
7
|
+
import { StoreError } from '../core/store/errors.js';
|
|
8
|
+
export function printJson(payload) {
|
|
9
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
10
|
+
}
|
|
11
|
+
export function asErrorMessage(error) {
|
|
12
|
+
return error instanceof Error ? error.message : String(error);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @inquirer prompts reject with ExitPromptError on Ctrl-C; commands
|
|
16
|
+
* translate that to `Cancelled.` + exit 130 (third caller extracted
|
|
17
|
+
* this here in slice 7.1).
|
|
18
|
+
*/
|
|
19
|
+
export function isPromptCancellationError(error) {
|
|
20
|
+
return (error instanceof Error &&
|
|
21
|
+
(error.name === 'ExitPromptError' ||
|
|
22
|
+
error.message.includes('force closed the prompt with SIGINT')));
|
|
23
|
+
}
|
|
24
|
+
export function asStatus(error, fallbackCode) {
|
|
25
|
+
if (error instanceof StoreError) {
|
|
26
|
+
return error.diagnostic;
|
|
27
|
+
}
|
|
28
|
+
// RootSelectionError (and siblings) carry the same envelope without
|
|
29
|
+
// sharing a class hierarchy; duck-type the diagnostic once, here.
|
|
30
|
+
const diagnostic = error.diagnostic;
|
|
31
|
+
if (diagnostic && typeof diagnostic.code === 'string') {
|
|
32
|
+
return diagnostic;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
severity: 'error',
|
|
36
|
+
code: fallbackCode,
|
|
37
|
+
message: asErrorMessage(error),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function emitFailure(json, payload, error, fallbackCode) {
|
|
41
|
+
// Ctrl-C in a prompt is the user's choice, not an error: every
|
|
42
|
+
// command group gets the Cancelled./130 convention through here.
|
|
43
|
+
if (!json && isPromptCancellationError(error)) {
|
|
44
|
+
console.error('Cancelled.');
|
|
45
|
+
process.exitCode = 130;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const status = asStatus(error, fallbackCode);
|
|
49
|
+
if (json) {
|
|
50
|
+
const prior = Array.isArray(payload.status) ? payload.status : [];
|
|
51
|
+
printJson({ ...payload, status: [...prior, status] });
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
console.error(`Error: ${status.message}`);
|
|
56
|
+
if (status.fix) {
|
|
57
|
+
console.error(`Fix: ${status.fix}`);
|
|
58
|
+
}
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=shared-output.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface ShowExecuteOptions {
|
|
2
|
+
json?: boolean;
|
|
3
|
+
type?: string;
|
|
4
|
+
noInteractive?: boolean;
|
|
5
|
+
store?: string;
|
|
6
|
+
storePath?: string;
|
|
7
|
+
[k: string]: any;
|
|
8
|
+
}
|
|
9
|
+
export declare class ShowCommand {
|
|
10
|
+
execute(itemName?: string, options?: ShowExecuteOptions): Promise<void>;
|
|
11
|
+
private normalizeType;
|
|
12
|
+
private delegateOptions;
|
|
13
|
+
private runInteractiveByType;
|
|
14
|
+
private showDirect;
|
|
15
|
+
private printNonInteractiveHint;
|
|
16
|
+
private warnIrrelevantFlags;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=show.d.ts.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { isInteractive } from '../utils/interactive.js';
|
|
2
|
+
import { getActiveChangeIds, getSpecIds } from '../utils/item-discovery.js';
|
|
3
|
+
import { resolveRootForCommand, toRootOutput, withStoreFlag, isStoreSelectedRoot, } from '../core/root-selection.js';
|
|
4
|
+
import { ChangeCommand } from './change.js';
|
|
5
|
+
import { SpecCommand } from './spec.js';
|
|
6
|
+
import { nearestMatches } from '../utils/match.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 root = await resolveRootForCommand(options, { json: options.json });
|
|
12
|
+
if (!root) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const interactive = isInteractive(options);
|
|
16
|
+
const typeOverride = this.normalizeType(options.type);
|
|
17
|
+
if (!itemName) {
|
|
18
|
+
if (interactive) {
|
|
19
|
+
const { select } = await import('@inquirer/prompts');
|
|
20
|
+
const type = await select({
|
|
21
|
+
message: 'What would you like to show?',
|
|
22
|
+
choices: [
|
|
23
|
+
{ name: 'Change', value: 'change' },
|
|
24
|
+
{ name: 'Spec', value: 'spec' },
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
await this.runInteractiveByType(type, options, root);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.printNonInteractiveHint(root);
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
await this.showDirect(itemName, { typeOverride, options, root });
|
|
35
|
+
}
|
|
36
|
+
normalizeType(value) {
|
|
37
|
+
if (!value)
|
|
38
|
+
return undefined;
|
|
39
|
+
const v = value.toLowerCase();
|
|
40
|
+
if (v === 'change' || v === 'spec')
|
|
41
|
+
return v;
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
delegateOptions(root, options) {
|
|
45
|
+
return {
|
|
46
|
+
...options,
|
|
47
|
+
...(options.json ? { rootOutput: toRootOutput(root) } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async runInteractiveByType(type, options, root) {
|
|
51
|
+
const { select } = await import('@inquirer/prompts');
|
|
52
|
+
if (type === 'change') {
|
|
53
|
+
const changes = await getActiveChangeIds(root.path);
|
|
54
|
+
if (changes.length === 0) {
|
|
55
|
+
console.error('No changes found.');
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const picked = await select({ message: 'Pick a change', choices: changes.map(id => ({ name: id, value: id })) });
|
|
60
|
+
const cmd = new ChangeCommand(root.path);
|
|
61
|
+
await cmd.show(picked, this.delegateOptions(root, options));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const specs = await getSpecIds(root.path);
|
|
65
|
+
if (specs.length === 0) {
|
|
66
|
+
console.error('No specs found.');
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const picked = await select({ message: 'Pick a spec', choices: specs.map(id => ({ name: id, value: id })) });
|
|
71
|
+
const cmd = new SpecCommand(root.path);
|
|
72
|
+
await cmd.show(picked, this.delegateOptions(root, options));
|
|
73
|
+
}
|
|
74
|
+
async showDirect(itemName, params) {
|
|
75
|
+
const root = params.root;
|
|
76
|
+
// Optimize lookups when type is pre-specified
|
|
77
|
+
let isChange = false;
|
|
78
|
+
let isSpec = false;
|
|
79
|
+
let changes = [];
|
|
80
|
+
let specs = [];
|
|
81
|
+
if (params.typeOverride === 'change') {
|
|
82
|
+
changes = await getActiveChangeIds(root.path);
|
|
83
|
+
isChange = changes.includes(itemName);
|
|
84
|
+
}
|
|
85
|
+
else if (params.typeOverride === 'spec') {
|
|
86
|
+
specs = await getSpecIds(root.path);
|
|
87
|
+
isSpec = specs.includes(itemName);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
[changes, specs] = await Promise.all([getActiveChangeIds(root.path), getSpecIds(root.path)]);
|
|
91
|
+
isChange = changes.includes(itemName);
|
|
92
|
+
isSpec = specs.includes(itemName);
|
|
93
|
+
}
|
|
94
|
+
const resolvedType = params.typeOverride ?? (isChange ? 'change' : isSpec ? 'spec' : undefined);
|
|
95
|
+
if (!resolvedType) {
|
|
96
|
+
const suggestions = nearestMatches(itemName, [...changes, ...specs]);
|
|
97
|
+
const message = suggestions.length
|
|
98
|
+
? `Unknown item '${itemName}'. Did you mean: ${suggestions.join(', ')}?`
|
|
99
|
+
: `Unknown item '${itemName}'.`;
|
|
100
|
+
if (params.options.json) {
|
|
101
|
+
console.log(JSON.stringify({ status: [{ severity: 'error', code: 'unknown_item', message }] }, null, 2));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.error(message);
|
|
105
|
+
}
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!params.typeOverride && isChange && isSpec) {
|
|
110
|
+
if (params.options.json) {
|
|
111
|
+
console.log(JSON.stringify({
|
|
112
|
+
status: [
|
|
113
|
+
{
|
|
114
|
+
severity: 'error',
|
|
115
|
+
code: 'ambiguous_item',
|
|
116
|
+
message: `Ambiguous item '${itemName}' matches both a change and a spec.`,
|
|
117
|
+
fix: 'Pass --type change|spec.',
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
}, null, 2));
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.error(`Ambiguous item '${itemName}' matches both a change and a spec.`);
|
|
125
|
+
// The noun-form commands are cwd-based and cannot reach a selected store.
|
|
126
|
+
if (isStoreSelectedRoot(root)) {
|
|
127
|
+
console.error('Pass --type change|spec.');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.error('Pass --type change|spec, or use: openspec change show / openspec spec show');
|
|
131
|
+
}
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.warnIrrelevantFlags(resolvedType, params.options);
|
|
136
|
+
if (resolvedType === 'change') {
|
|
137
|
+
const cmd = new ChangeCommand(root.path);
|
|
138
|
+
await cmd.show(itemName, this.delegateOptions(root, params.options));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const cmd = new SpecCommand(root.path);
|
|
142
|
+
await cmd.show(itemName, this.delegateOptions(root, params.options));
|
|
143
|
+
}
|
|
144
|
+
printNonInteractiveHint(root) {
|
|
145
|
+
console.error('Nothing to show. Try one of:');
|
|
146
|
+
console.error(` ${withStoreFlag(root, 'openspec show <item>')}`);
|
|
147
|
+
if (isStoreSelectedRoot(root)) {
|
|
148
|
+
// The noun-form commands are cwd-based and cannot reach a selected store.
|
|
149
|
+
console.error(` ${withStoreFlag(root, 'openspec show <item> --type change')}`);
|
|
150
|
+
console.error(` ${withStoreFlag(root, 'openspec show <item> --type spec')}`);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.error(' openspec change show');
|
|
154
|
+
console.error(' openspec spec show');
|
|
155
|
+
}
|
|
156
|
+
console.error('Or run in an interactive terminal.');
|
|
157
|
+
}
|
|
158
|
+
warnIrrelevantFlags(type, options) {
|
|
159
|
+
const irrelevant = [];
|
|
160
|
+
if (type === 'change') {
|
|
161
|
+
for (const k of SPEC_FLAG_KEYS)
|
|
162
|
+
if (k in options)
|
|
163
|
+
irrelevant.push(k);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
for (const k of CHANGE_FLAG_KEYS)
|
|
167
|
+
if (k in options)
|
|
168
|
+
irrelevant.push(k);
|
|
169
|
+
}
|
|
170
|
+
if (irrelevant.length > 0) {
|
|
171
|
+
console.error(`Warning: Ignoring flags not applicable to ${type}: ${irrelevant.join(', ')}`);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=show.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { program } from 'commander';
|
|
2
|
+
import type { RootOutput } from '../core/root-selection.js';
|
|
3
|
+
interface ShowOptions {
|
|
4
|
+
json?: boolean;
|
|
5
|
+
requirements?: boolean;
|
|
6
|
+
scenarios?: boolean;
|
|
7
|
+
requirement?: string;
|
|
8
|
+
noInteractive?: boolean;
|
|
9
|
+
rootOutput?: RootOutput;
|
|
10
|
+
}
|
|
11
|
+
export declare class SpecCommand {
|
|
12
|
+
private specsDir;
|
|
13
|
+
private rootPath?;
|
|
14
|
+
constructor(rootPath?: string);
|
|
15
|
+
show(specId?: string, options?: ShowOptions): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function registerSpecCommand(rootProgram: typeof program): import("commander").Command;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=spec.d.ts.map
|
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
const SPECS_DIR = 'openspec/specs';
|
|
8
|
+
function parseSpecFromFile(specPath, specId) {
|
|
9
|
+
const content = readFileSync(specPath, 'utf-8');
|
|
10
|
+
const parser = new MarkdownParser(content);
|
|
11
|
+
return parser.parseSpec(specId);
|
|
12
|
+
}
|
|
13
|
+
function validateRequirementIndex(spec, requirementOpt) {
|
|
14
|
+
if (!requirementOpt)
|
|
15
|
+
return undefined;
|
|
16
|
+
const index = Number.parseInt(requirementOpt, 10);
|
|
17
|
+
if (!Number.isInteger(index) || index < 1 || index > spec.requirements.length) {
|
|
18
|
+
throw new Error(`Requirement ${requirementOpt} not found`);
|
|
19
|
+
}
|
|
20
|
+
return index - 1; // convert to 0-based
|
|
21
|
+
}
|
|
22
|
+
function filterSpec(spec, options) {
|
|
23
|
+
const requirementIndex = validateRequirementIndex(spec, options.requirement);
|
|
24
|
+
const includeScenarios = options.scenarios !== false && !options.requirements;
|
|
25
|
+
const filteredRequirements = (requirementIndex !== undefined
|
|
26
|
+
? [spec.requirements[requirementIndex]]
|
|
27
|
+
: spec.requirements).map(req => ({
|
|
28
|
+
text: req.text,
|
|
29
|
+
scenarios: includeScenarios ? req.scenarios : [],
|
|
30
|
+
}));
|
|
31
|
+
const metadata = spec.metadata ?? { version: '1.0.0', format: 'openspec' };
|
|
32
|
+
return {
|
|
33
|
+
name: spec.name,
|
|
34
|
+
overview: spec.overview,
|
|
35
|
+
requirements: filteredRequirements,
|
|
36
|
+
metadata,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Print the raw markdown content for a spec file without any formatting.
|
|
41
|
+
* Raw-first behavior ensures text mode is a passthrough for deterministic output.
|
|
42
|
+
*/
|
|
43
|
+
function printSpecTextRaw(specPath) {
|
|
44
|
+
const content = readFileSync(specPath, 'utf-8');
|
|
45
|
+
console.log(content);
|
|
46
|
+
}
|
|
47
|
+
export class SpecCommand {
|
|
48
|
+
specsDir;
|
|
49
|
+
rootPath;
|
|
50
|
+
// rootPath is set only by root-aware callers (top-level `show`); the
|
|
51
|
+
// deprecated noun-form commands stay cwd-based.
|
|
52
|
+
constructor(rootPath) {
|
|
53
|
+
this.rootPath = rootPath;
|
|
54
|
+
this.specsDir = rootPath ? join(rootPath, 'openspec', 'specs') : SPECS_DIR;
|
|
55
|
+
}
|
|
56
|
+
async show(specId, options = {}) {
|
|
57
|
+
if (!specId) {
|
|
58
|
+
const canPrompt = isInteractive(options);
|
|
59
|
+
const specIds = await getSpecIds(this.rootPath ?? process.cwd());
|
|
60
|
+
if (canPrompt && specIds.length > 0) {
|
|
61
|
+
const { select } = await import('@inquirer/prompts');
|
|
62
|
+
specId = await select({
|
|
63
|
+
message: 'Select a spec to show',
|
|
64
|
+
choices: specIds.map(id => ({ name: id, value: id })),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw new Error('Missing required argument <spec-id>');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const specPath = join(this.specsDir, specId, 'spec.md');
|
|
72
|
+
if (!existsSync(specPath)) {
|
|
73
|
+
// Root-aware callers get the absolute path; the cwd-based noun form
|
|
74
|
+
// keeps its historical forward-slash relative message on all platforms.
|
|
75
|
+
const displayPath = this.rootPath ? specPath : `openspec/specs/${specId}/spec.md`;
|
|
76
|
+
throw new Error(`Spec '${specId}' not found at ${displayPath}`);
|
|
77
|
+
}
|
|
78
|
+
if (options.json) {
|
|
79
|
+
if (options.requirements && options.requirement) {
|
|
80
|
+
throw new Error('Options --requirements and --requirement cannot be used together');
|
|
81
|
+
}
|
|
82
|
+
const parsed = parseSpecFromFile(specPath, specId);
|
|
83
|
+
const filtered = filterSpec(parsed, options);
|
|
84
|
+
const output = {
|
|
85
|
+
id: specId,
|
|
86
|
+
title: parsed.name,
|
|
87
|
+
overview: parsed.overview,
|
|
88
|
+
requirementCount: filtered.requirements.length,
|
|
89
|
+
requirements: filtered.requirements,
|
|
90
|
+
metadata: parsed.metadata ?? { version: '1.0.0', format: 'openspec' },
|
|
91
|
+
...(options.rootOutput ? { root: options.rootOutput } : {}),
|
|
92
|
+
};
|
|
93
|
+
console.log(JSON.stringify(output, null, 2));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
printSpecTextRaw(specPath);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export function registerSpecCommand(rootProgram) {
|
|
100
|
+
const specCommand = rootProgram
|
|
101
|
+
.command('spec')
|
|
102
|
+
.description('Manage and view OpenSpec specifications');
|
|
103
|
+
// Deprecation notice for noun-based commands
|
|
104
|
+
specCommand.hook('preAction', () => {
|
|
105
|
+
console.error('Warning: The "openspec spec ..." commands are deprecated. Prefer verb-first commands (e.g., "openspec show", "openspec validate --specs").');
|
|
106
|
+
});
|
|
107
|
+
specCommand
|
|
108
|
+
.command('show [spec-id]')
|
|
109
|
+
.description('Display a specific specification')
|
|
110
|
+
.option('--json', 'Output as JSON')
|
|
111
|
+
.option('--requirements', 'JSON only: Show only requirements (exclude scenarios)')
|
|
112
|
+
.option('--no-scenarios', 'JSON only: Exclude scenario content')
|
|
113
|
+
.option('-r, --requirement <id>', 'JSON only: Show specific requirement by ID (1-based)')
|
|
114
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
115
|
+
.action(async (specId, options) => {
|
|
116
|
+
try {
|
|
117
|
+
const cmd = new SpecCommand();
|
|
118
|
+
await cmd.show(specId, options);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
specCommand
|
|
126
|
+
.command('list')
|
|
127
|
+
.description('List all available specifications')
|
|
128
|
+
.option('--json', 'Output as JSON')
|
|
129
|
+
.option('--long', 'Show id and title with counts')
|
|
130
|
+
.action((options) => {
|
|
131
|
+
try {
|
|
132
|
+
if (!existsSync(SPECS_DIR)) {
|
|
133
|
+
console.log('No items found');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const specs = readdirSync(SPECS_DIR, { withFileTypes: true })
|
|
137
|
+
.filter(dirent => dirent.isDirectory())
|
|
138
|
+
.map(dirent => {
|
|
139
|
+
const specPath = join(SPECS_DIR, dirent.name, 'spec.md');
|
|
140
|
+
if (existsSync(specPath)) {
|
|
141
|
+
try {
|
|
142
|
+
const spec = parseSpecFromFile(specPath, dirent.name);
|
|
143
|
+
return {
|
|
144
|
+
id: dirent.name,
|
|
145
|
+
title: spec.name,
|
|
146
|
+
requirementCount: spec.requirements.length
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return {
|
|
151
|
+
id: dirent.name,
|
|
152
|
+
title: dirent.name,
|
|
153
|
+
requirementCount: 0
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
})
|
|
159
|
+
.filter((spec) => spec !== null)
|
|
160
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
161
|
+
if (options.json) {
|
|
162
|
+
console.log(JSON.stringify(specs, null, 2));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
if (specs.length === 0) {
|
|
166
|
+
console.log('No items found');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!options.long) {
|
|
170
|
+
specs.forEach(spec => console.log(spec.id));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
specs.forEach(spec => {
|
|
174
|
+
console.log(`${spec.id}: ${spec.title} [requirements ${spec.requirementCount}]`);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
specCommand
|
|
184
|
+
.command('validate [spec-id]')
|
|
185
|
+
.description('Validate a specification structure')
|
|
186
|
+
.option('--strict', 'Enable strict validation mode')
|
|
187
|
+
.option('--json', 'Output validation report as JSON')
|
|
188
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
189
|
+
.action(async (specId, options) => {
|
|
190
|
+
try {
|
|
191
|
+
if (!specId) {
|
|
192
|
+
const canPrompt = isInteractive(options);
|
|
193
|
+
const specIds = await getSpecIds();
|
|
194
|
+
if (canPrompt && specIds.length > 0) {
|
|
195
|
+
const { select } = await import('@inquirer/prompts');
|
|
196
|
+
specId = await select({
|
|
197
|
+
message: 'Select a spec to validate',
|
|
198
|
+
choices: specIds.map(id => ({ name: id, value: id })),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
throw new Error('Missing required argument <spec-id>');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const specPath = join(SPECS_DIR, specId, 'spec.md');
|
|
206
|
+
if (!existsSync(specPath)) {
|
|
207
|
+
throw new Error(`Spec '${specId}' not found at openspec/specs/${specId}/spec.md`);
|
|
208
|
+
}
|
|
209
|
+
const validator = new Validator(options.strict);
|
|
210
|
+
const report = await validator.validateSpec(specPath);
|
|
211
|
+
if (options.json) {
|
|
212
|
+
console.log(JSON.stringify(report, null, 2));
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
if (report.valid) {
|
|
216
|
+
console.log(`Specification '${specId}' is valid`);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
console.error(`Specification '${specId}' has issues`);
|
|
220
|
+
report.issues.forEach(issue => {
|
|
221
|
+
const label = issue.level === 'ERROR' ? 'ERROR' : issue.level;
|
|
222
|
+
const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ';
|
|
223
|
+
console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
process.exitCode = report.valid ? 0 : 1;
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
231
|
+
process.exitCode = 1;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return specCommand;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=spec.js.map
|