@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,44 +2,44 @@
|
|
|
2
2
|
* Static template strings for Zsh completion scripts.
|
|
3
3
|
* These are Zsh-specific helper functions that never change.
|
|
4
4
|
*/
|
|
5
|
-
export const ZSH_DYNAMIC_HELPERS = `# Dynamic completion helpers
|
|
6
|
-
|
|
7
|
-
# Use openspec __complete to get available changes
|
|
8
|
-
_openspec_complete_changes() {
|
|
9
|
-
local -a changes
|
|
10
|
-
while IFS=$'\\t' read -r id desc; do
|
|
11
|
-
changes+=("$id:$desc")
|
|
12
|
-
done < <(openspec __complete changes 2>/dev/null)
|
|
13
|
-
_describe "change" changes
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
# Use openspec __complete to get available specs
|
|
17
|
-
_openspec_complete_specs() {
|
|
18
|
-
local -a specs
|
|
19
|
-
while IFS=$'\\t' read -r id desc; do
|
|
20
|
-
specs+=("$id:$desc")
|
|
21
|
-
done < <(openspec __complete specs 2>/dev/null)
|
|
22
|
-
_describe "spec" specs
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
# Get both changes and specs
|
|
26
|
-
_openspec_complete_items() {
|
|
27
|
-
local -a items
|
|
28
|
-
while IFS=$'\\t' read -r id desc; do
|
|
29
|
-
items+=("$id:$desc")
|
|
30
|
-
done < <(openspec __complete changes 2>/dev/null)
|
|
31
|
-
while IFS=$'\\t' read -r id desc; do
|
|
32
|
-
items+=("$id:$desc")
|
|
33
|
-
done < <(openspec __complete specs 2>/dev/null)
|
|
34
|
-
_describe "item" items
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
# Use openspec __complete to get available schemas
|
|
38
|
-
_openspec_complete_schemas() {
|
|
39
|
-
local -a schemas
|
|
40
|
-
while IFS=$'\\t' read -r id desc; do
|
|
41
|
-
schemas+=("$id:$desc")
|
|
42
|
-
done < <(openspec __complete schemas 2>/dev/null)
|
|
43
|
-
_describe "schema" schemas
|
|
5
|
+
export const ZSH_DYNAMIC_HELPERS = `# Dynamic completion helpers
|
|
6
|
+
|
|
7
|
+
# Use openspec __complete to get available changes
|
|
8
|
+
_openspec_complete_changes() {
|
|
9
|
+
local -a changes
|
|
10
|
+
while IFS=$'\\t' read -r id desc; do
|
|
11
|
+
changes+=("$id:$desc")
|
|
12
|
+
done < <(openspec __complete changes 2>/dev/null)
|
|
13
|
+
_describe "change" changes
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# Use openspec __complete to get available specs
|
|
17
|
+
_openspec_complete_specs() {
|
|
18
|
+
local -a specs
|
|
19
|
+
while IFS=$'\\t' read -r id desc; do
|
|
20
|
+
specs+=("$id:$desc")
|
|
21
|
+
done < <(openspec __complete specs 2>/dev/null)
|
|
22
|
+
_describe "spec" specs
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Get both changes and specs
|
|
26
|
+
_openspec_complete_items() {
|
|
27
|
+
local -a items
|
|
28
|
+
while IFS=$'\\t' read -r id desc; do
|
|
29
|
+
items+=("$id:$desc")
|
|
30
|
+
done < <(openspec __complete changes 2>/dev/null)
|
|
31
|
+
while IFS=$'\\t' read -r id desc; do
|
|
32
|
+
items+=("$id:$desc")
|
|
33
|
+
done < <(openspec __complete specs 2>/dev/null)
|
|
34
|
+
_describe "item" items
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Use openspec __complete to get available schemas
|
|
38
|
+
_openspec_complete_schemas() {
|
|
39
|
+
local -a schemas
|
|
40
|
+
while IFS=$'\\t' read -r id desc; do
|
|
41
|
+
schemas+=("$id:$desc")
|
|
42
|
+
done < <(openspec __complete schemas 2>/dev/null)
|
|
43
|
+
_describe "schema" schemas
|
|
44
44
|
}`;
|
|
45
45
|
//# sourceMappingURL=zsh-templates.js.map
|
|
@@ -2,7 +2,7 @@ import type { ProjectConfig } from './project-config.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Serialize config to YAML string with helpful comments.
|
|
4
4
|
*
|
|
5
|
-
* @param config - Partial config object (schema required,
|
|
5
|
+
* @param config - Partial config object (schema required, other fields optional)
|
|
6
6
|
* @returns YAML string ready to write to file
|
|
7
7
|
*/
|
|
8
8
|
export declare function serializeConfig(config: Partial<ProjectConfig>): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Serialize config to YAML string with helpful comments.
|
|
3
3
|
*
|
|
4
|
-
* @param config - Partial config object (schema required,
|
|
4
|
+
* @param config - Partial config object (schema required, other fields optional)
|
|
5
5
|
* @returns YAML string ready to write to file
|
|
6
6
|
*/
|
|
7
7
|
export function serializeConfig(config) {
|
|
@@ -29,6 +29,19 @@ export function serializeConfig(config) {
|
|
|
29
29
|
lines.push('# - Always include a "Non-goals" section');
|
|
30
30
|
lines.push('# tasks:');
|
|
31
31
|
lines.push('# - Break tasks into chunks of max 2 hours');
|
|
32
|
+
lines.push('');
|
|
33
|
+
// Operation guidance section with comments
|
|
34
|
+
lines.push('# Per-operation guidance (optional)');
|
|
35
|
+
lines.push('# Add advisory guidance for how apply and archive work should be conducted.');
|
|
36
|
+
lines.push('# This is separate from artifact rules above.');
|
|
37
|
+
lines.push('# Example:');
|
|
38
|
+
lines.push('# operations:');
|
|
39
|
+
lines.push('# apply:');
|
|
40
|
+
lines.push('# guidance:');
|
|
41
|
+
lines.push('# - Keep test summaries concise');
|
|
42
|
+
lines.push('# archive:');
|
|
43
|
+
lines.push('# guidance:');
|
|
44
|
+
lines.push('# - Summarize the archive outcome before finishing');
|
|
32
45
|
return lines.join('\n') + '\n';
|
|
33
46
|
}
|
|
34
47
|
//# sourceMappingURL=config-prompts.js.map
|
|
@@ -11,16 +11,25 @@ export declare const GlobalConfigSchema: z.ZodObject<{
|
|
|
11
11
|
}>>>;
|
|
12
12
|
delivery: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
13
13
|
commands: "commands";
|
|
14
|
-
skills: "skills";
|
|
15
14
|
both: "both";
|
|
15
|
+
skills: "skills";
|
|
16
16
|
}>>>;
|
|
17
17
|
workflows: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
18
|
+
defaultStore: z.ZodOptional<z.ZodString>;
|
|
19
|
+
telemetry: z.ZodOptional<z.ZodObject<{
|
|
20
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
}, z.core.$loose>>;
|
|
18
22
|
}, z.core.$loose>;
|
|
19
23
|
export type GlobalConfigType = z.infer<typeof GlobalConfigSchema>;
|
|
20
24
|
/**
|
|
21
25
|
* Default configuration values.
|
|
22
26
|
*/
|
|
23
27
|
export declare const DEFAULT_CONFIG: GlobalConfigType;
|
|
28
|
+
/**
|
|
29
|
+
* True when a dot-notation key path contains a prototype-reaching segment.
|
|
30
|
+
* Callers that bypass key validation (e.g. --allow-unknown) still must not bypass this.
|
|
31
|
+
*/
|
|
32
|
+
export declare function hasUnsafeKeySegment(path: string): boolean;
|
|
24
33
|
/**
|
|
25
34
|
* Validate a config key path for CLI set operations.
|
|
26
35
|
* Unknown top-level keys are rejected unless explicitly allowed by the caller.
|
|
@@ -20,6 +20,18 @@ export const GlobalConfigSchema = z
|
|
|
20
20
|
workflows: z
|
|
21
21
|
.array(z.string())
|
|
22
22
|
.optional(),
|
|
23
|
+
defaultStore: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Store id used as fallback root when no explicit --store, local root, or project-level store: pointer resolves'),
|
|
27
|
+
// passthrough keeps runtime-managed fields (anonymousId, noticeSeen) valid
|
|
28
|
+
// under CLI validate when users only set telemetry.enabled.
|
|
29
|
+
telemetry: z
|
|
30
|
+
.object({
|
|
31
|
+
enabled: z.boolean().optional(),
|
|
32
|
+
})
|
|
33
|
+
.passthrough()
|
|
34
|
+
.optional(),
|
|
23
35
|
})
|
|
24
36
|
.passthrough();
|
|
25
37
|
/**
|
|
@@ -30,7 +42,29 @@ export const DEFAULT_CONFIG = {
|
|
|
30
42
|
profile: 'core',
|
|
31
43
|
delivery: 'both',
|
|
32
44
|
};
|
|
33
|
-
const KNOWN_TOP_LEVEL_KEYS = new Set([
|
|
45
|
+
const KNOWN_TOP_LEVEL_KEYS = new Set([
|
|
46
|
+
...Object.keys(DEFAULT_CONFIG),
|
|
47
|
+
'workflows',
|
|
48
|
+
'defaultStore',
|
|
49
|
+
'telemetry',
|
|
50
|
+
]);
|
|
51
|
+
/** Nested keys users may set under `telemetry` via the CLI. */
|
|
52
|
+
const TELEMETRY_SETTABLE_KEYS = new Set(['enabled']);
|
|
53
|
+
/**
|
|
54
|
+
* Key segments that would reach the prototype chain instead of the config object.
|
|
55
|
+
* Never valid as configuration keys, so rejecting them costs nothing.
|
|
56
|
+
*/
|
|
57
|
+
const UNSAFE_KEY_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
58
|
+
function hasUnsafeSegment(keys) {
|
|
59
|
+
return keys.some((key) => UNSAFE_KEY_SEGMENTS.has(key));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* True when a dot-notation key path contains a prototype-reaching segment.
|
|
63
|
+
* Callers that bypass key validation (e.g. --allow-unknown) still must not bypass this.
|
|
64
|
+
*/
|
|
65
|
+
export function hasUnsafeKeySegment(path) {
|
|
66
|
+
return hasUnsafeSegment(path.split('.'));
|
|
67
|
+
}
|
|
34
68
|
/**
|
|
35
69
|
* Validate a config key path for CLI set operations.
|
|
36
70
|
* Unknown top-level keys are rejected unless explicitly allowed by the caller.
|
|
@@ -40,6 +74,10 @@ export function validateConfigKeyPath(path) {
|
|
|
40
74
|
if (rawKeys.length === 0 || rawKeys.some((key) => key.trim() === '')) {
|
|
41
75
|
return { valid: false, reason: 'Key path must not be empty' };
|
|
42
76
|
}
|
|
77
|
+
const unsafeKey = rawKeys.find((key) => UNSAFE_KEY_SEGMENTS.has(key));
|
|
78
|
+
if (unsafeKey) {
|
|
79
|
+
return { valid: false, reason: `Key segment "${unsafeKey}" is not allowed` };
|
|
80
|
+
}
|
|
43
81
|
const rootKey = rawKeys[0];
|
|
44
82
|
if (!KNOWN_TOP_LEVEL_KEYS.has(rootKey)) {
|
|
45
83
|
return { valid: false, reason: `Unknown top-level key "${rootKey}"` };
|
|
@@ -50,6 +88,18 @@ export function validateConfigKeyPath(path) {
|
|
|
50
88
|
}
|
|
51
89
|
return { valid: true };
|
|
52
90
|
}
|
|
91
|
+
if (rootKey === 'telemetry') {
|
|
92
|
+
if (rawKeys.length === 1) {
|
|
93
|
+
return { valid: false, reason: 'Set nested keys under telemetry (e.g. telemetry.enabled)' };
|
|
94
|
+
}
|
|
95
|
+
if (rawKeys.length !== 2 || !TELEMETRY_SETTABLE_KEYS.has(rawKeys[1])) {
|
|
96
|
+
return {
|
|
97
|
+
valid: false,
|
|
98
|
+
reason: `Unknown telemetry key "${rawKeys.slice(1).join('.')}" (allowed: enabled)`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return { valid: true };
|
|
102
|
+
}
|
|
53
103
|
if (rawKeys.length > 1) {
|
|
54
104
|
return { valid: false, reason: `"${rootKey}" does not support nested keys` };
|
|
55
105
|
}
|
|
@@ -64,6 +114,9 @@ export function validateConfigKeyPath(path) {
|
|
|
64
114
|
*/
|
|
65
115
|
export function getNestedValue(obj, path) {
|
|
66
116
|
const keys = path.split('.');
|
|
117
|
+
if (hasUnsafeSegment(keys)) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
67
120
|
let current = obj;
|
|
68
121
|
for (const key of keys) {
|
|
69
122
|
if (current === null || current === undefined) {
|
|
@@ -86,6 +139,14 @@ export function getNestedValue(obj, path) {
|
|
|
86
139
|
*/
|
|
87
140
|
export function setNestedValue(obj, path, value) {
|
|
88
141
|
const keys = path.split('.');
|
|
142
|
+
// Compared literally rather than through a helper, so the guard is plain to a
|
|
143
|
+
// reader and to static analysis. Checked for the whole path before anything is
|
|
144
|
+
// written, so a rejected key never leaves half-created objects behind.
|
|
145
|
+
for (const key of keys) {
|
|
146
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
89
150
|
let current = obj;
|
|
90
151
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
91
152
|
const key = keys[i];
|
|
@@ -106,6 +167,11 @@ export function setNestedValue(obj, path, value) {
|
|
|
106
167
|
*/
|
|
107
168
|
export function deleteNestedValue(obj, path) {
|
|
108
169
|
const keys = path.split('.');
|
|
170
|
+
for (const key of keys) {
|
|
171
|
+
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
109
175
|
let current = obj;
|
|
110
176
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
111
177
|
const key = keys[i];
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const OPENSPEC_DIR_NAME = "openspec";
|
|
2
|
+
export declare const OPENSPEC_SKILL_NAMES: readonly ["openspec-explore", "openspec-new-change", "openspec-continue-change", "openspec-apply-change", "openspec-update-change", "openspec-ff-change", "openspec-sync-specs", "openspec-archive-change", "openspec-bulk-archive-change", "openspec-verify-change", "openspec-onboard", "openspec-propose"];
|
|
2
3
|
export declare const OPENSPEC_MARKERS: {
|
|
3
4
|
start: string;
|
|
4
5
|
end: string;
|
|
@@ -12,7 +13,21 @@ export interface AIToolOption {
|
|
|
12
13
|
available: boolean;
|
|
13
14
|
successLabel?: string;
|
|
14
15
|
skillsDir?: string;
|
|
16
|
+
legacySkillsDirs?: string[];
|
|
17
|
+
globalSkillsDir?: string;
|
|
15
18
|
detectionPaths?: string[];
|
|
19
|
+
setupNote?: string;
|
|
16
20
|
}
|
|
17
21
|
export declare const AI_TOOLS: AIToolOption[];
|
|
22
|
+
/**
|
|
23
|
+
* Retired tool ids that still resolve, so a rebrand does not break scripted
|
|
24
|
+
* `--tools` invocations. Windsurf was rebranded to Devin Desktop on
|
|
25
|
+
* 2026-06-02 and its config directory moved from `.windsurf/` to `.devin/`;
|
|
26
|
+
* `--tools windsurf` therefore configures `devin`.
|
|
27
|
+
*/
|
|
28
|
+
export declare const TOOL_ID_ALIASES: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Resolves a tool id through TOOL_ID_ALIASES, leaving current ids untouched.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveToolIdAlias(toolId: string): string;
|
|
18
33
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/core/config.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
export const OPENSPEC_DIR_NAME = 'openspec';
|
|
2
|
+
export const OPENSPEC_SKILL_NAMES = [
|
|
3
|
+
'openspec-explore',
|
|
4
|
+
'openspec-new-change',
|
|
5
|
+
'openspec-continue-change',
|
|
6
|
+
'openspec-apply-change',
|
|
7
|
+
'openspec-update-change',
|
|
8
|
+
'openspec-ff-change',
|
|
9
|
+
'openspec-sync-specs',
|
|
10
|
+
'openspec-archive-change',
|
|
11
|
+
'openspec-bulk-archive-change',
|
|
12
|
+
'openspec-verify-change',
|
|
13
|
+
'openspec-onboard',
|
|
14
|
+
'openspec-propose',
|
|
15
|
+
];
|
|
2
16
|
export const OPENSPEC_MARKERS = {
|
|
3
17
|
start: '<!-- OPENSPEC:START -->',
|
|
4
18
|
end: '<!-- OPENSPEC:END -->'
|
|
@@ -10,7 +24,9 @@ export const AI_TOOLS = [
|
|
|
10
24
|
{ name: 'Bob Shell', value: 'bob', available: true, successLabel: 'Bob Shell', skillsDir: '.bob' },
|
|
11
25
|
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code', skillsDir: '.claude' },
|
|
12
26
|
{ name: 'Cline', value: 'cline', available: true, successLabel: 'Cline', skillsDir: '.cline' },
|
|
13
|
-
{ name: '
|
|
27
|
+
{ name: 'CodeArts', value: 'codeartsagent', available: true, successLabel: 'CodeArts', skillsDir: '.codeartsdoer' },
|
|
28
|
+
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex', skillsDir: '.agents', legacySkillsDirs: ['.codex'], detectionPaths: ['.agents/skills', '.codex/skills'] },
|
|
29
|
+
{ name: 'Devin Desktop (formerly Windsurf)', value: 'devin', available: true, successLabel: 'Devin Desktop', skillsDir: '.devin', detectionPaths: ['.devin', '.windsurf'] },
|
|
14
30
|
{ name: 'ForgeCode', value: 'forgecode', available: true, successLabel: 'ForgeCode', skillsDir: '.forge' },
|
|
15
31
|
{ name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code', skillsDir: '.codebuddy' },
|
|
16
32
|
{ name: 'Continue', value: 'continue', available: true, successLabel: 'Continue (VS Code / JetBrains / Cli)', skillsDir: '.continue' },
|
|
@@ -20,20 +36,45 @@ export const AI_TOOLS = [
|
|
|
20
36
|
{ name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid', skillsDir: '.factory' },
|
|
21
37
|
{ name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI', skillsDir: '.gemini' },
|
|
22
38
|
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot', skillsDir: '.github', detectionPaths: ['.github/copilot-instructions.md', '.github/instructions', '.github/workflows/copilot-setup-steps.yml', '.github/prompts', '.github/agents', '.github/skills', '.github/.mcp.json'] },
|
|
39
|
+
{ name: 'Hermes Agent', value: 'hermes', available: true, successLabel: 'Hermes Agent', skillsDir: '.hermes', detectionPaths: ['.hermes', 'HERMES.md', '.hermes.md'], setupNote: "Hermes only loads skills from ~/.hermes/skills by default. Add this project's .hermes/skills directory to skills.external_dirs in ~/.hermes/config.yaml so Hermes picks up the generated OpenSpec skills." },
|
|
23
40
|
{ name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow', skillsDir: '.iflow' },
|
|
24
41
|
{ name: 'Junie', value: 'junie', available: true, successLabel: 'Junie', skillsDir: '.junie' },
|
|
25
42
|
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code', skillsDir: '.kilocode' },
|
|
26
|
-
{ name: 'Kimi
|
|
43
|
+
{ name: 'Kimi Code', value: 'kimi', available: true, successLabel: 'Kimi Code', skillsDir: '.kimi-code', detectionPaths: ['.kimi-code', '.kimi'] },
|
|
27
44
|
{ name: 'Kiro', value: 'kiro', available: true, successLabel: 'Kiro', skillsDir: '.kiro' },
|
|
28
45
|
{ name: 'Lingma', value: 'lingma', available: true, successLabel: 'Lingma', skillsDir: '.lingma' },
|
|
46
|
+
{ name: 'MiniMax Code', value: 'minimax-code', available: true, successLabel: 'MiniMax Code', globalSkillsDir: '.minimax' },
|
|
29
47
|
{ name: 'Mistral Vibe', value: 'vibe', available: true, successLabel: 'Mistral Vibe', skillsDir: '.vibe' },
|
|
48
|
+
{ name: 'Oh My Pi', value: 'oh-my-pi', available: true, successLabel: 'Oh My Pi', skillsDir: '.omp' },
|
|
30
49
|
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode', skillsDir: '.opencode' },
|
|
31
50
|
{ name: 'Pi', value: 'pi', available: true, successLabel: 'Pi', skillsDir: '.pi' },
|
|
32
51
|
{ name: 'Qoder', value: 'qoder', available: true, successLabel: 'Qoder', skillsDir: '.qoder' },
|
|
33
52
|
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code', skillsDir: '.qwen' },
|
|
34
|
-
{ name: '
|
|
53
|
+
{ name: 'Rovo Dev CLI', value: 'rovodev', available: true, successLabel: 'Rovo Dev CLI', skillsDir: '.rovodev', detectionPaths: ['.rovodev/skills', '.rovodev'] },
|
|
54
|
+
{ name: 'Zoo Code', value: 'roocode', available: true, successLabel: 'Zoo Code', skillsDir: '.roo' },
|
|
35
55
|
{ name: 'Trae', value: 'trae', available: true, successLabel: 'Trae', skillsDir: '.trae' },
|
|
36
|
-
{ name: '
|
|
37
|
-
|
|
56
|
+
{ name: 'ZCode', value: 'zcode', available: true, successLabel: 'ZCode', skillsDir: '.zcode' },
|
|
57
|
+
// Vendor-neutral target for assistants that read the shared `.agents` root.
|
|
58
|
+
// Detection keys off `.agents/skills` rather than the bare root: frameworks use
|
|
59
|
+
// `.agents/` for more than skills, so the root alone says nothing about skills.
|
|
60
|
+
// A project that does keep skills there is a project this target fits, the same
|
|
61
|
+
// way `.claude/` selects Claude Code — the signal is the user's setup, not
|
|
62
|
+
// OpenSpec's own files.
|
|
63
|
+
{ name: 'Shared .agents skills', value: 'agents', available: true, successLabel: 'shared .agents skills', skillsDir: '.agents', detectionPaths: ['.agents/skills'] }
|
|
38
64
|
];
|
|
65
|
+
/**
|
|
66
|
+
* Retired tool ids that still resolve, so a rebrand does not break scripted
|
|
67
|
+
* `--tools` invocations. Windsurf was rebranded to Devin Desktop on
|
|
68
|
+
* 2026-06-02 and its config directory moved from `.windsurf/` to `.devin/`;
|
|
69
|
+
* `--tools windsurf` therefore configures `devin`.
|
|
70
|
+
*/
|
|
71
|
+
export const TOOL_ID_ALIASES = {
|
|
72
|
+
windsurf: 'devin',
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Resolves a tool id through TOOL_ID_ALIASES, leaving current ids untouched.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveToolIdAlias(toolId) {
|
|
78
|
+
return TOOL_ID_ALIASES[toolId] ?? toolId;
|
|
79
|
+
}
|
|
39
80
|
//# sourceMappingURL=config.js.map
|
package/dist/core/file-state.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as nodeFs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { FileSystemUtils } from '../utils/file-system.js';
|
|
4
5
|
import { StoreError } from './store/errors.js';
|
|
5
6
|
const fs = nodeFs.promises;
|
|
@@ -19,9 +20,15 @@ export function makeLockErrorFactory(data) {
|
|
|
19
20
|
});
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
|
-
const STALE_LOCK_THRESHOLD_MS = 30_000;
|
|
23
23
|
const LOCK_DEADLINE_MS = 5000;
|
|
24
24
|
const LOCK_POLL_MS = 25;
|
|
25
|
+
const PRIVATE_FILE_MODE = 0o600;
|
|
26
|
+
const lockOwnership = new WeakMap();
|
|
27
|
+
function isUnsupportedSyncError(error) {
|
|
28
|
+
return (isNodeErrorCode(error, 'EINVAL') ||
|
|
29
|
+
isNodeErrorCode(error, 'ENOTSUP') ||
|
|
30
|
+
isNodeErrorCode(error, 'ENOSYS'));
|
|
31
|
+
}
|
|
25
32
|
export function isNodeErrorCode(error, code) {
|
|
26
33
|
return (typeof error === 'object' &&
|
|
27
34
|
error !== null &&
|
|
@@ -56,7 +63,10 @@ export async function writeFileAtomically(filePath, content) {
|
|
|
56
63
|
await FileSystemUtils.createDirectory(dirPath);
|
|
57
64
|
const tempPath = path.join(dirPath, `.${path.basename(filePath)}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`);
|
|
58
65
|
try {
|
|
59
|
-
await fs.writeFile(tempPath, content,
|
|
66
|
+
await fs.writeFile(tempPath, content, {
|
|
67
|
+
encoding: 'utf-8',
|
|
68
|
+
mode: PRIVATE_FILE_MODE,
|
|
69
|
+
});
|
|
60
70
|
await fs.rename(tempPath, filePath);
|
|
61
71
|
}
|
|
62
72
|
catch (error) {
|
|
@@ -74,39 +84,61 @@ export async function acquireFileLock(options) {
|
|
|
74
84
|
const deadline = Date.now() + LOCK_DEADLINE_MS;
|
|
75
85
|
while (true) {
|
|
76
86
|
try {
|
|
77
|
-
|
|
87
|
+
const lock = await fs.open(lockPath, 'wx', PRIVATE_FILE_MODE);
|
|
88
|
+
const ownershipToken = `${process.pid}:${randomUUID()}`;
|
|
89
|
+
try {
|
|
90
|
+
await lock.writeFile(ownershipToken, 'utf-8');
|
|
91
|
+
try {
|
|
92
|
+
await lock.sync();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// Some FUSE and network filesystems support exclusive lock files but
|
|
96
|
+
// explicitly do not implement fsync. The token is still visible to
|
|
97
|
+
// cooperating processes, so do not make those projects unusable.
|
|
98
|
+
if (!isUnsupportedSyncError(error)) {
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
await lock.close().catch(() => undefined);
|
|
105
|
+
await fs.rm(lockPath, { force: true }).catch(() => undefined);
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
lockOwnership.set(lock, ownershipToken);
|
|
109
|
+
return lock;
|
|
78
110
|
}
|
|
79
111
|
catch (error) {
|
|
80
112
|
if (!isNodeErrorCode(error, 'EEXIST')) {
|
|
81
113
|
// A permission or filesystem problem, not contention - say so.
|
|
82
114
|
throw errorFor('create-failed', { lockPath, cause: error });
|
|
83
115
|
}
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (Date.now() - lockStat.mtimeMs > STALE_LOCK_THRESHOLD_MS) {
|
|
90
|
-
await fs.rm(lockPath, { force: true });
|
|
91
|
-
staleStolen = true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
// The holder released between open and stat - retry, but stay
|
|
96
|
-
// bounded: a persistently failing stat (EPERM, delete-pending)
|
|
97
|
-
// must hit the deadline instead of spinning forever.
|
|
98
|
-
}
|
|
99
|
-
if (!staleStolen) {
|
|
100
|
-
if (Date.now() >= deadline) {
|
|
101
|
-
throw errorFor('timeout', { lockPath });
|
|
102
|
-
}
|
|
103
|
-
await sleep(LOCK_POLL_MS);
|
|
116
|
+
// Never steal by age: unlinking a supposedly stale path can race with
|
|
117
|
+
// its replacement and erase a live owner's lock. The timeout diagnostic
|
|
118
|
+
// gives the user an explicit recovery path for genuinely orphaned locks.
|
|
119
|
+
if (Date.now() >= deadline) {
|
|
120
|
+
throw errorFor('timeout', { lockPath });
|
|
104
121
|
}
|
|
122
|
+
await sleep(LOCK_POLL_MS);
|
|
105
123
|
}
|
|
106
124
|
}
|
|
107
125
|
}
|
|
108
126
|
export async function releaseFileLock(lock, lockPath) {
|
|
127
|
+
const ownershipToken = lockOwnership.get(lock);
|
|
128
|
+
lockOwnership.delete(lock);
|
|
109
129
|
await lock.close().catch(() => undefined);
|
|
110
|
-
|
|
130
|
+
if (ownershipToken === undefined) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const currentToken = await fs.readFile(lockPath, 'utf-8');
|
|
135
|
+
if (currentToken === ownershipToken) {
|
|
136
|
+
await fs.rm(lockPath, { force: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// The lock was already removed or replaced with an unreadable path.
|
|
141
|
+
// In either case, this owner must not remove anything else.
|
|
142
|
+
}
|
|
111
143
|
}
|
|
112
144
|
//# sourceMappingURL=file-state.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot Cloud Agent Support
|
|
3
|
+
*
|
|
4
|
+
* Generates copilot-setup-steps.yml and .github/agents/openspec.agent.md
|
|
5
|
+
* when the github-copilot tool is selected during init/update.
|
|
6
|
+
* These files enable the GitHub Copilot coding agent (cloud) to use the
|
|
7
|
+
* OpenSpec CLI in its ephemeral dev environment.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Check if a tool list includes github-copilot.
|
|
11
|
+
*/
|
|
12
|
+
export declare function includesGitHubCopilot(toolIds: string[]): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Generate the copilot-setup-steps.yml workflow file content.
|
|
15
|
+
* This workflow pre-installs the OpenSpec CLI in the Copilot coding agent's
|
|
16
|
+
* ephemeral GitHub Actions environment.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateCopilotSetupSteps(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate the .github/agents/openspec.agent.md custom agent file content.
|
|
21
|
+
* This tells the GitHub Copilot coding agent how to use the OpenSpec CLI.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateCopilotAgentFile(): string;
|
|
24
|
+
/**
|
|
25
|
+
* File paths (relative to project root) for the generated files.
|
|
26
|
+
*/
|
|
27
|
+
export declare const COPILOT_CLOUD_FILES: {
|
|
28
|
+
readonly setupSteps: string;
|
|
29
|
+
readonly agent: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Reconcile Copilot cloud agent files in the project directory.
|
|
33
|
+
* Creates missing files and refreshes recognized legacy generated files while
|
|
34
|
+
* preserving current generated content and user customizations.
|
|
35
|
+
*
|
|
36
|
+
* @returns Object indicating which files were written.
|
|
37
|
+
*/
|
|
38
|
+
export declare function writeCopilotCloudFiles(projectPath: string): Promise<{
|
|
39
|
+
setupStepsWritten: boolean;
|
|
40
|
+
agentWritten: boolean;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Remove copilot cloud agent files from the project directory.
|
|
44
|
+
* Used when github-copilot is deselected.
|
|
45
|
+
*
|
|
46
|
+
* @returns Number of files removed.
|
|
47
|
+
*/
|
|
48
|
+
export declare function removeCopilotCloudFiles(projectPath: string): Promise<number>;
|
|
49
|
+
/**
|
|
50
|
+
* Read the persisted opt-in for Copilot cloud-file generation.
|
|
51
|
+
*
|
|
52
|
+
* Tri-state: `true` (opted in), `false` (explicitly opted out), or `undefined`
|
|
53
|
+
* (never decided). A malformed value is treated as undecided rather than an
|
|
54
|
+
* error, matching how {@link readProjectConfig} degrades on bad fields.
|
|
55
|
+
*/
|
|
56
|
+
export declare function readCopilotCloudOptIn(projectPath: string): boolean | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* True when a managed Copilot cloud file (the current generation or a
|
|
59
|
+
* recognized legacy one) already exists. Projects created before the opt-in
|
|
60
|
+
* prompt existed are treated as implicitly opted in, so `openspec update`
|
|
61
|
+
* keeps their files current instead of silently abandoning them.
|
|
62
|
+
*/
|
|
63
|
+
export declare function hasExistingManagedCloudFiles(projectPath: string): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* Effective decision on whether to generate/refresh Copilot cloud files.
|
|
66
|
+
* An explicit opt-in or opt-out always wins; when undecided, fall back to
|
|
67
|
+
* whether managed files already exist (the migration path above).
|
|
68
|
+
*/
|
|
69
|
+
export declare function isCopilotCloudEnabled(projectPath: string): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* Persist the Copilot cloud opt-in into openspec/config.yaml.
|
|
72
|
+
*
|
|
73
|
+
* Uses the YAML document model rather than a re-serialize so the user's
|
|
74
|
+
* existing comments, ordering, and formatting survive untouched — the config
|
|
75
|
+
* file is hand-authored and heavily commented, so a lossy round-trip would be
|
|
76
|
+
* its own source of toil. No-op when no config file exists yet (init creates it
|
|
77
|
+
* before this is called); the caller treats persistence failures as non-fatal.
|
|
78
|
+
*/
|
|
79
|
+
export declare function persistCopilotCloudOptIn(projectPath: string, value: boolean): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Return the managed cloud-file paths (relative to the project root) that
|
|
82
|
+
* currently hold user-owned, non-managed content — i.e. files OpenSpec will
|
|
83
|
+
* deliberately leave untouched. Used to tell an opted-in user that we preserved
|
|
84
|
+
* their existing file rather than silently doing nothing, which is the honest
|
|
85
|
+
* answer to "will this affect my existing Copilot cloud setup?".
|
|
86
|
+
*/
|
|
87
|
+
export declare function findUnmanagedCloudFiles(projectPath: string): Promise<string[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Return the managed cloud-file paths (relative to the project root) that
|
|
90
|
+
* currently exist and hold OpenSpec-generated content. Callers report this
|
|
91
|
+
* rather than the intended paths, so output never claims a file that a write
|
|
92
|
+
* skipped (user already owns it) or that reconciliation removed.
|
|
93
|
+
*/
|
|
94
|
+
export declare function listManagedCloudFiles(projectPath: string): Promise<string[]>;
|
|
95
|
+
//# sourceMappingURL=cloud-agent.d.ts.map
|