@thiagodiogo/pscode 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +142 -0
- package/bin/pscode.js +5 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +526 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.js +277 -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 +611 -0
- package/dist/commands/context-store.d.ts +3 -0
- package/dist/commands/context-store.js +282 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +183 -0
- package/dist/commands/initiative.d.ts +13 -0
- package/dist/commands/initiative.js +318 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.js +869 -0
- package/dist/commands/show.d.ts +14 -0
- package/dist/commands/show.js +132 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.js +225 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +294 -0
- package/dist/commands/workflow/index.d.ts +19 -0
- package/dist/commands/workflow/index.js +13 -0
- package/dist/commands/workflow/initiative-link.d.ts +24 -0
- package/dist/commands/workflow/initiative-link.js +47 -0
- package/dist/commands/workflow/instructions.d.ts +29 -0
- package/dist/commands/workflow/instructions.js +344 -0
- package/dist/commands/workflow/new-change.d.ts +17 -0
- package/dist/commands/workflow/new-change.js +141 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.js +34 -0
- package/dist/commands/workflow/set-change.d.ts +13 -0
- package/dist/commands/workflow/set-change.js +87 -0
- package/dist/commands/workflow/shared.d.ts +59 -0
- package/dist/commands/workflow/shared.js +116 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.js +90 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +69 -0
- package/dist/commands/workspace/context-status.d.ts +4 -0
- package/dist/commands/workspace/context-status.js +59 -0
- package/dist/commands/workspace/open-view.d.ts +62 -0
- package/dist/commands/workspace/open-view.js +228 -0
- package/dist/commands/workspace/open.d.ts +37 -0
- package/dist/commands/workspace/open.js +102 -0
- package/dist/commands/workspace/opener-selection.d.ts +11 -0
- package/dist/commands/workspace/opener-selection.js +93 -0
- package/dist/commands/workspace/operations.d.ts +28 -0
- package/dist/commands/workspace/operations.js +543 -0
- package/dist/commands/workspace/prompt-theme.d.ts +29 -0
- package/dist/commands/workspace/prompt-theme.js +24 -0
- package/dist/commands/workspace/registration.d.ts +13 -0
- package/dist/commands/workspace/registration.js +84 -0
- package/dist/commands/workspace/selection.d.ts +6 -0
- package/dist/commands/workspace/selection.js +122 -0
- package/dist/commands/workspace/types.d.ts +103 -0
- package/dist/commands/workspace/types.js +36 -0
- package/dist/commands/workspace.d.ts +6 -0
- package/dist/commands/workspace.js +678 -0
- package/dist/core/archive.d.ts +11 -0
- package/dist/core/archive.js +318 -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 +183 -0
- package/dist/core/artifact-graph/instruction-loader.js +256 -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 +18 -0
- package/dist/core/change-metadata/schema.js +28 -0
- package/dist/core/change-status-policy.d.ts +50 -0
- package/dist/core/change-status-policy.js +70 -0
- package/dist/core/collections/index.d.ts +3 -0
- package/dist/core/collections/index.js +3 -0
- package/dist/core/collections/initiatives/collection.d.ts +4 -0
- package/dist/core/collections/initiatives/collection.js +17 -0
- package/dist/core/collections/initiatives/index.d.ts +6 -0
- package/dist/core/collections/initiatives/index.js +6 -0
- package/dist/core/collections/initiatives/operations.d.ts +49 -0
- package/dist/core/collections/initiatives/operations.js +175 -0
- package/dist/core/collections/initiatives/resolution.d.ts +87 -0
- package/dist/core/collections/initiatives/resolution.js +374 -0
- package/dist/core/collections/initiatives/schema.d.ts +41 -0
- package/dist/core/collections/initiatives/schema.js +134 -0
- package/dist/core/collections/initiatives/templates.d.ts +12 -0
- package/dist/core/collections/initiatives/templates.js +90 -0
- package/dist/core/collections/runtime.d.ts +46 -0
- package/dist/core/collections/runtime.js +194 -0
- package/dist/core/command-generation/adapters/claude.d.ts +13 -0
- package/dist/core/command-generation/adapters/claude.js +50 -0
- package/dist/core/command-generation/adapters/codex.d.ts +16 -0
- package/dist/core/command-generation/adapters/codex.js +39 -0
- package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
- package/dist/core/command-generation/adapters/cursor.js +44 -0
- package/dist/core/command-generation/adapters/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/index.d.ts +11 -0
- package/dist/core/command-generation/adapters/index.js +11 -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 +35 -0
- package/dist/core/command-generation/registry.js +55 -0
- package/dist/core/command-generation/types.d.ts +56 -0
- package/dist/core/command-generation/types.js +8 -0
- package/dist/core/completions/command-registry.d.ts +3 -0
- package/dist/core/completions/command-registry.js +939 -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 +274 -0
- package/dist/core/completions/installers/bash-installer.d.ts +87 -0
- package/dist/core/completions/installers/bash-installer.js +318 -0
- package/dist/core/completions/installers/fish-installer.d.ts +43 -0
- package/dist/core/completions/installers/fish-installer.js +143 -0
- package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
- package/dist/core/completions/installers/powershell-installer.js +387 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +117 -0
- package/dist/core/completions/installers/zsh-installer.js +421 -0
- package/dist/core/completions/shared-flags.d.ts +12 -0
- package/dist/core/completions/shared-flags.js +28 -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/config-prompts.d.ts +9 -0
- package/dist/core/config-prompts.js +34 -0
- package/dist/core/config-schema.d.ts +86 -0
- package/dist/core/config-schema.js +213 -0
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.js +13 -0
- package/dist/core/context-store/binding.d.ts +53 -0
- package/dist/core/context-store/binding.js +197 -0
- package/dist/core/context-store/errors.d.ts +20 -0
- package/dist/core/context-store/errors.js +22 -0
- package/dist/core/context-store/foundation.d.ts +54 -0
- package/dist/core/context-store/foundation.js +318 -0
- package/dist/core/context-store/index.d.ts +6 -0
- package/dist/core/context-store/index.js +6 -0
- package/dist/core/context-store/operations.d.ts +62 -0
- package/dist/core/context-store/operations.js +352 -0
- package/dist/core/context-store/registry.d.ts +35 -0
- package/dist/core/context-store/registry.js +158 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/global-config.d.ts +49 -0
- package/dist/core/global-config.js +124 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +7 -0
- package/dist/core/init.d.ts +40 -0
- package/dist/core/init.js +676 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +489 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +171 -0
- package/dist/core/migration.d.ts +23 -0
- package/dist/core/migration.js +108 -0
- package/dist/core/openspec-migration.d.ts +71 -0
- package/dist/core/openspec-migration.js +320 -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 +21 -0
- package/dist/core/planning-home.js +108 -0
- package/dist/core/profile-sync-drift.d.ts +38 -0
- package/dist/core/profile-sync-drift.js +203 -0
- package/dist/core/profiles.d.ts +26 -0
- package/dist/core/profiles.js +43 -0
- package/dist/core/project-config.d.ts +64 -0
- package/dist/core/project-config.js +223 -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 +102 -0
- package/dist/core/shared/tool-detection.d.ts +71 -0
- package/dist/core/shared/tool-detection.js +158 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.js +392 -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 +21 -0
- package/dist/core/templates/skill-templates.js +21 -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 +287 -0
- package/dist/core/templates/workflows/archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/archive-change.js +227 -0
- package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
- package/dist/core/templates/workflows/bulk-archive-change.js +492 -0
- package/dist/core/templates/workflows/continue-change.d.ts +10 -0
- package/dist/core/templates/workflows/continue-change.js +234 -0
- package/dist/core/templates/workflows/explore.d.ts +10 -0
- package/dist/core/templates/workflows/explore.js +755 -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 +200 -0
- package/dist/core/templates/workflows/new-change.d.ts +10 -0
- package/dist/core/templates/workflows/new-change.js +143 -0
- package/dist/core/templates/workflows/onboard.d.ts +10 -0
- package/dist/core/templates/workflows/onboard.js +607 -0
- package/dist/core/templates/workflows/propose.d.ts +10 -0
- package/dist/core/templates/workflows/propose.js +347 -0
- package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
- package/dist/core/templates/workflows/sync-specs.js +290 -0
- package/dist/core/templates/workflows/trello-draft.d.ts +12 -0
- package/dist/core/templates/workflows/trello-draft.js +217 -0
- package/dist/core/templates/workflows/trello-setup.d.ts +12 -0
- package/dist/core/templates/workflows/trello-setup.js +315 -0
- package/dist/core/templates/workflows/verify-change.d.ts +10 -0
- package/dist/core/templates/workflows/verify-change.js +338 -0
- package/dist/core/trello-config.d.ts +90 -0
- package/dist/core/trello-config.js +86 -0
- package/dist/core/trello-init-prompt.d.ts +61 -0
- package/dist/core/trello-init-prompt.js +180 -0
- package/dist/core/update.d.ts +82 -0
- package/dist/core/update.js +560 -0
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/validator.d.ts +33 -0
- package/dist/core/validation/validator.js +418 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +168 -0
- package/dist/core/workspace/foundation.d.ts +62 -0
- package/dist/core/workspace/foundation.js +274 -0
- package/dist/core/workspace/index.d.ts +8 -0
- package/dist/core/workspace/index.js +8 -0
- package/dist/core/workspace/legacy-state.d.ts +28 -0
- package/dist/core/workspace/legacy-state.js +200 -0
- package/dist/core/workspace/link-input.d.ts +9 -0
- package/dist/core/workspace/link-input.js +32 -0
- package/dist/core/workspace/open-surface.d.ts +43 -0
- package/dist/core/workspace/open-surface.js +214 -0
- package/dist/core/workspace/openers.d.ts +21 -0
- package/dist/core/workspace/openers.js +119 -0
- package/dist/core/workspace/registry.d.ts +24 -0
- package/dist/core/workspace/registry.js +146 -0
- package/dist/core/workspace/skills.d.ts +57 -0
- package/dist/core/workspace/skills.js +334 -0
- package/dist/core/workspace/state-io.d.ts +10 -0
- package/dist/core/workspace/state-io.js +119 -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 +54 -0
- package/dist/utils/change-metadata.js +141 -0
- package/dist/utils/change-utils.d.ts +71 -0
- package/dist/utils/change-utils.js +123 -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 +301 -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/schemas/workspace-planning/schema.yaml +72 -0
- package/schemas/workspace-planning/templates/design.md +33 -0
- package/schemas/workspace-planning/templates/proposal.md +28 -0
- package/schemas/workspace-planning/templates/spec.md +9 -0
- package/schemas/workspace-planning/templates/tasks.md +15 -0
- package/scripts/postinstall.js +83 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy Tool → Pscode Migration
|
|
3
|
+
*
|
|
4
|
+
* Detects projects using the old "openspec" structure and migrates them
|
|
5
|
+
* cleanly to Pscode: deletes old skills/commands generated by openspec
|
|
6
|
+
* (opsx-* prefix), renames the .openspec/ folder to pscode/, and
|
|
7
|
+
* migrates any stale config fields.
|
|
8
|
+
*
|
|
9
|
+
* Migration is triggered automatically during `pscode init` when an
|
|
10
|
+
* .openspec/ directory is detected. The user is prompted to confirm before
|
|
11
|
+
* any changes are made.
|
|
12
|
+
*/
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { promises as fs } from 'fs';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { AI_TOOLS } from './config.js';
|
|
17
|
+
import { CommandAdapterRegistry } from './command-generation/index.js';
|
|
18
|
+
import { ALL_WORKFLOWS } from './profiles.js';
|
|
19
|
+
import { FileSystemUtils } from '../utils/file-system.js';
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Constants
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Candidate directory names used by the old openspec tool.
|
|
25
|
+
* Check both "openspec" (without dot) and ".openspec" (with dot).
|
|
26
|
+
*/
|
|
27
|
+
const OPENSPEC_DIR_CANDIDATES = ['openspec', '.openspec'];
|
|
28
|
+
/** Old skill prefix used by openspec */
|
|
29
|
+
const OPENSPEC_SKILL_PREFIX = 'opsx-';
|
|
30
|
+
/** Old slash command prefix used by openspec */
|
|
31
|
+
const OPENSPEC_COMMAND_PREFIX = 'opsx-';
|
|
32
|
+
/** Old slash command namespaces (subdirectories inside commands/) to remove */
|
|
33
|
+
const OPENSPEC_COMMAND_DIRS = ['opsx', 'openspec'];
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Detection
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
/**
|
|
38
|
+
* Detects legacy tool artifacts in a project.
|
|
39
|
+
* Checks for both "openspec/" and ".openspec/" directory names.
|
|
40
|
+
*/
|
|
41
|
+
export async function detectLegacyToolArtifacts(projectPath) {
|
|
42
|
+
// Find whichever openspec directory exists (prefer "openspec" over ".openspec")
|
|
43
|
+
let legacyDirName = OPENSPEC_DIR_CANDIDATES[0];
|
|
44
|
+
let legacyDirPath = path.join(projectPath, legacyDirName);
|
|
45
|
+
let hasLegacyDir = false;
|
|
46
|
+
for (const candidate of OPENSPEC_DIR_CANDIDATES) {
|
|
47
|
+
const candidatePath = path.join(projectPath, candidate);
|
|
48
|
+
if (await FileSystemUtils.directoryExists(candidatePath)) {
|
|
49
|
+
legacyDirName = candidate;
|
|
50
|
+
legacyDirPath = candidatePath;
|
|
51
|
+
hasLegacyDir = true;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const legacySkillFiles = [];
|
|
56
|
+
const legacyCommandFiles = [];
|
|
57
|
+
// Scan all known tool skill directories for opsx-* skill dirs
|
|
58
|
+
for (const tool of AI_TOOLS) {
|
|
59
|
+
if (!tool.skillsDir)
|
|
60
|
+
continue;
|
|
61
|
+
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
62
|
+
if (await FileSystemUtils.directoryExists(skillsDir)) {
|
|
63
|
+
try {
|
|
64
|
+
const entries = await fs.readdir(skillsDir);
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
// Match both opsx-* prefixed dirs and any dir containing "openspec"
|
|
67
|
+
if (entry.startsWith(OPENSPEC_SKILL_PREFIX) || entry.includes('openspec')) {
|
|
68
|
+
legacySkillFiles.push(path.join(tool.skillsDir, 'skills', entry));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// ignore
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Scan for old command files (opsx-*.md or openspec/ dir)
|
|
77
|
+
const adapter = CommandAdapterRegistry.get(tool.value);
|
|
78
|
+
if (!adapter)
|
|
79
|
+
continue;
|
|
80
|
+
// Check for old opsx-prefixed command files using the adapter's base dir
|
|
81
|
+
for (const workflow of ALL_WORKFLOWS) {
|
|
82
|
+
const cmdPath = adapter.getFilePath(workflow);
|
|
83
|
+
const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
|
|
84
|
+
const dir = path.dirname(fullPath);
|
|
85
|
+
const basename = path.basename(fullPath);
|
|
86
|
+
// Check for opsx-prefixed variant in the same directory
|
|
87
|
+
const opsPrefixedName = basename.replace(/^pastel-/, OPENSPEC_COMMAND_PREFIX);
|
|
88
|
+
if (opsPrefixedName !== basename) {
|
|
89
|
+
const opsPrefixedPath = path.join(dir, opsPrefixedName);
|
|
90
|
+
try {
|
|
91
|
+
await fs.access(opsPrefixedPath);
|
|
92
|
+
const rel = path.relative(projectPath, opsPrefixedPath).replace(/\\/g, '/');
|
|
93
|
+
legacyCommandFiles.push(rel);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// file doesn't exist
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Check for old opsx/ and openspec/ command directories (claude/gemini-style)
|
|
101
|
+
const toolDir = path.join(projectPath, tool.skillsDir);
|
|
102
|
+
for (const cmdDirName of OPENSPEC_COMMAND_DIRS) {
|
|
103
|
+
const legacyCmdDir = path.join(toolDir, 'commands', cmdDirName);
|
|
104
|
+
if (await FileSystemUtils.directoryExists(legacyCmdDir)) {
|
|
105
|
+
const rel = path.relative(projectPath, legacyCmdDir).replace(/\\/g, '/');
|
|
106
|
+
legacyCommandFiles.push(rel + '/');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const hasLegacyArtifacts = hasLegacyDir ||
|
|
111
|
+
legacySkillFiles.length > 0 ||
|
|
112
|
+
legacyCommandFiles.length > 0;
|
|
113
|
+
return {
|
|
114
|
+
hasLegacyDir,
|
|
115
|
+
legacyDirPath,
|
|
116
|
+
legacyDirName,
|
|
117
|
+
legacySkillFiles,
|
|
118
|
+
legacyCommandFiles,
|
|
119
|
+
hasLegacyArtifacts,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Summary display
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
/**
|
|
126
|
+
* Formats a human-readable summary of what was detected.
|
|
127
|
+
* @param pscodeAlreadyExists - when true, shows merge instead of rename
|
|
128
|
+
*/
|
|
129
|
+
export function formatLegacyToolDetectionSummary(detection, pscodeAlreadyExists = false) {
|
|
130
|
+
const lines = [];
|
|
131
|
+
lines.push(chalk.bold('This project uses the OpenSpec framework'));
|
|
132
|
+
lines.push('');
|
|
133
|
+
lines.push('OpenSpec and Pscode share the same workflow philosophy, but Pscode');
|
|
134
|
+
lines.push('is the evolution of the framework — with better tooling, more AI integrations,');
|
|
135
|
+
lines.push('and active development going forward.');
|
|
136
|
+
lines.push('');
|
|
137
|
+
lines.push('Migrating will:');
|
|
138
|
+
lines.push('');
|
|
139
|
+
if (detection.hasLegacyDir) {
|
|
140
|
+
const dirName = detection.legacyDirName;
|
|
141
|
+
if (pscodeAlreadyExists) {
|
|
142
|
+
lines.push(` • Merge ${chalk.cyan(dirName + '/changes')} and ${chalk.cyan(dirName + '/specs')} into ${chalk.cyan('pscode/')} — your work stays intact`);
|
|
143
|
+
lines.push(` • Remove ${chalk.cyan(dirName + '/')} after merge`);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
lines.push(` • Move ${chalk.cyan(dirName + '/')} → ${chalk.cyan('pscode/')} — all your changes and specs come along`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (detection.legacySkillFiles.length > 0) {
|
|
150
|
+
lines.push(` • Remove ${detection.legacySkillFiles.length} old opsx-* / openspec-* skill director${detection.legacySkillFiles.length === 1 ? 'y' : 'ies'}`);
|
|
151
|
+
for (const f of detection.legacySkillFiles) {
|
|
152
|
+
lines.push(` ${chalk.dim(f + '/')}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (detection.legacyCommandFiles.length > 0) {
|
|
156
|
+
lines.push(` • Remove ${detection.legacyCommandFiles.length} old opsx/ / openspec/ command folder${detection.legacyCommandFiles.length === 1 ? '' : 's'} and file${detection.legacyCommandFiles.length === 1 ? '' : 's'}`);
|
|
157
|
+
for (const f of detection.legacyCommandFiles) {
|
|
158
|
+
lines.push(` ${chalk.dim(f)}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
lines.push('');
|
|
162
|
+
lines.push(chalk.dim('Fresh Pscode skills and commands will be installed right after.'));
|
|
163
|
+
return lines.join('\n');
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Formats a summary of what was done during migration.
|
|
167
|
+
*/
|
|
168
|
+
export function formatLegacyToolMigrationSummary(result) {
|
|
169
|
+
const lines = [];
|
|
170
|
+
if (result.renamedDir) {
|
|
171
|
+
lines.push(` ✓ Renamed ${result.sourceDirName}/ → pscode/`);
|
|
172
|
+
}
|
|
173
|
+
if (result.mergedDir) {
|
|
174
|
+
lines.push(` ✓ Merged ${result.sourceDirName}/ content into pscode/`);
|
|
175
|
+
for (const f of result.mergedFiles) {
|
|
176
|
+
lines.push(` ${chalk.dim(f)}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const f of result.deletedSkills) {
|
|
180
|
+
lines.push(` ✓ Deleted skill: ${f}`);
|
|
181
|
+
}
|
|
182
|
+
for (const f of result.deletedCommands) {
|
|
183
|
+
lines.push(` ✓ Deleted command: ${f}`);
|
|
184
|
+
}
|
|
185
|
+
for (const e of result.errors) {
|
|
186
|
+
lines.push(` ⚠ ${e}`);
|
|
187
|
+
}
|
|
188
|
+
return lines.join('\n');
|
|
189
|
+
}
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// Migration
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
/** Subdirectories inside .openspec/ that carry user data worth migrating. */
|
|
194
|
+
const OPENSPEC_DATA_DIRS = ['changes', 'specs'];
|
|
195
|
+
/**
|
|
196
|
+
* Recursively copies files from `src` into `dst`.
|
|
197
|
+
* Existing destination files are never overwritten (skip-if-exists strategy).
|
|
198
|
+
* Returns the list of relative paths (relative to `src`) that were copied.
|
|
199
|
+
*/
|
|
200
|
+
async function mergeDirectoryInto(src, dst, baseRel = '') {
|
|
201
|
+
const copied = [];
|
|
202
|
+
let entries;
|
|
203
|
+
try {
|
|
204
|
+
entries = await fs.readdir(src, { withFileTypes: true });
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return copied; // src doesn't exist or can't be read
|
|
208
|
+
}
|
|
209
|
+
await fs.mkdir(dst, { recursive: true });
|
|
210
|
+
for (const entry of entries) {
|
|
211
|
+
const rel = baseRel ? `${baseRel}/${entry.name}` : entry.name;
|
|
212
|
+
const srcPath = path.join(src, entry.name);
|
|
213
|
+
const dstPath = path.join(dst, entry.name);
|
|
214
|
+
if (entry.isDirectory()) {
|
|
215
|
+
const sub = await mergeDirectoryInto(srcPath, dstPath, rel);
|
|
216
|
+
copied.push(...sub);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
// Skip if destination already exists
|
|
220
|
+
const dstExists = await fs.stat(dstPath).then(() => true).catch(() => false);
|
|
221
|
+
if (!dstExists) {
|
|
222
|
+
try {
|
|
223
|
+
await fs.copyFile(srcPath, dstPath);
|
|
224
|
+
copied.push(rel);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// best-effort
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return copied;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Runs the legacy tool → pscode migration.
|
|
236
|
+
* - Deletes old opsx-* skill directories
|
|
237
|
+
* - Deletes old openspec command files/directories
|
|
238
|
+
* - If pscode/ doesn't exist: renames .openspec/ → pscode/
|
|
239
|
+
* - If pscode/ already exists: merges changes/ and specs/ from .openspec/
|
|
240
|
+
* into pscode/ (existing files are never overwritten), then removes .openspec/
|
|
241
|
+
*/
|
|
242
|
+
export async function runLegacyToolMigration(projectPath, detection) {
|
|
243
|
+
const result = {
|
|
244
|
+
renamedDir: false,
|
|
245
|
+
mergedDir: false,
|
|
246
|
+
sourceDirName: detection.legacyDirName,
|
|
247
|
+
deletedSkills: [],
|
|
248
|
+
deletedCommands: [],
|
|
249
|
+
mergedFiles: [],
|
|
250
|
+
errors: [],
|
|
251
|
+
};
|
|
252
|
+
// 1. Delete old opsx-* skill directories
|
|
253
|
+
for (const relPath of detection.legacySkillFiles) {
|
|
254
|
+
const fullPath = path.join(projectPath, relPath);
|
|
255
|
+
try {
|
|
256
|
+
await fs.rm(fullPath, { recursive: true, force: true });
|
|
257
|
+
result.deletedSkills.push(relPath);
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
result.errors.push(`Could not delete skill ${relPath}: ${err.message}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// 2. Delete old openspec command files / directories
|
|
264
|
+
for (const relPath of detection.legacyCommandFiles) {
|
|
265
|
+
const fullPath = path.join(projectPath, relPath.replace(/\/$/, ''));
|
|
266
|
+
try {
|
|
267
|
+
const stat = await fs.stat(fullPath).catch(() => null);
|
|
268
|
+
if (stat?.isDirectory()) {
|
|
269
|
+
await fs.rm(fullPath, { recursive: true, force: true });
|
|
270
|
+
}
|
|
271
|
+
else if (stat) {
|
|
272
|
+
await fs.unlink(fullPath);
|
|
273
|
+
}
|
|
274
|
+
result.deletedCommands.push(relPath);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
result.errors.push(`Could not delete command ${relPath}: ${err.message}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// 3. Migrate .openspec/ directory
|
|
281
|
+
if (detection.hasLegacyDir) {
|
|
282
|
+
const pscodePath = path.join(projectPath, 'pscode');
|
|
283
|
+
const pscodeExists = await FileSystemUtils.directoryExists(pscodePath);
|
|
284
|
+
if (pscodeExists) {
|
|
285
|
+
// pscode/ already exists — merge user data subdirectories, then remove .openspec/
|
|
286
|
+
for (const subDir of OPENSPEC_DATA_DIRS) {
|
|
287
|
+
const srcSub = path.join(detection.legacyDirPath, subDir);
|
|
288
|
+
const dstSub = path.join(pscodePath, subDir);
|
|
289
|
+
const copied = await mergeDirectoryInto(srcSub, dstSub, subDir);
|
|
290
|
+
result.mergedFiles.push(...copied);
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
await fs.rm(detection.legacyDirPath, { recursive: true, force: true });
|
|
294
|
+
result.mergedDir = true;
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
result.errors.push(`Could not remove ${detection.legacyDirName}/ after merge: ${err.message}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
// pscode/ doesn't exist yet — simple rename
|
|
302
|
+
try {
|
|
303
|
+
await fs.rename(detection.legacyDirPath, pscodePath);
|
|
304
|
+
result.renamedDir = true;
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
result.errors.push(`Could not rename ${detection.legacyDirName}/ → pscode/: ${err.message}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Returns true when a `pscode/` directory already exists at the given project path.
|
|
315
|
+
* Used by the init command to pass the correct context to formatLegacyToolDetectionSummary.
|
|
316
|
+
*/
|
|
317
|
+
export async function pscodeDirExists(projectPath) {
|
|
318
|
+
return FileSystemUtils.directoryExists(path.join(projectPath, 'pscode'));
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=openspec-migration.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MarkdownParser } from './markdown-parser.js';
|
|
2
|
+
import { Change } from '../schemas/index.js';
|
|
3
|
+
export declare class ChangeParser extends MarkdownParser {
|
|
4
|
+
private changeDir;
|
|
5
|
+
constructor(content: string, changeDir: string);
|
|
6
|
+
parseChangeWithDeltas(name: string): Promise<Change>;
|
|
7
|
+
private parseDeltaSpecs;
|
|
8
|
+
private parseSpecDeltas;
|
|
9
|
+
private parseRenames;
|
|
10
|
+
private parseSectionsFromContent;
|
|
11
|
+
private getContentUntilNextHeaderFromLines;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=change-parser.d.ts.map
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { MarkdownParser } from './markdown-parser.js';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { promises as fs } from 'fs';
|
|
4
|
+
export class ChangeParser extends MarkdownParser {
|
|
5
|
+
changeDir;
|
|
6
|
+
constructor(content, changeDir) {
|
|
7
|
+
super(content);
|
|
8
|
+
this.changeDir = changeDir;
|
|
9
|
+
}
|
|
10
|
+
async parseChangeWithDeltas(name) {
|
|
11
|
+
const sections = this.parseSections();
|
|
12
|
+
const why = this.findSection(sections, 'Why')?.content || '';
|
|
13
|
+
const whatChanges = this.findSection(sections, 'What Changes')?.content || '';
|
|
14
|
+
if (!why) {
|
|
15
|
+
throw new Error('Change must have a Why section');
|
|
16
|
+
}
|
|
17
|
+
if (!whatChanges) {
|
|
18
|
+
throw new Error('Change must have a What Changes section');
|
|
19
|
+
}
|
|
20
|
+
// Parse deltas from the What Changes section (simple format)
|
|
21
|
+
const simpleDeltas = this.parseDeltas(whatChanges);
|
|
22
|
+
// Check if there are spec files with delta format
|
|
23
|
+
const specsDir = path.join(this.changeDir, 'specs');
|
|
24
|
+
const deltaDeltas = await this.parseDeltaSpecs(specsDir);
|
|
25
|
+
// Combine both types of deltas, preferring delta format if available
|
|
26
|
+
const deltas = deltaDeltas.length > 0 ? deltaDeltas : simpleDeltas;
|
|
27
|
+
return {
|
|
28
|
+
name,
|
|
29
|
+
why: why.trim(),
|
|
30
|
+
whatChanges: whatChanges.trim(),
|
|
31
|
+
deltas,
|
|
32
|
+
metadata: {
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
format: 'pscode-change',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async parseDeltaSpecs(specsDir) {
|
|
39
|
+
const deltas = [];
|
|
40
|
+
try {
|
|
41
|
+
const specDirs = await fs.readdir(specsDir, { withFileTypes: true });
|
|
42
|
+
for (const dir of specDirs) {
|
|
43
|
+
if (!dir.isDirectory())
|
|
44
|
+
continue;
|
|
45
|
+
const specName = dir.name;
|
|
46
|
+
const specFile = path.join(specsDir, specName, 'spec.md');
|
|
47
|
+
try {
|
|
48
|
+
const content = await fs.readFile(specFile, 'utf-8');
|
|
49
|
+
const specDeltas = this.parseSpecDeltas(specName, content);
|
|
50
|
+
deltas.push(...specDeltas);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
// Spec file might not exist, which is okay
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
// Specs directory might not exist, which is okay
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return deltas;
|
|
63
|
+
}
|
|
64
|
+
parseSpecDeltas(specName, content) {
|
|
65
|
+
const deltas = [];
|
|
66
|
+
const sections = this.parseSectionsFromContent(content);
|
|
67
|
+
// Parse ADDED requirements
|
|
68
|
+
const addedSection = this.findSection(sections, 'ADDED Requirements');
|
|
69
|
+
if (addedSection) {
|
|
70
|
+
const requirements = this.parseRequirements(addedSection);
|
|
71
|
+
requirements.forEach(req => {
|
|
72
|
+
deltas.push({
|
|
73
|
+
spec: specName,
|
|
74
|
+
operation: 'ADDED',
|
|
75
|
+
description: `Add requirement: ${req.text}`,
|
|
76
|
+
// Provide both single and plural forms for compatibility
|
|
77
|
+
requirement: req,
|
|
78
|
+
requirements: [req],
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Parse MODIFIED requirements
|
|
83
|
+
const modifiedSection = this.findSection(sections, 'MODIFIED Requirements');
|
|
84
|
+
if (modifiedSection) {
|
|
85
|
+
const requirements = this.parseRequirements(modifiedSection);
|
|
86
|
+
requirements.forEach(req => {
|
|
87
|
+
deltas.push({
|
|
88
|
+
spec: specName,
|
|
89
|
+
operation: 'MODIFIED',
|
|
90
|
+
description: `Modify requirement: ${req.text}`,
|
|
91
|
+
requirement: req,
|
|
92
|
+
requirements: [req],
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// Parse REMOVED requirements
|
|
97
|
+
const removedSection = this.findSection(sections, 'REMOVED Requirements');
|
|
98
|
+
if (removedSection) {
|
|
99
|
+
const requirements = this.parseRequirements(removedSection);
|
|
100
|
+
requirements.forEach(req => {
|
|
101
|
+
deltas.push({
|
|
102
|
+
spec: specName,
|
|
103
|
+
operation: 'REMOVED',
|
|
104
|
+
description: `Remove requirement: ${req.text}`,
|
|
105
|
+
requirement: req,
|
|
106
|
+
requirements: [req],
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// Parse RENAMED requirements
|
|
111
|
+
const renamedSection = this.findSection(sections, 'RENAMED Requirements');
|
|
112
|
+
if (renamedSection) {
|
|
113
|
+
const renames = this.parseRenames(renamedSection.content);
|
|
114
|
+
renames.forEach(rename => {
|
|
115
|
+
deltas.push({
|
|
116
|
+
spec: specName,
|
|
117
|
+
operation: 'RENAMED',
|
|
118
|
+
description: `Rename requirement from "${rename.from}" to "${rename.to}"`,
|
|
119
|
+
rename,
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return deltas;
|
|
124
|
+
}
|
|
125
|
+
parseRenames(content) {
|
|
126
|
+
const renames = [];
|
|
127
|
+
const lines = ChangeParser.normalizeContent(content).split('\n');
|
|
128
|
+
let currentRename = {};
|
|
129
|
+
for (const line of lines) {
|
|
130
|
+
const fromMatch = line.match(/^\s*-?\s*FROM:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
|
|
131
|
+
const toMatch = line.match(/^\s*-?\s*TO:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
|
|
132
|
+
if (fromMatch) {
|
|
133
|
+
currentRename.from = fromMatch[1].trim();
|
|
134
|
+
}
|
|
135
|
+
else if (toMatch) {
|
|
136
|
+
currentRename.to = toMatch[1].trim();
|
|
137
|
+
if (currentRename.from && currentRename.to) {
|
|
138
|
+
renames.push({
|
|
139
|
+
from: currentRename.from,
|
|
140
|
+
to: currentRename.to,
|
|
141
|
+
});
|
|
142
|
+
currentRename = {};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return renames;
|
|
147
|
+
}
|
|
148
|
+
parseSectionsFromContent(content) {
|
|
149
|
+
const normalizedContent = ChangeParser.normalizeContent(content);
|
|
150
|
+
const lines = normalizedContent.split('\n');
|
|
151
|
+
const codeFenceLineMask = ChangeParser.buildCodeFenceMask(lines);
|
|
152
|
+
const sections = [];
|
|
153
|
+
const stack = [];
|
|
154
|
+
for (let i = 0; i < lines.length; i++) {
|
|
155
|
+
const line = lines[i];
|
|
156
|
+
if (codeFenceLineMask[i]) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
160
|
+
if (headerMatch) {
|
|
161
|
+
const level = headerMatch[1].length;
|
|
162
|
+
const title = headerMatch[2].trim();
|
|
163
|
+
const contentLines = this.getContentUntilNextHeaderFromLines(lines, codeFenceLineMask, i + 1, level);
|
|
164
|
+
const section = {
|
|
165
|
+
level,
|
|
166
|
+
title,
|
|
167
|
+
content: contentLines.join('\n').trim(),
|
|
168
|
+
children: [],
|
|
169
|
+
};
|
|
170
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= level) {
|
|
171
|
+
stack.pop();
|
|
172
|
+
}
|
|
173
|
+
if (stack.length === 0) {
|
|
174
|
+
sections.push(section);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
stack[stack.length - 1].children.push(section);
|
|
178
|
+
}
|
|
179
|
+
stack.push(section);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return sections;
|
|
183
|
+
}
|
|
184
|
+
getContentUntilNextHeaderFromLines(lines, codeFenceLineMask, startLine, currentLevel) {
|
|
185
|
+
const contentLines = [];
|
|
186
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
187
|
+
const line = lines[i];
|
|
188
|
+
const headerMatch = codeFenceLineMask[i] ? null : line.match(/^(#{1,6})\s+/);
|
|
189
|
+
if (headerMatch && headerMatch[1].length <= currentLevel) {
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
contentLines.push(line);
|
|
193
|
+
}
|
|
194
|
+
return contentLines;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=change-parser.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Spec, Change, Requirement, Scenario, Delta } from '../schemas/index.js';
|
|
2
|
+
export interface Section {
|
|
3
|
+
level: number;
|
|
4
|
+
title: string;
|
|
5
|
+
content: string;
|
|
6
|
+
children: Section[];
|
|
7
|
+
}
|
|
8
|
+
export declare class MarkdownParser {
|
|
9
|
+
private lines;
|
|
10
|
+
private codeFenceLineMask;
|
|
11
|
+
private currentLine;
|
|
12
|
+
constructor(content: string);
|
|
13
|
+
protected static normalizeContent(content: string): string;
|
|
14
|
+
protected static buildCodeFenceMask(lines: string[]): boolean[];
|
|
15
|
+
private static getFenceMarker;
|
|
16
|
+
private static isClosingFence;
|
|
17
|
+
parseSpec(name: string): Spec;
|
|
18
|
+
parseChange(name: string): Change;
|
|
19
|
+
protected parseSections(): Section[];
|
|
20
|
+
protected getContentUntilNextHeader(startLine: number, currentLevel: number): string;
|
|
21
|
+
protected findSection(sections: Section[], title: string): Section | undefined;
|
|
22
|
+
protected parseRequirements(section: Section): Requirement[];
|
|
23
|
+
protected parseScenarios(requirementSection: Section): Scenario[];
|
|
24
|
+
protected parseDeltas(content: string): Delta[];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=markdown-parser.d.ts.map
|