@codewalla_india/openspec 1.3.1 → 1.3.2
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 -22
- package/README.md +272 -191
- package/bin/openspec.js +5 -5
- package/dist/cli/index.js +99 -62
- package/dist/commands/change.d.ts +0 -2
- package/dist/commands/change.js +107 -86
- package/dist/commands/config.js +14 -16
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/feedback.js +79 -30
- package/dist/commands/modify.d.ts +26 -0
- package/dist/commands/modify.js +147 -0
- package/dist/commands/schema.js +170 -91
- package/dist/commands/show.js +5 -2
- package/dist/commands/spec.js +49 -29
- package/dist/commands/store.js +15 -21
- package/dist/commands/validate.d.ts +8 -0
- package/dist/commands/validate.js +43 -20
- package/dist/commands/workflow/index.d.ts +2 -2
- package/dist/commands/workflow/index.js +1 -1
- package/dist/commands/workflow/instructions.d.ts +6 -26
- package/dist/commands/workflow/instructions.js +183 -376
- package/dist/commands/workflow/new-change.d.ts +0 -4
- package/dist/commands/workflow/new-change.js +10 -28
- package/dist/commands/workflow/shared.d.ts +34 -19
- package/dist/commands/workflow/shared.js +34 -4
- package/dist/commands/workflow/status.js +10 -32
- package/dist/commands/workflow/templates.js +15 -5
- package/dist/commands/workset.d.ts +2 -2
- package/dist/commands/workset.js +19 -22
- package/dist/comprehension-quiz/fingerprint.d.ts +39 -0
- package/dist/comprehension-quiz/fingerprint.js +84 -0
- package/dist/comprehension-quiz/index.d.ts +42 -0
- package/dist/comprehension-quiz/index.js +47 -0
- package/dist/comprehension-quiz/pass-record.d.ts +44 -0
- package/dist/comprehension-quiz/pass-record.js +98 -0
- package/dist/comprehension-quiz/providers/anthropic.d.ts +34 -0
- package/dist/comprehension-quiz/providers/anthropic.js +53 -0
- package/dist/comprehension-quiz/providers/factory.d.ts +44 -0
- package/dist/comprehension-quiz/providers/factory.js +174 -0
- package/dist/comprehension-quiz/providers/interface.d.ts +59 -0
- package/dist/comprehension-quiz/providers/interface.js +63 -0
- package/dist/comprehension-quiz/providers/local.d.ts +34 -0
- package/dist/comprehension-quiz/providers/local.js +53 -0
- package/dist/comprehension-quiz/providers/ollama.d.ts +34 -0
- package/dist/comprehension-quiz/providers/ollama.js +53 -0
- package/dist/comprehension-quiz/providers/openai.d.ts +34 -0
- package/dist/comprehension-quiz/providers/openai.js +53 -0
- package/dist/comprehension-quiz/question-generator.d.ts +69 -0
- package/dist/comprehension-quiz/question-generator.js +141 -0
- package/dist/comprehension-quiz/quiz-executor.d.ts +70 -0
- package/dist/comprehension-quiz/quiz-executor.js +165 -0
- package/dist/comprehension-quiz/types.d.ts +115 -0
- package/dist/comprehension-quiz/types.js +11 -0
- package/dist/core/archive.d.ts +14 -1
- package/dist/core/archive.js +1350 -190
- package/dist/core/artifact-graph/graph.d.ts +28 -9
- package/dist/core/artifact-graph/graph.js +94 -43
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/index.js +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +30 -3
- package/dist/core/artifact-graph/instruction-loader.js +70 -12
- package/dist/core/artifact-graph/outputs.d.ts +1 -0
- package/dist/core/artifact-graph/outputs.js +67 -5
- package/dist/core/artifact-graph/resolver.d.ts +14 -0
- package/dist/core/artifact-graph/resolver.js +69 -15
- package/dist/core/artifact-graph/types.js +21 -3
- package/dist/core/available-tools.d.ts +2 -2
- package/dist/core/available-tools.js +14 -3
- package/dist/core/change-metadata/schema.d.ts +2 -0
- package/dist/core/change-metadata/schema.js +13 -0
- package/dist/core/change-status-policy.d.ts +10 -1
- package/dist/core/change-status-policy.js +30 -2
- package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
- package/dist/core/command-generation/adapters/amazon-q.js +11 -5
- package/dist/core/command-generation/adapters/antigravity.js +6 -5
- package/dist/core/command-generation/adapters/auggie.js +7 -6
- package/dist/core/command-generation/adapters/bob.d.ts +5 -1
- package/dist/core/command-generation/adapters/bob.js +11 -10
- package/dist/core/command-generation/adapters/claude.d.ts +1 -1
- package/dist/core/command-generation/adapters/claude.js +12 -17
- package/dist/core/command-generation/adapters/cline.js +5 -5
- package/dist/core/command-generation/adapters/codebuddy.js +8 -7
- package/dist/core/command-generation/adapters/continue.js +8 -7
- package/dist/core/command-generation/adapters/costrict.js +7 -6
- package/dist/core/command-generation/adapters/crush.js +9 -9
- package/dist/core/command-generation/adapters/cursor.js +8 -8
- package/dist/core/command-generation/adapters/devin.d.ts +19 -0
- package/dist/core/command-generation/adapters/devin.js +36 -0
- package/dist/core/command-generation/adapters/factory.js +7 -6
- package/dist/core/command-generation/adapters/gemini.js +40 -5
- package/dist/core/command-generation/adapters/github-copilot.js +6 -5
- package/dist/core/command-generation/adapters/iflow.js +9 -8
- package/dist/core/command-generation/adapters/index.d.ts +4 -2
- package/dist/core/command-generation/adapters/index.js +4 -2
- package/dist/core/command-generation/adapters/junie.js +6 -5
- package/dist/core/command-generation/adapters/kilocode.js +1 -1
- package/dist/core/command-generation/adapters/kiro.js +6 -5
- package/dist/core/command-generation/adapters/lingma.js +9 -9
- package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
- package/dist/core/command-generation/adapters/oh-my-pi.js +42 -0
- package/dist/core/command-generation/adapters/opencode.js +6 -8
- package/dist/core/command-generation/adapters/pi.d.ts +2 -2
- package/dist/core/command-generation/adapters/pi.js +7 -10
- package/dist/core/command-generation/adapters/qoder.js +9 -9
- package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
- package/dist/core/command-generation/adapters/qwen.js +14 -9
- package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
- package/dist/core/command-generation/adapters/roocode.js +9 -9
- package/dist/core/command-generation/adapters/trae.d.ts +13 -0
- package/dist/core/command-generation/adapters/trae.js +28 -0
- package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
- package/dist/core/command-generation/adapters/zcode.js +33 -0
- package/dist/core/command-generation/generator.d.ts +8 -0
- package/dist/core/command-generation/generator.js +15 -1
- package/dist/core/command-generation/index.d.ts +1 -1
- package/dist/core/command-generation/index.js +1 -1
- package/dist/core/command-generation/invocation.d.ts +73 -0
- package/dist/core/command-generation/invocation.js +77 -0
- package/dist/core/command-generation/registry.js +8 -4
- package/dist/core/command-generation/types.d.ts +9 -2
- package/dist/core/command-generation/yaml.d.ts +20 -6
- package/dist/core/command-generation/yaml.js +31 -21
- package/dist/core/command-surface.d.ts +15 -0
- package/dist/core/command-surface.js +33 -0
- package/dist/core/completions/command-registry.js +16 -61
- package/dist/core/completions/generators/bash-generator.js +41 -41
- package/dist/core/completions/generators/fish-generator.js +7 -7
- package/dist/core/completions/generators/powershell-generator.js +44 -37
- package/dist/core/completions/generators/zsh-generator.js +33 -33
- package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
- package/dist/core/completions/installers/zsh-installer.js +20 -4
- package/dist/core/completions/templates/bash-templates.js +24 -24
- package/dist/core/completions/templates/fish-templates.js +38 -38
- package/dist/core/completions/templates/powershell-templates.js +28 -28
- package/dist/core/completions/templates/zsh-templates.js +39 -39
- package/dist/core/config-prompts.d.ts +1 -1
- package/dist/core/config-prompts.js +14 -1
- package/dist/core/config-schema.d.ts +10 -1
- package/dist/core/config-schema.js +67 -1
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.js +46 -5
- package/dist/core/file-state.js +56 -24
- package/dist/core/github-copilot/cloud-agent.d.ts +95 -0
- package/dist/core/github-copilot/cloud-agent.js +497 -0
- package/dist/core/global-config.d.ts +16 -0
- package/dist/core/init.d.ts +58 -0
- package/dist/core/init.js +474 -73
- package/dist/core/legacy-cleanup.d.ts +59 -0
- package/dist/core/legacy-cleanup.js +253 -24
- package/dist/core/list.js +23 -16
- package/dist/core/migration.d.ts +73 -1
- package/dist/core/migration.js +360 -11
- package/dist/core/modify/conflict-detector.d.ts +26 -0
- package/dist/core/modify/conflict-detector.js +62 -0
- package/dist/core/modify/dependency-propagator.d.ts +38 -0
- package/dist/core/modify/dependency-propagator.js +103 -0
- package/dist/core/modify/history-tracker.d.ts +34 -0
- package/dist/core/modify/history-tracker.js +114 -0
- package/dist/core/modify/index.d.ts +7 -0
- package/dist/core/modify/index.js +7 -0
- package/dist/core/onboarding-commands.d.ts +30 -0
- package/dist/core/onboarding-commands.js +38 -0
- package/dist/core/openspec-root.js +18 -15
- package/dist/core/parsers/change-parser.d.ts +17 -2
- package/dist/core/parsers/change-parser.js +35 -21
- package/dist/core/parsers/code-fence.d.ts +15 -0
- package/dist/core/parsers/code-fence.js +49 -0
- package/dist/core/parsers/markdown-parser.d.ts +0 -3
- package/dist/core/parsers/markdown-parser.js +7 -60
- package/dist/core/parsers/requirement-blocks.d.ts +29 -0
- package/dist/core/parsers/requirement-blocks.js +150 -31
- package/dist/core/parsers/requirement-text.d.ts +48 -0
- package/dist/core/parsers/requirement-text.js +98 -0
- package/dist/core/parsers/spec-structure.d.ts +1 -1
- package/dist/core/parsers/spec-structure.js +20 -30
- package/dist/core/profile-sync-drift.d.ts +0 -8
- package/dist/core/profile-sync-drift.js +49 -50
- package/dist/core/profiles.d.ts +2 -2
- package/dist/core/profiles.js +2 -2
- package/dist/core/project-config.d.ts +27 -18
- package/dist/core/project-config.js +101 -34
- package/dist/core/references.js +42 -4
- package/dist/core/relationship-health.d.ts +8 -0
- package/dist/core/relationship-health.js +13 -0
- package/dist/core/root-selection.d.ts +9 -6
- package/dist/core/root-selection.js +42 -5
- package/dist/core/schemas/base.schema.js +7 -2
- package/dist/core/shared/allowed-tools.d.ts +12 -0
- package/dist/core/shared/allowed-tools.js +12 -0
- package/dist/core/shared/index.d.ts +1 -0
- package/dist/core/shared/index.js +1 -0
- package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
- package/dist/core/shared/skill-content-equivalence.js +41 -0
- package/dist/core/shared/skill-generation.js +17 -13
- package/dist/core/shared/skill-paths.d.ts +13 -0
- package/dist/core/shared/skill-paths.js +23 -0
- package/dist/core/shared/tool-detection.d.ts +27 -7
- package/dist/core/shared/tool-detection.js +167 -37
- package/dist/core/shared-skill-target.d.ts +16 -0
- package/dist/core/shared-skill-target.js +156 -0
- package/dist/core/specs-apply.d.ts +80 -33
- package/dist/core/specs-apply.js +667 -145
- package/dist/core/store/git.d.ts +13 -0
- package/dist/core/store/git.js +24 -0
- package/dist/core/store/operations.js +20 -0
- package/dist/core/templates/skill-templates.d.ts +3 -2
- package/dist/core/templates/skill-templates.js +3 -2
- package/dist/core/templates/workflows/apply-change.d.ts +10 -0
- package/dist/core/templates/workflows/apply-change.js +186 -224
- package/dist/core/templates/workflows/archive-change.js +387 -260
- package/dist/core/templates/workflows/bulk-archive-change.js +655 -475
- package/dist/core/templates/workflows/continue-change.js +210 -219
- package/dist/core/templates/workflows/explore.js +479 -443
- package/dist/core/templates/workflows/feedback.js +97 -97
- package/dist/core/templates/workflows/ff-change.js +200 -191
- package/dist/core/templates/workflows/modify-change.d.ts +4 -1
- package/dist/core/templates/workflows/modify-change.js +237 -115
- package/dist/core/templates/workflows/new-change.js +127 -134
- package/dist/core/templates/workflows/onboard.js +548 -542
- package/dist/core/templates/workflows/propose.js +273 -217
- package/dist/core/templates/workflows/store-selection.d.ts +1 -1
- package/dist/core/templates/workflows/store-selection.js +1 -1
- package/dist/core/templates/workflows/sync-specs.js +500 -273
- package/dist/core/templates/workflows/update-change.d.ts +10 -0
- package/dist/core/templates/workflows/update-change.js +177 -0
- package/dist/core/templates/workflows/verify-change.js +324 -326
- package/dist/core/update.d.ts +40 -5
- package/dist/core/update.js +411 -96
- package/dist/core/validation/constants.d.ts +4 -1
- package/dist/core/validation/constants.js +4 -1
- package/dist/core/validation/plan-validator.d.ts +8 -0
- package/dist/core/validation/plan-validator.js +45 -0
- package/dist/core/validation/task-numbering.d.ts +16 -0
- package/dist/core/validation/task-numbering.js +57 -0
- package/dist/core/validation/validator.d.ts +27 -3
- package/dist/core/validation/validator.js +337 -48
- package/dist/core/version-check.d.ts +152 -0
- package/dist/core/version-check.js +731 -0
- package/dist/core/view.js +13 -18
- package/dist/prompts/searchable-multi-select.js +1 -1
- package/dist/telemetry/caller-detection.d.ts +27 -0
- package/dist/telemetry/caller-detection.js +85 -0
- package/dist/telemetry/caller-detection.test.d.ts +5 -0
- package/dist/telemetry/config.d.ts +48 -2
- package/dist/telemetry/config.js +113 -11
- package/dist/telemetry/config.test.d.ts +5 -0
- package/dist/telemetry/deduplication.d.ts +35 -0
- package/dist/telemetry/deduplication.js +82 -0
- package/dist/telemetry/deduplication.test.d.ts +5 -0
- package/dist/telemetry/identity.d.ts +62 -22
- package/dist/telemetry/identity.js +147 -102
- package/dist/telemetry/index.d.ts +126 -15
- package/dist/telemetry/index.js +569 -37
- package/dist/telemetry/sanitization.d.ts +40 -0
- package/dist/telemetry/sanitization.js +114 -0
- package/dist/telemetry/sanitization.test.d.ts +5 -0
- package/dist/telemetry/throttling.d.ts +20 -0
- package/dist/telemetry/throttling.js +70 -0
- package/dist/ui/welcome-screen.d.ts +9 -1
- package/dist/ui/welcome-screen.js +91 -38
- package/dist/utils/change-metadata.d.ts +43 -0
- package/dist/utils/change-metadata.js +123 -7
- package/dist/utils/change-utils.d.ts +10 -4
- package/dist/utils/change-utils.js +20 -13
- package/dist/utils/ci.d.ts +12 -0
- package/dist/utils/ci.js +16 -0
- package/dist/utils/command-references.d.ts +92 -6
- package/dist/utils/command-references.js +180 -7
- package/dist/utils/date.d.ts +8 -0
- package/dist/utils/date.js +13 -0
- package/dist/utils/file-system.d.ts +10 -0
- package/dist/utils/file-system.js +70 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/interactive.d.ts +23 -0
- package/dist/utils/interactive.js +32 -0
- package/dist/utils/item-discovery.d.ts +19 -0
- package/dist/utils/item-discovery.js +30 -48
- package/dist/utils/shell-detection.d.ts +1 -1
- package/dist/utils/shell-detection.js +64 -11
- package/dist/utils/spec-discovery.d.ts +40 -0
- package/dist/utils/spec-discovery.js +115 -0
- package/dist/utils/task-progress.d.ts +29 -1
- package/dist/utils/task-progress.js +109 -15
- package/package.json +96 -84
- package/schemas/spec-driven/schema.yaml +252 -184
- package/schemas/spec-driven/templates/design.md +19 -19
- package/schemas/spec-driven/templates/plan-template.md +91 -0
- package/schemas/spec-driven/templates/proposal.md +29 -24
- package/schemas/spec-driven/templates/spec.md +11 -8
- package/schemas/spec-driven/templates/tasks.md +9 -9
- package/scripts/postinstall.js +83 -83
- package/dist/core/command-generation/adapters/codex.d.ts +0 -16
- package/dist/core/command-generation/adapters/codex.js +0 -39
- package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
- package/dist/core/command-generation/adapters/windsurf.js +0 -38
- package/dist/core/comprehension/config.d.ts +0 -20
- package/dist/core/comprehension/config.js +0 -23
- package/dist/core/comprehension/fingerprint.d.ts +0 -14
- package/dist/core/comprehension/fingerprint.js +0 -33
- package/dist/core/comprehension/index.d.ts +0 -57
- package/dist/core/comprehension/index.js +0 -87
- package/dist/core/comprehension/pass-record.d.ts +0 -29
- package/dist/core/comprehension/pass-record.js +0 -64
- package/dist/core/comprehension/stats.d.ts +0 -35
- package/dist/core/comprehension/stats.js +0 -133
- package/dist/core/templates/workflows/comprehension-guidance.d.ts +0 -9
- package/dist/core/templates/workflows/comprehension-guidance.js +0 -73
- package/dist/core/templates/workflows/mcp-guidance.d.ts +0 -13
- package/dist/core/templates/workflows/mcp-guidance.js +0 -131
- package/dist/core/templates/workflows/user-prompt-guidance.d.ts +0 -16
- package/dist/core/templates/workflows/user-prompt-guidance.js +0 -48
- package/dist/telemetry/caller.d.ts +0 -5
- package/dist/telemetry/caller.js +0 -29
- package/dist/telemetry/client.d.ts +0 -27
- package/dist/telemetry/client.js +0 -127
- package/dist/telemetry/command-context.d.ts +0 -13
- package/dist/telemetry/command-context.js +0 -59
- package/dist/telemetry/comprehension.d.ts +0 -44
- package/dist/telemetry/comprehension.js +0 -105
- package/dist/telemetry/content.d.ts +0 -10
- package/dist/telemetry/content.js +0 -56
- package/dist/telemetry/git-stats.d.ts +0 -12
- package/dist/telemetry/git-stats.js +0 -69
- package/dist/telemetry/identify-cache.d.ts +0 -7
- package/dist/telemetry/identify-cache.js +0 -47
- package/dist/telemetry/input.d.ts +0 -17
- package/dist/telemetry/input.js +0 -68
- package/dist/telemetry/marker.d.ts +0 -37
- package/dist/telemetry/marker.js +0 -67
- package/dist/telemetry/workflow.d.ts +0 -85
- package/dist/telemetry/workflow.js +0 -318
- package/schemas/spec-driven/templates/plan.md +0 -19
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Legacy cleanup module for detecting and removing OpenSpec artifacts
|
|
3
3
|
* from previous init versions during the migration to the skill-based workflow.
|
|
4
4
|
*/
|
|
5
|
+
import type { WorkflowId } from './profiles.js';
|
|
5
6
|
/**
|
|
6
7
|
* Legacy config file names from the old ToolRegistry.
|
|
7
8
|
* These were config files created at project root with OpenSpec markers.
|
|
@@ -13,6 +14,11 @@ export declare const LEGACY_CONFIG_FILES: readonly ["CLAUDE.md", "CLINE.md", "CO
|
|
|
13
14
|
* Some tools used a directory structure, others used individual files.
|
|
14
15
|
*/
|
|
15
16
|
export declare const LEGACY_SLASH_COMMAND_PATHS: Record<string, LegacySlashCommandPattern>;
|
|
17
|
+
/**
|
|
18
|
+
* Global legacy prompt locations that live outside the project tree and require
|
|
19
|
+
* allowlisted matching instead of broad glob-based cleanup.
|
|
20
|
+
*/
|
|
21
|
+
export declare const LEGACY_GLOBAL_SLASH_COMMAND_PATHS: Record<string, LegacyGlobalPromptPattern>;
|
|
16
22
|
/**
|
|
17
23
|
* Pattern types for legacy slash commands
|
|
18
24
|
*/
|
|
@@ -21,6 +27,28 @@ export interface LegacySlashCommandPattern {
|
|
|
21
27
|
path?: string;
|
|
22
28
|
pattern?: string | string[];
|
|
23
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Describes a managed global prompt home and the exact filenames OpenSpec is
|
|
32
|
+
* allowed to treat as legacy artifacts there.
|
|
33
|
+
*/
|
|
34
|
+
export interface LegacyGlobalPromptPattern {
|
|
35
|
+
managedFileNames: readonly string[];
|
|
36
|
+
workflowIdsByFileName?: Readonly<Record<string, readonly WorkflowId[]>>;
|
|
37
|
+
resolvePromptDir: () => string;
|
|
38
|
+
replacementLabel?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Workflow-aware metadata for a detected global legacy prompt that is safe for
|
|
42
|
+
* replacement-gated cleanup.
|
|
43
|
+
*/
|
|
44
|
+
export interface LegacyGlobalPromptMatch {
|
|
45
|
+
path: string;
|
|
46
|
+
toolId: string;
|
|
47
|
+
managedFileName: string;
|
|
48
|
+
workflowIds: readonly WorkflowId[];
|
|
49
|
+
replacementLabel?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function getCodexPromptDir(): string;
|
|
24
52
|
/**
|
|
25
53
|
* Result of legacy artifact detection
|
|
26
54
|
*/
|
|
@@ -33,6 +61,10 @@ export interface LegacyDetectionResult {
|
|
|
33
61
|
slashCommandDirs: string[];
|
|
34
62
|
/** Legacy slash command files found (for file-based tools) */
|
|
35
63
|
slashCommandFiles: string[];
|
|
64
|
+
/** Managed global command/prompt files found outside the project root */
|
|
65
|
+
globalSlashCommandFiles: string[];
|
|
66
|
+
/** Details for managed global command/prompt files */
|
|
67
|
+
globalSlashCommandDetails?: LegacyGlobalPromptMatch[];
|
|
36
68
|
/** Whether openspec/AGENTS.md exists */
|
|
37
69
|
hasOpenspecAgents: boolean;
|
|
38
70
|
/** Whether openspec/project.md exists (preserved, migration hint only) */
|
|
@@ -111,6 +143,8 @@ export declare function removeMarkerBlock(content: string): string;
|
|
|
111
143
|
export interface CleanupResult {
|
|
112
144
|
/** Files that were deleted entirely */
|
|
113
145
|
deletedFiles: string[];
|
|
146
|
+
/** Replacement labels for deleted files when cleanup knows the new surface */
|
|
147
|
+
deletedFileReplacementLabels?: Record<string, string>;
|
|
114
148
|
/** Files that had marker blocks removed */
|
|
115
149
|
modifiedFiles: string[];
|
|
116
150
|
/** Directories that were deleted */
|
|
@@ -144,6 +178,11 @@ export declare function formatCleanupSummary(result: CleanupResult): string;
|
|
|
144
178
|
* @returns Formatted summary string showing what was found
|
|
145
179
|
*/
|
|
146
180
|
export declare function formatDetectionSummary(detection: LegacyDetectionResult): string;
|
|
181
|
+
/**
|
|
182
|
+
* Generates a summary for managed global prompt files whose cleanup must wait
|
|
183
|
+
* until replacement skills are installed.
|
|
184
|
+
*/
|
|
185
|
+
export declare function formatDeferredGlobalPromptSummary(detection: LegacyDetectionResult): string;
|
|
147
186
|
/**
|
|
148
187
|
* Extract tool IDs from detected legacy artifacts.
|
|
149
188
|
* Uses LEGACY_SLASH_COMMAND_PATHS to map paths back to tool IDs.
|
|
@@ -152,6 +191,26 @@ export declare function formatDetectionSummary(detection: LegacyDetectionResult)
|
|
|
152
191
|
* @returns Array of tool IDs that had legacy artifacts
|
|
153
192
|
*/
|
|
154
193
|
export declare function getToolsFromLegacyArtifacts(detection: LegacyDetectionResult): string[];
|
|
194
|
+
/**
|
|
195
|
+
* Normalizes global Codex prompt matches so callers can rely on workflow-aware
|
|
196
|
+
* metadata even when older detection results only carry file paths.
|
|
197
|
+
*/
|
|
198
|
+
export declare function getLegacyGlobalPromptMatches(detection: LegacyDetectionResult): LegacyGlobalPromptMatch[];
|
|
199
|
+
/**
|
|
200
|
+
* Collects workflow IDs inferred from detected legacy global prompts for a
|
|
201
|
+
* specific tool.
|
|
202
|
+
*/
|
|
203
|
+
export declare function getLegacyWorkflowIdsForTool(detection: LegacyDetectionResult, toolId: string): WorkflowId[];
|
|
204
|
+
/**
|
|
205
|
+
* Returns a detection snapshot with global Codex prompt cleanup removed so
|
|
206
|
+
* callers can safely perform the immediate, non-deferred cleanup pass.
|
|
207
|
+
*/
|
|
208
|
+
export declare function omitGlobalLegacyPromptFiles(detection: LegacyDetectionResult): LegacyDetectionResult;
|
|
209
|
+
/**
|
|
210
|
+
* Builds a detection snapshot containing only the selected global Codex prompt
|
|
211
|
+
* matches for replacement-gated cleanup.
|
|
212
|
+
*/
|
|
213
|
+
export declare function pickGlobalLegacyPromptFiles(detection: LegacyDetectionResult, filePaths: readonly string[]): LegacyDetectionResult;
|
|
155
214
|
/**
|
|
156
215
|
* Generates a migration hint message for project.md.
|
|
157
216
|
* This is shown when project.md exists and needs manual migration to config.yaml.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Legacy cleanup module for detecting and removing OpenSpec artifacts
|
|
3
3
|
* from previous init versions during the migration to the skill-based workflow.
|
|
4
4
|
*/
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
5
7
|
import { promises as fs } from 'fs';
|
|
6
8
|
import chalk from 'chalk';
|
|
7
9
|
import { FileSystemUtils, removeMarkerBlock as removeMarkerBlockUtil } from '../utils/file-system.js';
|
|
@@ -33,10 +35,12 @@ export const LEGACY_SLASH_COMMAND_PATHS = {
|
|
|
33
35
|
'lingma': { type: 'directory', path: '.lingma/commands/openspec' },
|
|
34
36
|
'crush': { type: 'directory', path: '.crush/commands/openspec' },
|
|
35
37
|
'gemini': { type: 'directory', path: '.gemini/commands/openspec' },
|
|
36
|
-
'costrict': { type: 'directory', path: '.cospec/openspec/commands' },
|
|
37
38
|
// File-based: individual openspec-*.md files in a commands/workflows/prompts folder
|
|
38
39
|
'cursor': { type: 'files', pattern: '.cursor/commands/openspec-*.md' },
|
|
39
|
-
|
|
40
|
+
// Keyed by the tool id these map back to, so the pre-opsx Windsurf files
|
|
41
|
+
// belong to `devin` — the id Windsurf became. Only `.windsurf/` is listed:
|
|
42
|
+
// `.devin/` postdates the opsx rename and never held `openspec-*` files.
|
|
43
|
+
'devin': { type: 'files', pattern: '.windsurf/workflows/openspec-*.md' },
|
|
40
44
|
'kilocode': { type: 'files', pattern: '.kilocode/workflows/openspec-*.md' },
|
|
41
45
|
'kiro': { type: 'files', pattern: '.kiro/prompts/openspec-*.prompt.md' },
|
|
42
46
|
'github-copilot': { type: 'files', pattern: '.github/prompts/openspec-*.prompt.md' },
|
|
@@ -49,10 +53,87 @@ export const LEGACY_SLASH_COMMAND_PATHS = {
|
|
|
49
53
|
'continue': { type: 'files', pattern: '.continue/prompts/openspec-*.prompt' },
|
|
50
54
|
'antigravity': { type: 'files', pattern: '.agent/workflows/openspec-*.md' },
|
|
51
55
|
'iflow': { type: 'files', pattern: '.iflow/commands/openspec-*.md' },
|
|
52
|
-
'
|
|
53
|
-
'qwen': { type: 'files', pattern: '.qwen/commands/openspec-*.toml' },
|
|
56
|
+
'qwen': { type: 'files', pattern: ['.qwen/commands/opsx-*.toml', '.qwen/commands/openspec-*.toml'] },
|
|
54
57
|
'codex': { type: 'files', pattern: '.codex/prompts/openspec-*.md' },
|
|
58
|
+
// Keep this file-scoped: the CoStrict adapter writes `opsx-*.md` into the
|
|
59
|
+
// same folder, so a directory entry removes the live command files — and
|
|
60
|
+
// anything else the user keeps there — on every run.
|
|
61
|
+
'costrict': { type: 'files', pattern: '.cospec/openspec/commands/openspec-*.md' },
|
|
55
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Final OpenSpec-managed global Codex prompt filenames mapped to the workflows
|
|
65
|
+
* they represented before Codex moved to skills-only delivery.
|
|
66
|
+
*/
|
|
67
|
+
const LEGACY_GLOBAL_CODEX_WORKFLOWS = {
|
|
68
|
+
'opsx-propose.md': ['propose'],
|
|
69
|
+
'opsx-explore.md': ['explore'],
|
|
70
|
+
'opsx-new.md': ['new'],
|
|
71
|
+
'opsx-continue.md': ['continue'],
|
|
72
|
+
'opsx-apply.md': ['apply'],
|
|
73
|
+
'opsx-update.md': ['update'],
|
|
74
|
+
'opsx-ff.md': ['ff'],
|
|
75
|
+
'opsx-sync.md': ['sync'],
|
|
76
|
+
'opsx-archive.md': ['archive'],
|
|
77
|
+
'opsx-bulk-archive.md': ['bulk-archive'],
|
|
78
|
+
'opsx-verify.md': ['verify'],
|
|
79
|
+
'opsx-onboard.md': ['onboard'],
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Global legacy prompt locations that live outside the project tree and require
|
|
83
|
+
* allowlisted matching instead of broad glob-based cleanup.
|
|
84
|
+
*/
|
|
85
|
+
export const LEGACY_GLOBAL_SLASH_COMMAND_PATHS = {
|
|
86
|
+
'codex': {
|
|
87
|
+
managedFileNames: Object.keys(LEGACY_GLOBAL_CODEX_WORKFLOWS),
|
|
88
|
+
workflowIdsByFileName: LEGACY_GLOBAL_CODEX_WORKFLOWS,
|
|
89
|
+
resolvePromptDir: getCodexPromptDir,
|
|
90
|
+
replacementLabel: 'Codex skills',
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
// Resolve the Codex global prompts directory, respecting CODEX_HOME if set.
|
|
94
|
+
export function getCodexPromptDir() {
|
|
95
|
+
const envHome = process.env.CODEX_HOME?.trim();
|
|
96
|
+
const codexHome = envHome ? envHome : path.join(os.homedir(), '.codex');
|
|
97
|
+
return path.join(path.resolve(codexHome), 'prompts');
|
|
98
|
+
}
|
|
99
|
+
// Convert a simple glob pattern (only * wildcards) into an anchored RegExp.
|
|
100
|
+
function globToRegex(pattern) {
|
|
101
|
+
const regexPattern = pattern
|
|
102
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
103
|
+
.replace(/\*/g, '.*');
|
|
104
|
+
return new RegExp(`^${regexPattern}$`);
|
|
105
|
+
}
|
|
106
|
+
// Normalize Windows backslashes to forward slashes for cross-platform path matching.
|
|
107
|
+
function normalizePathForMatch(filePath) {
|
|
108
|
+
return filePath.replace(/\\/g, '/');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Classifies a global Codex prompt path as OpenSpec-managed only when it matches
|
|
112
|
+
* the explicit legacy allowlist for the resolved prompt home.
|
|
113
|
+
*/
|
|
114
|
+
function getManagedGlobalLegacyPromptMetadata(filePath) {
|
|
115
|
+
if (!path.isAbsolute(filePath)) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
const resolvedPath = path.resolve(filePath);
|
|
119
|
+
for (const [toolId, pattern] of Object.entries(LEGACY_GLOBAL_SLASH_COMMAND_PATHS)) {
|
|
120
|
+
const promptDir = path.resolve(pattern.resolvePromptDir());
|
|
121
|
+
if (path.dirname(resolvedPath) !== promptDir) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const managedFileName = path.basename(resolvedPath);
|
|
125
|
+
if (pattern.managedFileNames.includes(managedFileName)) {
|
|
126
|
+
return {
|
|
127
|
+
path: resolvedPath,
|
|
128
|
+
toolId,
|
|
129
|
+
managedFileName,
|
|
130
|
+
workflowIds: pattern.workflowIdsByFileName?.[managedFileName] ?? [],
|
|
131
|
+
replacementLabel: pattern.replacementLabel,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
56
137
|
/**
|
|
57
138
|
* Detects all legacy OpenSpec artifacts in a project.
|
|
58
139
|
*
|
|
@@ -65,6 +146,8 @@ export async function detectLegacyArtifacts(projectPath) {
|
|
|
65
146
|
configFilesToUpdate: [],
|
|
66
147
|
slashCommandDirs: [],
|
|
67
148
|
slashCommandFiles: [],
|
|
149
|
+
globalSlashCommandFiles: [],
|
|
150
|
+
globalSlashCommandDetails: [],
|
|
68
151
|
hasOpenspecAgents: false,
|
|
69
152
|
hasProjectMd: false,
|
|
70
153
|
hasRootAgentsWithMarkers: false,
|
|
@@ -77,7 +160,10 @@ export async function detectLegacyArtifacts(projectPath) {
|
|
|
77
160
|
// Detect legacy slash commands
|
|
78
161
|
const slashResult = await detectLegacySlashCommands(projectPath);
|
|
79
162
|
result.slashCommandDirs = slashResult.directories;
|
|
80
|
-
result.slashCommandFiles = slashResult.files;
|
|
163
|
+
result.slashCommandFiles = [...new Set(slashResult.files)];
|
|
164
|
+
// Detect legacy global slash commands
|
|
165
|
+
result.globalSlashCommandDetails = await detectLegacyGlobalPromptFiles();
|
|
166
|
+
result.globalSlashCommandFiles = result.globalSlashCommandDetails.map((detail) => detail.path);
|
|
81
167
|
// Detect legacy structure files
|
|
82
168
|
const structureResult = await detectLegacyStructureFiles(projectPath);
|
|
83
169
|
result.hasOpenspecAgents = structureResult.hasOpenspecAgents;
|
|
@@ -88,6 +174,7 @@ export async function detectLegacyArtifacts(projectPath) {
|
|
|
88
174
|
result.configFiles.length > 0 ||
|
|
89
175
|
result.slashCommandDirs.length > 0 ||
|
|
90
176
|
result.slashCommandFiles.length > 0 ||
|
|
177
|
+
result.globalSlashCommandFiles.length > 0 ||
|
|
91
178
|
result.hasOpenspecAgents ||
|
|
92
179
|
result.hasRootAgentsWithMarkers ||
|
|
93
180
|
result.hasProjectMd;
|
|
@@ -125,7 +212,7 @@ export async function detectLegacyConfigFiles(projectPath) {
|
|
|
125
212
|
export async function detectLegacySlashCommands(projectPath) {
|
|
126
213
|
const directories = [];
|
|
127
214
|
const files = [];
|
|
128
|
-
for (const
|
|
215
|
+
for (const pattern of Object.values(LEGACY_SLASH_COMMAND_PATHS)) {
|
|
129
216
|
if (pattern.type === 'directory' && pattern.path) {
|
|
130
217
|
const dirPath = FileSystemUtils.joinPath(projectPath, pattern.path);
|
|
131
218
|
if (await FileSystemUtils.directoryExists(dirPath)) {
|
|
@@ -133,7 +220,6 @@ export async function detectLegacySlashCommands(projectPath) {
|
|
|
133
220
|
}
|
|
134
221
|
}
|
|
135
222
|
else if (pattern.type === 'files' && pattern.pattern) {
|
|
136
|
-
// For file-based patterns, check for individual files
|
|
137
223
|
const patterns = Array.isArray(pattern.pattern) ? pattern.pattern : [pattern.pattern];
|
|
138
224
|
for (const p of patterns) {
|
|
139
225
|
const foundFiles = await findLegacySlashCommandFiles(projectPath, p);
|
|
@@ -143,6 +229,37 @@ export async function detectLegacySlashCommands(projectPath) {
|
|
|
143
229
|
}
|
|
144
230
|
return { directories, files };
|
|
145
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Detects legacy global slash command files.
|
|
234
|
+
*
|
|
235
|
+
* @returns Object with individual files found
|
|
236
|
+
*/
|
|
237
|
+
/**
|
|
238
|
+
* Scans the resolved global Codex prompt directories and returns only the
|
|
239
|
+
* allowlisted OpenSpec-managed legacy prompt files.
|
|
240
|
+
*/
|
|
241
|
+
async function detectLegacyGlobalPromptFiles() {
|
|
242
|
+
const foundFiles = [];
|
|
243
|
+
for (const pattern of Object.values(LEGACY_GLOBAL_SLASH_COMMAND_PATHS)) {
|
|
244
|
+
const promptDir = pattern.resolvePromptDir();
|
|
245
|
+
try {
|
|
246
|
+
const entries = await fs.readdir(promptDir, { withFileTypes: true });
|
|
247
|
+
for (const entry of entries) {
|
|
248
|
+
if (entry.isFile() && pattern.managedFileNames.includes(entry.name)) {
|
|
249
|
+
const fullPath = path.join(promptDir, entry.name);
|
|
250
|
+
const match = getManagedGlobalLegacyPromptMetadata(fullPath);
|
|
251
|
+
if (match) {
|
|
252
|
+
foundFiles.push(match);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// Directory does not exist or cannot be read.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return foundFiles;
|
|
262
|
+
}
|
|
146
263
|
/**
|
|
147
264
|
* Finds legacy slash command files matching a glob pattern.
|
|
148
265
|
*
|
|
@@ -165,14 +282,7 @@ async function findLegacySlashCommandFiles(projectPath, pattern) {
|
|
|
165
282
|
}
|
|
166
283
|
try {
|
|
167
284
|
const entries = await fs.readdir(dirPath);
|
|
168
|
-
|
|
169
|
-
// openspec-*.md -> /^openspec-.*\.md$/
|
|
170
|
-
// openspec-*.prompt.md -> /^openspec-.*\.prompt\.md$/
|
|
171
|
-
// openspec-*.toml -> /^openspec-.*\.toml$/
|
|
172
|
-
const regexPattern = filePart
|
|
173
|
-
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
|
|
174
|
-
.replace(/\*/g, '.*'); // Replace * with .*
|
|
175
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
285
|
+
const regex = globToRegex(filePart);
|
|
176
286
|
for (const entry of entries) {
|
|
177
287
|
if (regex.test(entry)) {
|
|
178
288
|
// Use forward slashes for consistency in relative paths (cross-platform)
|
|
@@ -257,6 +367,7 @@ export function removeMarkerBlock(content) {
|
|
|
257
367
|
export async function cleanupLegacyArtifacts(projectPath, detection) {
|
|
258
368
|
const result = {
|
|
259
369
|
deletedFiles: [],
|
|
370
|
+
deletedFileReplacementLabels: {},
|
|
260
371
|
modifiedFiles: [],
|
|
261
372
|
deletedDirs: [],
|
|
262
373
|
projectMdNeedsMigration: detection.hasProjectMd,
|
|
@@ -299,6 +410,25 @@ export async function cleanupLegacyArtifacts(projectPath, detection) {
|
|
|
299
410
|
result.errors.push(`Failed to delete ${filePath}: ${error.message}`);
|
|
300
411
|
}
|
|
301
412
|
}
|
|
413
|
+
// Delete managed global slash command files (these are 100% OpenSpec-managed)
|
|
414
|
+
const globalPromptMatchesByPath = new Map(getLegacyGlobalPromptMatches(detection).map((prompt) => [prompt.path, prompt]));
|
|
415
|
+
for (const filePath of detection.globalSlashCommandFiles) {
|
|
416
|
+
if (!getManagedGlobalLegacyPromptMetadata(filePath)) {
|
|
417
|
+
result.errors.push(`Skipped unmanaged global prompt ${filePath}`);
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
await fs.unlink(filePath);
|
|
422
|
+
result.deletedFiles.push(filePath);
|
|
423
|
+
const promptMatch = globalPromptMatchesByPath.get(filePath);
|
|
424
|
+
if (promptMatch?.replacementLabel) {
|
|
425
|
+
result.deletedFileReplacementLabels[filePath] = promptMatch.replacementLabel;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
result.errors.push(`Failed to delete ${filePath}: ${error.message}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
302
432
|
// Delete openspec/AGENTS.md (this is inside openspec/, it's OpenSpec-managed)
|
|
303
433
|
if (detection.hasOpenspecAgents) {
|
|
304
434
|
const agentsPath = FileSystemUtils.joinPath(projectPath, 'openspec', 'AGENTS.md');
|
|
@@ -328,10 +458,15 @@ export function formatCleanupSummary(result) {
|
|
|
328
458
|
if (result.deletedFiles.length > 0 || result.deletedDirs.length > 0 || result.modifiedFiles.length > 0) {
|
|
329
459
|
lines.push('Cleaned up legacy files:');
|
|
330
460
|
for (const file of result.deletedFiles) {
|
|
331
|
-
|
|
461
|
+
const replacementLabel = result.deletedFileReplacementLabels?.[file]
|
|
462
|
+
?? getManagedGlobalLegacyPromptMetadata(file)?.replacementLabel;
|
|
463
|
+
const replacement = replacementLabel
|
|
464
|
+
? ` (replaced by ${replacementLabel})`
|
|
465
|
+
: '';
|
|
466
|
+
lines.push(` ✓ Removed ${file}${replacement}`);
|
|
332
467
|
}
|
|
333
468
|
for (const dir of result.deletedDirs) {
|
|
334
|
-
lines.push(` ✓ Removed ${dir}/ (replaced by
|
|
469
|
+
lines.push(` ✓ Removed ${dir}/ (replaced by OpenSpec skills and commands)`);
|
|
335
470
|
}
|
|
336
471
|
for (const file of result.modifiedFiles) {
|
|
337
472
|
lines.push(` ✓ Removed OpenSpec markers from ${file}`);
|
|
@@ -374,6 +509,13 @@ function buildRemovalsList(detection) {
|
|
|
374
509
|
for (const file of detection.slashCommandFiles) {
|
|
375
510
|
removals.push({ path: file, explanation: 'replaced by skills/' });
|
|
376
511
|
}
|
|
512
|
+
// Managed global slash command files
|
|
513
|
+
for (const prompt of getLegacyGlobalPromptMatches(detection)) {
|
|
514
|
+
const explanation = prompt.toolId
|
|
515
|
+
? `replaced by .${prompt.toolId}/skills/`
|
|
516
|
+
: 'replaced by skills/';
|
|
517
|
+
removals.push({ path: prompt.path, explanation });
|
|
518
|
+
}
|
|
377
519
|
// openspec/AGENTS.md (inside openspec/, it's OpenSpec-managed)
|
|
378
520
|
if (detection.hasOpenspecAgents) {
|
|
379
521
|
removals.push({ path: 'openspec/AGENTS.md', explanation: 'obsolete workflow file' });
|
|
@@ -445,6 +587,24 @@ export function formatDetectionSummary(detection) {
|
|
|
445
587
|
}
|
|
446
588
|
return lines.join('\n');
|
|
447
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Generates a summary for managed global prompt files whose cleanup must wait
|
|
592
|
+
* until replacement skills are installed.
|
|
593
|
+
*/
|
|
594
|
+
export function formatDeferredGlobalPromptSummary(detection) {
|
|
595
|
+
const deferredPrompts = getLegacyGlobalPromptMatches(detection);
|
|
596
|
+
if (deferredPrompts.length === 0) {
|
|
597
|
+
return '';
|
|
598
|
+
}
|
|
599
|
+
const lines = [];
|
|
600
|
+
lines.push(chalk.bold('Deferred global prompts cleanup'));
|
|
601
|
+
lines.push(chalk.dim('These global prompts will only be removed after matching replacement skills are installed.'));
|
|
602
|
+
for (const prompt of deferredPrompts) {
|
|
603
|
+
const toolLabel = prompt.toolId ? `${prompt.toolId}: ` : '';
|
|
604
|
+
lines.push(` • ${toolLabel}${prompt.path}`);
|
|
605
|
+
}
|
|
606
|
+
return lines.join('\n');
|
|
607
|
+
}
|
|
448
608
|
/**
|
|
449
609
|
* Extract tool IDs from detected legacy artifacts.
|
|
450
610
|
* Uses LEGACY_SLASH_COMMAND_PATHS to map paths back to tool IDs.
|
|
@@ -466,18 +626,13 @@ export function getToolsFromLegacyArtifacts(detection) {
|
|
|
466
626
|
// Match files to tool IDs using glob patterns
|
|
467
627
|
for (const file of detection.slashCommandFiles) {
|
|
468
628
|
// Normalize file path to use forward slashes for consistent matching (Windows compatibility)
|
|
469
|
-
const normalizedFile = file
|
|
629
|
+
const normalizedFile = normalizePathForMatch(file);
|
|
470
630
|
for (const [toolId, pattern] of Object.entries(LEGACY_SLASH_COMMAND_PATHS)) {
|
|
471
631
|
if (pattern.type === 'files' && pattern.pattern) {
|
|
472
|
-
// Convert glob pattern to regex for matching
|
|
473
|
-
// e.g., '.cursor/commands/openspec-*.md' -> /^\.cursor\/commands\/openspec-.*\.md$/
|
|
474
632
|
const patterns = Array.isArray(pattern.pattern) ? pattern.pattern : [pattern.pattern];
|
|
475
633
|
let matched = false;
|
|
476
634
|
for (const p of patterns) {
|
|
477
|
-
const
|
|
478
|
-
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
|
|
479
|
-
.replace(/\*/g, '.*'); // Replace * with .*
|
|
480
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
635
|
+
const regex = globToRegex(p);
|
|
481
636
|
if (regex.test(normalizedFile)) {
|
|
482
637
|
tools.add(toolId);
|
|
483
638
|
matched = true;
|
|
@@ -489,8 +644,82 @@ export function getToolsFromLegacyArtifacts(detection) {
|
|
|
489
644
|
}
|
|
490
645
|
}
|
|
491
646
|
}
|
|
647
|
+
for (const prompt of getLegacyGlobalPromptMatches(detection)) {
|
|
648
|
+
tools.add(prompt.toolId);
|
|
649
|
+
}
|
|
492
650
|
return Array.from(tools);
|
|
493
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* Normalizes global Codex prompt matches so callers can rely on workflow-aware
|
|
654
|
+
* metadata even when older detection results only carry file paths.
|
|
655
|
+
*/
|
|
656
|
+
export function getLegacyGlobalPromptMatches(detection) {
|
|
657
|
+
if (detection.globalSlashCommandDetails && detection.globalSlashCommandDetails.length > 0) {
|
|
658
|
+
return detection.globalSlashCommandDetails;
|
|
659
|
+
}
|
|
660
|
+
return detection.globalSlashCommandFiles
|
|
661
|
+
.map((filePath) => getManagedGlobalLegacyPromptMetadata(filePath))
|
|
662
|
+
.filter((match) => match !== undefined);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Collects workflow IDs inferred from detected legacy global prompts for a
|
|
666
|
+
* specific tool.
|
|
667
|
+
*/
|
|
668
|
+
export function getLegacyWorkflowIdsForTool(detection, toolId) {
|
|
669
|
+
const workflows = new Set();
|
|
670
|
+
for (const prompt of getLegacyGlobalPromptMatches(detection)) {
|
|
671
|
+
if (prompt.toolId !== toolId) {
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
for (const workflowId of prompt.workflowIds) {
|
|
675
|
+
workflows.add(workflowId);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return Array.from(workflows);
|
|
679
|
+
}
|
|
680
|
+
function hasLegacyArtifacts(detection) {
|
|
681
|
+
return (detection.configFiles.length > 0 ||
|
|
682
|
+
detection.slashCommandDirs.length > 0 ||
|
|
683
|
+
detection.slashCommandFiles.length > 0 ||
|
|
684
|
+
detection.globalSlashCommandFiles.length > 0 ||
|
|
685
|
+
detection.hasOpenspecAgents ||
|
|
686
|
+
detection.hasRootAgentsWithMarkers ||
|
|
687
|
+
detection.hasProjectMd);
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Returns a detection snapshot with global Codex prompt cleanup removed so
|
|
691
|
+
* callers can safely perform the immediate, non-deferred cleanup pass.
|
|
692
|
+
*/
|
|
693
|
+
export function omitGlobalLegacyPromptFiles(detection) {
|
|
694
|
+
const nextDetection = {
|
|
695
|
+
...detection,
|
|
696
|
+
globalSlashCommandFiles: [],
|
|
697
|
+
globalSlashCommandDetails: [],
|
|
698
|
+
};
|
|
699
|
+
nextDetection.hasLegacyArtifacts = hasLegacyArtifacts(nextDetection);
|
|
700
|
+
return nextDetection;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Builds a detection snapshot containing only the selected global Codex prompt
|
|
704
|
+
* matches for replacement-gated cleanup.
|
|
705
|
+
*/
|
|
706
|
+
export function pickGlobalLegacyPromptFiles(detection, filePaths) {
|
|
707
|
+
const selectedPaths = new Set(filePaths.map((filePath) => path.resolve(filePath)));
|
|
708
|
+
const details = getLegacyGlobalPromptMatches(detection)
|
|
709
|
+
.filter((detail) => selectedPaths.has(path.resolve(detail.path)));
|
|
710
|
+
return {
|
|
711
|
+
configFiles: [],
|
|
712
|
+
configFilesToUpdate: [],
|
|
713
|
+
slashCommandDirs: [],
|
|
714
|
+
slashCommandFiles: [],
|
|
715
|
+
globalSlashCommandFiles: details.map((detail) => detail.path),
|
|
716
|
+
globalSlashCommandDetails: details,
|
|
717
|
+
hasOpenspecAgents: false,
|
|
718
|
+
hasProjectMd: false,
|
|
719
|
+
hasRootAgentsWithMarkers: false,
|
|
720
|
+
hasLegacyArtifacts: details.length > 0,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
494
723
|
/**
|
|
495
724
|
* Generates a migration hint message for project.md.
|
|
496
725
|
* This is shown when project.md exists and needs manual migration to config.yaml.
|
package/dist/core/list.js
CHANGED
|
@@ -2,8 +2,24 @@ import { promises as fs } from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
|
|
4
4
|
import { readFileSync } from 'fs';
|
|
5
|
-
import { join } from 'path';
|
|
6
5
|
import { MarkdownParser } from './parsers/markdown-parser.js';
|
|
6
|
+
import { discoverSpecFiles } from '../utils/spec-discovery.js';
|
|
7
|
+
function isMissingPathError(error) {
|
|
8
|
+
return (typeof error === 'object' &&
|
|
9
|
+
error !== null &&
|
|
10
|
+
'code' in error &&
|
|
11
|
+
error.code === 'ENOENT');
|
|
12
|
+
}
|
|
13
|
+
async function readChangeDirectoryEntries(changesDir) {
|
|
14
|
+
try {
|
|
15
|
+
return await fs.readdir(changesDir, { withFileTypes: true });
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (isMissingPathError(error))
|
|
19
|
+
return [];
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
7
23
|
/**
|
|
8
24
|
* Get the most recent modification time of any file in a directory (recursive).
|
|
9
25
|
* Falls back to the directory's own mtime if no files are found.
|
|
@@ -64,15 +80,8 @@ export class ListCommand {
|
|
|
64
80
|
const { sort = 'recent', json = false, root } = options;
|
|
65
81
|
if (mode === 'changes') {
|
|
66
82
|
const changesDir = path.join(targetPath, 'openspec', 'changes');
|
|
67
|
-
// Check if changes directory exists
|
|
68
|
-
try {
|
|
69
|
-
await fs.access(changesDir);
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
throw new Error("No OpenSpec changes directory found. Run 'openspec init' first.");
|
|
73
|
-
}
|
|
74
83
|
// Get all directories in changes (excluding archive)
|
|
75
|
-
const entries = await
|
|
84
|
+
const entries = await readChangeDirectoryEntries(changesDir);
|
|
76
85
|
const changeDirs = entries
|
|
77
86
|
.filter(entry => entry.isDirectory() && entry.name !== 'archive')
|
|
78
87
|
.map(entry => entry.name);
|
|
@@ -88,7 +97,7 @@ export class ListCommand {
|
|
|
88
97
|
// Collect information about each change
|
|
89
98
|
const changes = [];
|
|
90
99
|
for (const changeDir of changeDirs) {
|
|
91
|
-
const progress = await getTaskProgressForChange(changesDir, changeDir);
|
|
100
|
+
const progress = await getTaskProgressForChange(changesDir, changeDir, targetPath);
|
|
92
101
|
const changePath = path.join(changesDir, changeDir);
|
|
93
102
|
const lastModified = await getLastModified(changePath);
|
|
94
103
|
changes.push({
|
|
@@ -143,9 +152,8 @@ export class ListCommand {
|
|
|
143
152
|
}
|
|
144
153
|
return;
|
|
145
154
|
}
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
if (specDirs.length === 0) {
|
|
155
|
+
const discovered = await discoverSpecFiles(specsDir);
|
|
156
|
+
if (discovered.length === 0) {
|
|
149
157
|
if (json) {
|
|
150
158
|
console.log(JSON.stringify({ specs: [], ...(root ? { root } : {}) }, null, 2));
|
|
151
159
|
}
|
|
@@ -155,10 +163,9 @@ export class ListCommand {
|
|
|
155
163
|
return;
|
|
156
164
|
}
|
|
157
165
|
const specs = [];
|
|
158
|
-
for (const id of
|
|
159
|
-
const specPath = join(specsDir, id, 'spec.md');
|
|
166
|
+
for (const { id, specFile } of discovered) {
|
|
160
167
|
try {
|
|
161
|
-
const content = readFileSync(
|
|
168
|
+
const content = readFileSync(specFile, 'utf-8');
|
|
162
169
|
const parser = new MarkdownParser(content);
|
|
163
170
|
const spec = parser.parseSpec(id);
|
|
164
171
|
specs.push({ id, requirementCount: spec.requirements.length });
|
package/dist/core/migration.d.ts
CHANGED
|
@@ -4,7 +4,79 @@
|
|
|
4
4
|
* One-time migration logic for existing projects when profile system is introduced.
|
|
5
5
|
* Called by both init and update commands before profile resolution.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type AIToolOption } from './config.js';
|
|
8
|
+
export interface LegacyToolRoot {
|
|
9
|
+
/** Former tool root, e.g. '.kimi' */
|
|
10
|
+
root: string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether leaving this root requires the user's say-so. False when the old
|
|
13
|
+
* product is gone and its directory is certainly dead. True when the old
|
|
14
|
+
* location may still be the live one for somebody.
|
|
15
|
+
*/
|
|
16
|
+
needsConsent: boolean;
|
|
17
|
+
/** Migrations that need a freshly generated destination run afterward. */
|
|
18
|
+
timing?: 'before-generation' | 'after-generation';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Former tool roots whose OpenSpec-managed content belongs under the tool's
|
|
22
|
+
* current skillsDir. User files are never touched.
|
|
23
|
+
*/
|
|
24
|
+
export declare const LEGACY_TOOL_ROOTS: Record<string, LegacyToolRoot[]>;
|
|
25
|
+
export interface LegacyToolMigration {
|
|
26
|
+
toolId: string;
|
|
27
|
+
/** Legacy tool root, e.g. '.windsurf' */
|
|
28
|
+
from: string;
|
|
29
|
+
/** Current tool root, e.g. '.devin' */
|
|
30
|
+
to: string;
|
|
31
|
+
/** Skill directories that moved, or would move */
|
|
32
|
+
skillDirs: number;
|
|
33
|
+
/** Command files that moved, or would move */
|
|
34
|
+
commandFiles: number;
|
|
35
|
+
/**
|
|
36
|
+
* OpenSpec-managed files left under the legacy root because the copy there
|
|
37
|
+
* differs materially from the one that survives, so it is reported rather
|
|
38
|
+
* than dropped.
|
|
39
|
+
*/
|
|
40
|
+
keptInPlace: number;
|
|
41
|
+
/** Whether this move needs the user's consent first */
|
|
42
|
+
needsConsent: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Reports the OpenSpec content sitting under each tool's legacy root, without
|
|
46
|
+
* moving anything. Callers use this to ask before a move that needs consent.
|
|
47
|
+
*/
|
|
48
|
+
export declare function findLegacyToolMigrations(projectPath: string, timing?: 'before-generation' | 'after-generation'): LegacyToolMigration[];
|
|
49
|
+
/**
|
|
50
|
+
* Moves OpenSpec-managed skill directories (openspec-*) and command files
|
|
51
|
+
* (opsx-*) from a tool's legacy root to its current one. When the destination
|
|
52
|
+
* already exists the legacy copy is removed instead. Legacy directories are
|
|
53
|
+
* deleted only when left empty, so user files under the old location — a
|
|
54
|
+
* hand-written Cascade workflow next to the generated ones — are preserved.
|
|
55
|
+
*
|
|
56
|
+
* @param projectPath - Project root
|
|
57
|
+
* @param toolIds - Restrict the move to these tools; omit to move every tool
|
|
58
|
+
* whose legacy root needs no consent
|
|
59
|
+
*/
|
|
60
|
+
export declare function migrateLegacyToolDirs(projectPath: string, toolIds?: string[], timing?: 'before-generation' | 'after-generation'): LegacyToolMigration[];
|
|
61
|
+
/**
|
|
62
|
+
* Summarizes what a migration moved, e.g. "6 skills and 6 commands".
|
|
63
|
+
*/
|
|
64
|
+
export declare function describeLegacyMigration(migration: LegacyToolMigration): string;
|
|
65
|
+
/**
|
|
66
|
+
* Names OpenSpec-managed files the move deliberately left behind, so a user
|
|
67
|
+
* who customized one knows there are now two copies to reconcile.
|
|
68
|
+
*/
|
|
69
|
+
export declare function keptInPlaceNotice(migration: LegacyToolMigration): string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Whether a migration has anything to move, as opposed to only files left in
|
|
72
|
+
* place. Callers use this to avoid offering a move of nothing.
|
|
73
|
+
*/
|
|
74
|
+
export declare function hasMovableContent(migration: LegacyToolMigration): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Explains why a consent-gated move is being offered, in the user's terms.
|
|
77
|
+
* Keyed by tool so the reason is specific rather than a generic "files moved".
|
|
78
|
+
*/
|
|
79
|
+
export declare function legacyMigrationNotice(migration: LegacyToolMigration): string;
|
|
8
80
|
/**
|
|
9
81
|
* Scans installed workflow files across all detected tools and returns
|
|
10
82
|
* the union of installed workflow IDs.
|