@codewalla_india/openspec 1.3.1 → 1.3.3
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 +214 -133
- 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 +35 -0
- package/dist/comprehension-quiz/providers/anthropic.js +69 -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 +98 -0
- package/dist/comprehension-quiz/providers/interface.js +150 -0
- package/dist/comprehension-quiz/providers/local.d.ts +35 -0
- package/dist/comprehension-quiz/providers/local.js +69 -0
- package/dist/comprehension-quiz/providers/ollama.d.ts +35 -0
- package/dist/comprehension-quiz/providers/ollama.js +69 -0
- package/dist/comprehension-quiz/providers/openai.d.ts +35 -0
- package/dist/comprehension-quiz/providers/openai.js +72 -0
- package/dist/comprehension-quiz/question-generator.d.ts +117 -0
- package/dist/comprehension-quiz/question-generator.js +203 -0
- package/dist/comprehension-quiz/quiz-executor.d.ts +75 -0
- package/dist/comprehension-quiz/quiz-executor.js +181 -0
- package/dist/comprehension-quiz/types.d.ts +137 -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 +193 -71
- 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
|
@@ -0,0 +1,497 @@
|
|
|
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
|
+
import path from 'path';
|
|
10
|
+
import { promises as fs } from 'fs';
|
|
11
|
+
import { Document, YAMLMap, parseDocument, isMap } from 'yaml';
|
|
12
|
+
import { FileSystemUtils } from '../../utils/file-system.js';
|
|
13
|
+
import { readProjectConfig, resolveConfigFilePath } from '../project-config.js';
|
|
14
|
+
const COPILOT_TOOL_ID = 'github-copilot';
|
|
15
|
+
const OPENSPEC_MANAGED_MARKER = 'Generated by OpenSpec for GitHub Copilot coding agent support.';
|
|
16
|
+
/**
|
|
17
|
+
* Check if a tool list includes github-copilot.
|
|
18
|
+
*/
|
|
19
|
+
export function includesGitHubCopilot(toolIds) {
|
|
20
|
+
return toolIds.includes(COPILOT_TOOL_ID);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate the copilot-setup-steps.yml workflow file content.
|
|
24
|
+
* This workflow pre-installs the OpenSpec CLI in the Copilot coding agent's
|
|
25
|
+
* ephemeral GitHub Actions environment.
|
|
26
|
+
*/
|
|
27
|
+
export function generateCopilotSetupSteps() {
|
|
28
|
+
return `# ${OPENSPEC_MANAGED_MARKER}
|
|
29
|
+
|
|
30
|
+
${generateCopilotSetupStepsBody()}`;
|
|
31
|
+
}
|
|
32
|
+
function generateCopilotSetupStepsBody() {
|
|
33
|
+
return `name: "Copilot Setup Steps"
|
|
34
|
+
|
|
35
|
+
# Runs automatically when changed (for validation) and can be triggered manually.
|
|
36
|
+
on:
|
|
37
|
+
workflow_dispatch:
|
|
38
|
+
push:
|
|
39
|
+
paths:
|
|
40
|
+
- .github/workflows/copilot-setup-steps.yml
|
|
41
|
+
pull_request:
|
|
42
|
+
paths:
|
|
43
|
+
- .github/workflows/copilot-setup-steps.yml
|
|
44
|
+
|
|
45
|
+
jobs:
|
|
46
|
+
# The job MUST be called \`copilot-setup-steps\` for Copilot coding agent to pick it up.
|
|
47
|
+
copilot-setup-steps:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
timeout-minutes: 10
|
|
50
|
+
|
|
51
|
+
permissions:
|
|
52
|
+
contents: read
|
|
53
|
+
|
|
54
|
+
steps:
|
|
55
|
+
- name: Checkout code
|
|
56
|
+
uses: actions/checkout@v4
|
|
57
|
+
|
|
58
|
+
- name: Install OpenSpec CLI
|
|
59
|
+
run: npm install -g @codewalla_india/openspec
|
|
60
|
+
|
|
61
|
+
- name: Verify OpenSpec CLI
|
|
62
|
+
run: openspec --version
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Generate the .github/agents/openspec.agent.md custom agent file content.
|
|
67
|
+
* This tells the GitHub Copilot coding agent how to use the OpenSpec CLI.
|
|
68
|
+
*/
|
|
69
|
+
export function generateCopilotAgentFile() {
|
|
70
|
+
return generateCopilotAgentFileBody(true);
|
|
71
|
+
}
|
|
72
|
+
function generateCopilotAgentFileBody(includeManagedMarker = false) {
|
|
73
|
+
const managedMarker = includeManagedMarker
|
|
74
|
+
? `<!-- ${OPENSPEC_MANAGED_MARKER} -->\n\n`
|
|
75
|
+
: '';
|
|
76
|
+
return `---
|
|
77
|
+
name: OpenSpec
|
|
78
|
+
description: "Manages OpenSpec changes, specs, and workflows using the OpenSpec CLI. Use this agent for proposing changes, exploring ideas, validating artifacts, checking status, and archiving completed work."
|
|
79
|
+
tools:
|
|
80
|
+
- "execute"
|
|
81
|
+
- "read"
|
|
82
|
+
- "search"
|
|
83
|
+
- "edit"
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
${managedMarker}# OpenSpec Agent
|
|
87
|
+
|
|
88
|
+
You are a specialized agent for managing OpenSpec workflows. Before using the \`openspec\` CLI, run \`openspec --version\`. If it is unavailable, install it with \`npm install -g @codewalla_india/openspec\`.
|
|
89
|
+
|
|
90
|
+
## What is OpenSpec?
|
|
91
|
+
|
|
92
|
+
OpenSpec is a structured change management system for codebases. It organizes work into **changes** with planning artifacts (proposals, specs, designs, tasks) that guide implementation.
|
|
93
|
+
|
|
94
|
+
## Available Commands
|
|
95
|
+
|
|
96
|
+
### Agent-Compatible CLI Commands (prefer \`--json\` for structured output)
|
|
97
|
+
|
|
98
|
+
| Command | Purpose |
|
|
99
|
+
|---------|---------|
|
|
100
|
+
| \`openspec list [--json]\` | List all changes and specs |
|
|
101
|
+
| \`openspec show <item> [--json]\` | View a specific change or spec |
|
|
102
|
+
| \`openspec validate [--all] [--json]\` | Validate changes and specs for issues |
|
|
103
|
+
| \`openspec status [--change <name>] [--json]\` | Show artifact progress for a change |
|
|
104
|
+
| \`openspec instructions [artifact] [--change <name>] [--json]\` | Get next-step instructions for a change |
|
|
105
|
+
| \`openspec templates [--json]\` | List available templates |
|
|
106
|
+
| \`openspec schemas [--json]\` | List available workflow schemas |
|
|
107
|
+
| \`openspec archive <change> --json [--yes]\` | Archive a completed change; use \`--yes\` only after confirming all tasks are complete |
|
|
108
|
+
|
|
109
|
+
### Interactive CLI Commands (use when prompted by the user)
|
|
110
|
+
|
|
111
|
+
| Command | Purpose |
|
|
112
|
+
|---------|---------|
|
|
113
|
+
| \`openspec init\` | Initialize OpenSpec in the project |
|
|
114
|
+
| \`openspec update\` | Update OpenSpec configuration and artifacts |
|
|
115
|
+
| \`openspec view\` | Interactive dashboard |
|
|
116
|
+
| \`openspec config\` | View or modify settings |
|
|
117
|
+
|
|
118
|
+
## Workflow
|
|
119
|
+
|
|
120
|
+
When asked to work with OpenSpec, follow this pattern:
|
|
121
|
+
|
|
122
|
+
1. **Find the change**: Run \`openspec list --json\` to see active changes.
|
|
123
|
+
2. **Check progress**: Run \`openspec status --change <name> --json\` for the selected change.
|
|
124
|
+
3. **Follow instructions**: Run \`openspec instructions [artifact] --change <name> --json\` for the next artifact.
|
|
125
|
+
4. **Validate before completing**: Run \`openspec validate <name> --json\`.
|
|
126
|
+
|
|
127
|
+
## Creating New Changes
|
|
128
|
+
|
|
129
|
+
When the user wants to propose a new change:
|
|
130
|
+
|
|
131
|
+
1. Run \`openspec new change <name>\`.
|
|
132
|
+
2. Run \`openspec status --change <name> --json\` to see the artifact sequence.
|
|
133
|
+
3. Use \`openspec instructions [artifact] --change <name> --json\` before creating each artifact.
|
|
134
|
+
4. Run \`openspec validate <name> --json\` when the artifacts are complete.
|
|
135
|
+
|
|
136
|
+
## Key Directories
|
|
137
|
+
|
|
138
|
+
- \`openspec/\` — Root OpenSpec directory
|
|
139
|
+
- \`openspec/changes/\` — Active changes with their artifacts
|
|
140
|
+
- \`openspec/config.yaml\` — Project configuration
|
|
141
|
+
|
|
142
|
+
## Best Practices
|
|
143
|
+
|
|
144
|
+
- Always use \`--json\` flag when you need to parse output programmatically
|
|
145
|
+
- Run \`openspec validate\` after creating or modifying artifacts
|
|
146
|
+
- Check \`openspec status\` before starting work to understand the current state
|
|
147
|
+
- When archiving, ensure all tasks are completed and validated first
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
function generatePreviousCopilotAgentFileBody(includeManagedMarker = false) {
|
|
151
|
+
let content = generateCopilotAgentFileBody();
|
|
152
|
+
content = replaceRequired(content, 'You are a specialized agent for managing OpenSpec workflows. Before using the `openspec` CLI, run `openspec --version`. If it is unavailable, install it with `npm install -g @codewalla_india/openspec`.', 'You are a specialized agent for managing OpenSpec workflows. You have access to the `openspec` CLI through shell commands, pre-installed in the development environment via `copilot-setup-steps.yml`.', 'previous CLI access sentence');
|
|
153
|
+
content = replaceRequired(content, '| `openspec archive <change> --json [--yes]` | Archive a completed change; use `--yes` only after confirming all tasks are complete |', '| `openspec archive <change>` | Archive a completed change |', 'previous archive command row');
|
|
154
|
+
if (!includeManagedMarker) {
|
|
155
|
+
return content;
|
|
156
|
+
}
|
|
157
|
+
return replaceRequired(content, '\n# OpenSpec Agent', `\n<!-- ${OPENSPEC_MANAGED_MARKER} -->\n\n# OpenSpec Agent`, 'previous agent heading');
|
|
158
|
+
}
|
|
159
|
+
function generateLegacyCopilotAgentFileBody() {
|
|
160
|
+
let content = generatePreviousCopilotAgentFileBody();
|
|
161
|
+
content = replaceRequired(content, `## Workflow
|
|
162
|
+
|
|
163
|
+
When asked to work with OpenSpec, follow this pattern:
|
|
164
|
+
|
|
165
|
+
1. **Find the change**: Run \`openspec list --json\` to see active changes.
|
|
166
|
+
2. **Check progress**: Run \`openspec status --change <name> --json\` for the selected change.
|
|
167
|
+
3. **Follow instructions**: Run \`openspec instructions [artifact] --change <name> --json\` for the next artifact.
|
|
168
|
+
4. **Validate before completing**: Run \`openspec validate <name> --json\`.
|
|
169
|
+
|
|
170
|
+
## Creating New Changes
|
|
171
|
+
|
|
172
|
+
When the user wants to propose a new change:
|
|
173
|
+
|
|
174
|
+
1. Run \`openspec new change <name>\`.
|
|
175
|
+
2. Run \`openspec status --change <name> --json\` to see the artifact sequence.
|
|
176
|
+
3. Use \`openspec instructions [artifact] --change <name> --json\` before creating each artifact.
|
|
177
|
+
4. Run \`openspec validate <name> --json\` when the artifacts are complete.`, `## Workflow
|
|
178
|
+
|
|
179
|
+
When asked to work with OpenSpec, follow this pattern:
|
|
180
|
+
|
|
181
|
+
1. **Check current state**: Run \`openspec status --json\` to understand what changes exist and their progress.
|
|
182
|
+
2. **Follow instructions**: Run \`openspec instructions --json\` to get context-aware next steps.
|
|
183
|
+
3. **Validate before completing**: Run \`openspec validate --all --json\` to ensure artifacts are correct.
|
|
184
|
+
|
|
185
|
+
## Creating New Changes
|
|
186
|
+
|
|
187
|
+
When the user wants to propose a new change:
|
|
188
|
+
|
|
189
|
+
1. Create the change directory under \`openspec/changes/<change-name>/\`
|
|
190
|
+
2. Generate the required planning artifacts based on the project's configured workflow schema
|
|
191
|
+
3. Run \`openspec validate --json\` to verify the artifacts are well-formed`, 'legacy workflow guidance');
|
|
192
|
+
content = replaceRequired(content, `tools:
|
|
193
|
+
- "execute"
|
|
194
|
+
- "read"
|
|
195
|
+
- "search"
|
|
196
|
+
- "edit"`, `tools:
|
|
197
|
+
- "terminal"`, 'legacy tool alias');
|
|
198
|
+
content = replaceRequired(content, 'You are a specialized agent for managing OpenSpec workflows. You have access to the `openspec` CLI through shell commands, pre-installed in the development environment via `copilot-setup-steps.yml`.', 'You are a specialized agent for managing OpenSpec workflows. You have access to the `openspec` CLI which is pre-installed in the development environment via `copilot-setup-steps.yml`.', 'legacy CLI access sentence');
|
|
199
|
+
content = replaceRequired(content, '| `openspec status [--change <name>] [--json]` | Show artifact progress for a change |', '| `openspec status [--json]` | Show artifact progress for active changes |', 'legacy status command row');
|
|
200
|
+
content = replaceRequired(content, '| `openspec instructions [artifact] [--change <name>] [--json]` | Get next-step instructions for a change |', '| `openspec instructions [--json]` | Get next-step instructions for a change |', 'legacy instructions command row');
|
|
201
|
+
return replaceRequired(content, '- `openspec/config.yaml` — Project configuration', `- \`openspec/config.yaml\` — Project configuration
|
|
202
|
+
- \`openspec/explorations/\` — Exploration documents`, 'legacy exploration directory');
|
|
203
|
+
}
|
|
204
|
+
function replaceRequired(content, searchValue, replaceValue, label) {
|
|
205
|
+
if (!content.includes(searchValue)) {
|
|
206
|
+
throw new Error(`Cannot build Copilot cloud file content: missing ${label}`);
|
|
207
|
+
}
|
|
208
|
+
return content.replace(searchValue, replaceValue);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* File paths (relative to project root) for the generated files.
|
|
212
|
+
*/
|
|
213
|
+
export const COPILOT_CLOUD_FILES = {
|
|
214
|
+
setupSteps: path.join('.github', 'workflows', 'copilot-setup-steps.yml'),
|
|
215
|
+
agent: path.join('.github', 'agents', 'openspec.agent.md'),
|
|
216
|
+
};
|
|
217
|
+
const COPILOT_AGENT_ALTERNATE_FILE = path.join('.github', 'agents', 'openspec.md');
|
|
218
|
+
const COPILOT_CLOUD_FILE_CONTENTS = {
|
|
219
|
+
[COPILOT_CLOUD_FILES.setupSteps]: generateCopilotSetupSteps(),
|
|
220
|
+
[COPILOT_CLOUD_FILES.agent]: generateCopilotAgentFile(),
|
|
221
|
+
};
|
|
222
|
+
function getLegacyCopilotCloudFileContents(relPath) {
|
|
223
|
+
if (relPath === COPILOT_CLOUD_FILES.setupSteps) {
|
|
224
|
+
return [generateCopilotSetupStepsBody()];
|
|
225
|
+
}
|
|
226
|
+
return [
|
|
227
|
+
generateCopilotAgentFileBody(),
|
|
228
|
+
generatePreviousCopilotAgentFileBody(),
|
|
229
|
+
generatePreviousCopilotAgentFileBody(true),
|
|
230
|
+
generateLegacyCopilotAgentFileBody(),
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
function normalizeLineEndings(content) {
|
|
234
|
+
return content.replace(/\r\n/g, '\n');
|
|
235
|
+
}
|
|
236
|
+
function isCurrentCopilotCloudFile(relPath, content) {
|
|
237
|
+
return normalizeLineEndings(content) === COPILOT_CLOUD_FILE_CONTENTS[relPath];
|
|
238
|
+
}
|
|
239
|
+
function isLegacyCopilotCloudFile(relPath, content) {
|
|
240
|
+
return getLegacyCopilotCloudFileContents(relPath).includes(normalizeLineEndings(content));
|
|
241
|
+
}
|
|
242
|
+
function isManagedCopilotCloudFile(relPath, content) {
|
|
243
|
+
return isCurrentCopilotCloudFile(relPath, content) || isLegacyCopilotCloudFile(relPath, content);
|
|
244
|
+
}
|
|
245
|
+
async function reconcileCopilotCloudFile(fullPath, relPath) {
|
|
246
|
+
const currentContent = COPILOT_CLOUD_FILE_CONTENTS[relPath];
|
|
247
|
+
if (!(await FileSystemUtils.fileExists(fullPath))) {
|
|
248
|
+
await FileSystemUtils.writeFile(fullPath, currentContent);
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
const existingContent = await FileSystemUtils.readFile(fullPath);
|
|
252
|
+
if (isCurrentCopilotCloudFile(relPath, existingContent)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
if (!isLegacyCopilotCloudFile(relPath, existingContent)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
await FileSystemUtils.writeFile(fullPath, currentContent);
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
async function assertCreatableFilePath(filePath) {
|
|
262
|
+
let candidate = path.dirname(filePath);
|
|
263
|
+
while (true) {
|
|
264
|
+
try {
|
|
265
|
+
const stats = await fs.stat(candidate);
|
|
266
|
+
if (!stats.isDirectory()) {
|
|
267
|
+
throw new Error(`Parent path is not a directory: ${candidate}`);
|
|
268
|
+
}
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (error.code !== 'ENOENT') {
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const parent = path.dirname(candidate);
|
|
277
|
+
if (parent === candidate) {
|
|
278
|
+
throw new Error(`Cannot resolve a directory ancestor for: ${filePath}`);
|
|
279
|
+
}
|
|
280
|
+
candidate = parent;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async function assertMissingOrRegularFile(filePath) {
|
|
284
|
+
try {
|
|
285
|
+
const stats = await fs.stat(filePath);
|
|
286
|
+
if (!stats.isFile()) {
|
|
287
|
+
throw new Error(`Managed Copilot path is not a regular file: ${filePath}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (error.code !== 'ENOENT') {
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function classifyCopilotAgentReconciliation(agentPath, alternateAgentPath) {
|
|
297
|
+
if (!(await FileSystemUtils.fileExists(alternateAgentPath))) {
|
|
298
|
+
return 'reconcile';
|
|
299
|
+
}
|
|
300
|
+
if (!(await FileSystemUtils.fileExists(agentPath))) {
|
|
301
|
+
return 'skip';
|
|
302
|
+
}
|
|
303
|
+
const existingContent = await FileSystemUtils.readFile(agentPath);
|
|
304
|
+
if (isManagedCopilotCloudFile(COPILOT_CLOUD_FILES.agent, existingContent)) {
|
|
305
|
+
return 'remove-managed';
|
|
306
|
+
}
|
|
307
|
+
throw new Error(`Conflicting Copilot agent profiles: preserve either ${COPILOT_AGENT_ALTERNATE_FILE} or ${COPILOT_CLOUD_FILES.agent}`);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Reconcile Copilot cloud agent files in the project directory.
|
|
311
|
+
* Creates missing files and refreshes recognized legacy generated files while
|
|
312
|
+
* preserving current generated content and user customizations.
|
|
313
|
+
*
|
|
314
|
+
* @returns Object indicating which files were written.
|
|
315
|
+
*/
|
|
316
|
+
export async function writeCopilotCloudFiles(projectPath) {
|
|
317
|
+
const setupStepsPath = FileSystemUtils.resolveProjectArtifactPath(projectPath, COPILOT_CLOUD_FILES.setupSteps);
|
|
318
|
+
const agentPath = FileSystemUtils.resolveProjectArtifactPath(projectPath, COPILOT_CLOUD_FILES.agent);
|
|
319
|
+
const alternateAgentPath = FileSystemUtils.resolveProjectArtifactPath(projectPath, COPILOT_AGENT_ALTERNATE_FILE);
|
|
320
|
+
await assertCreatableFilePath(setupStepsPath);
|
|
321
|
+
await assertCreatableFilePath(agentPath);
|
|
322
|
+
await assertMissingOrRegularFile(setupStepsPath);
|
|
323
|
+
await assertMissingOrRegularFile(agentPath);
|
|
324
|
+
await assertMissingOrRegularFile(alternateAgentPath);
|
|
325
|
+
const agentReconciliation = await classifyCopilotAgentReconciliation(agentPath, alternateAgentPath);
|
|
326
|
+
const setupStepsWritten = await reconcileCopilotCloudFile(setupStepsPath, COPILOT_CLOUD_FILES.setupSteps);
|
|
327
|
+
let agentWritten = false;
|
|
328
|
+
if (agentReconciliation === 'reconcile') {
|
|
329
|
+
agentWritten = await reconcileCopilotCloudFile(agentPath, COPILOT_CLOUD_FILES.agent);
|
|
330
|
+
}
|
|
331
|
+
else if (agentReconciliation === 'remove-managed') {
|
|
332
|
+
await fs.unlink(agentPath);
|
|
333
|
+
}
|
|
334
|
+
return { setupStepsWritten, agentWritten };
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Remove copilot cloud agent files from the project directory.
|
|
338
|
+
* Used when github-copilot is deselected.
|
|
339
|
+
*
|
|
340
|
+
* @returns Number of files removed.
|
|
341
|
+
*/
|
|
342
|
+
export async function removeCopilotCloudFiles(projectPath) {
|
|
343
|
+
let removed = 0;
|
|
344
|
+
const managedPaths = Object.values(COPILOT_CLOUD_FILES).map((relPath) => ({
|
|
345
|
+
relPath,
|
|
346
|
+
fullPath: FileSystemUtils.resolveProjectArtifactPath(projectPath, relPath),
|
|
347
|
+
}));
|
|
348
|
+
for (const { fullPath } of managedPaths) {
|
|
349
|
+
await assertMissingOrRegularFile(fullPath);
|
|
350
|
+
}
|
|
351
|
+
for (const { relPath, fullPath } of managedPaths) {
|
|
352
|
+
if (await FileSystemUtils.fileExists(fullPath)) {
|
|
353
|
+
const content = await FileSystemUtils.readFile(fullPath);
|
|
354
|
+
if (!isManagedCopilotCloudFile(relPath, content)) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
await fs.unlink(fullPath);
|
|
358
|
+
removed++;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return removed;
|
|
362
|
+
}
|
|
363
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
364
|
+
// Opt-in
|
|
365
|
+
//
|
|
366
|
+
// Generating a GitHub Actions workflow into a user's `.github/` is invasive and
|
|
367
|
+
// ties us to Copilot's externally-owned coding-agent format, so cloud files are
|
|
368
|
+
// opt-in rather than an automatic side effect of selecting the Copilot tool.
|
|
369
|
+
// The decision is persisted in openspec/config.yaml so non-interactive
|
|
370
|
+
// `openspec update` (CI, agents) honors it without ever prompting.
|
|
371
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
372
|
+
const COPILOT_CONFIG_KEY = 'githubCopilot';
|
|
373
|
+
const COPILOT_CLOUD_AGENT_KEY = 'cloudAgent';
|
|
374
|
+
/**
|
|
375
|
+
* Read the persisted opt-in for Copilot cloud-file generation.
|
|
376
|
+
*
|
|
377
|
+
* Tri-state: `true` (opted in), `false` (explicitly opted out), or `undefined`
|
|
378
|
+
* (never decided). A malformed value is treated as undecided rather than an
|
|
379
|
+
* error, matching how {@link readProjectConfig} degrades on bad fields.
|
|
380
|
+
*/
|
|
381
|
+
export function readCopilotCloudOptIn(projectPath) {
|
|
382
|
+
const value = readProjectConfig(projectPath)?.githubCopilot?.cloudAgent;
|
|
383
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* True when a managed Copilot cloud file (the current generation or a
|
|
387
|
+
* recognized legacy one) already exists. Projects created before the opt-in
|
|
388
|
+
* prompt existed are treated as implicitly opted in, so `openspec update`
|
|
389
|
+
* keeps their files current instead of silently abandoning them.
|
|
390
|
+
*/
|
|
391
|
+
export async function hasExistingManagedCloudFiles(projectPath) {
|
|
392
|
+
for (const relPath of Object.values(COPILOT_CLOUD_FILES)) {
|
|
393
|
+
const fullPath = FileSystemUtils.resolveProjectArtifactPath(projectPath, relPath);
|
|
394
|
+
if (!(await FileSystemUtils.fileExists(fullPath))) {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const content = await FileSystemUtils.readFile(fullPath);
|
|
398
|
+
if (isManagedCopilotCloudFile(relPath, content)) {
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Effective decision on whether to generate/refresh Copilot cloud files.
|
|
406
|
+
* An explicit opt-in or opt-out always wins; when undecided, fall back to
|
|
407
|
+
* whether managed files already exist (the migration path above).
|
|
408
|
+
*/
|
|
409
|
+
export async function isCopilotCloudEnabled(projectPath) {
|
|
410
|
+
const optIn = readCopilotCloudOptIn(projectPath);
|
|
411
|
+
if (typeof optIn === 'boolean') {
|
|
412
|
+
return optIn;
|
|
413
|
+
}
|
|
414
|
+
return hasExistingManagedCloudFiles(projectPath);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Persist the Copilot cloud opt-in into openspec/config.yaml.
|
|
418
|
+
*
|
|
419
|
+
* Uses the YAML document model rather than a re-serialize so the user's
|
|
420
|
+
* existing comments, ordering, and formatting survive untouched — the config
|
|
421
|
+
* file is hand-authored and heavily commented, so a lossy round-trip would be
|
|
422
|
+
* its own source of toil. No-op when no config file exists yet (init creates it
|
|
423
|
+
* before this is called); the caller treats persistence failures as non-fatal.
|
|
424
|
+
*/
|
|
425
|
+
export async function persistCopilotCloudOptIn(projectPath, value) {
|
|
426
|
+
const configPath = resolveConfigFilePath(projectPath);
|
|
427
|
+
if (!configPath) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const existing = await FileSystemUtils.readFile(configPath);
|
|
431
|
+
const parsed = parseDocument(existing);
|
|
432
|
+
// A file YAML can't parse cleanly — a multi-document stream, a tab-indented
|
|
433
|
+
// syntax error — can't be edited without corrupting it, and toString() would
|
|
434
|
+
// throw. Leave it untouched rather than clobber or crash; such a file is
|
|
435
|
+
// already invalid, so readProjectConfig ignores it anyway.
|
|
436
|
+
if (parsed.errors.length > 0) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
// `setIn(['githubCopilot', ...])` needs a top-level map. A config whose root
|
|
440
|
+
// is anything else — a scalar (`null`, a bare string) or even a sequence —
|
|
441
|
+
// has no map to set a key on and makes setIn throw. Such a file is already
|
|
442
|
+
// invalid (readProjectConfig rejects it), so start fresh rather than crash.
|
|
443
|
+
// An empty or comment-only file parses to null contents, which setIn fills in
|
|
444
|
+
// while keeping the comments — so only a non-map root is discarded.
|
|
445
|
+
const doc = parsed.contents === null || isMap(parsed.contents) ? parsed : new Document();
|
|
446
|
+
// The root is a map now, but the `githubCopilot` node itself may be a stray
|
|
447
|
+
// scalar/sequence/null (e.g. `githubCopilot: false`) — descending into that
|
|
448
|
+
// with setIn also throws. Replace any non-map node with an empty map first.
|
|
449
|
+
const section = doc.getIn([COPILOT_CONFIG_KEY], true);
|
|
450
|
+
if (section !== undefined && !isMap(section)) {
|
|
451
|
+
doc.setIn([COPILOT_CONFIG_KEY], new YAMLMap());
|
|
452
|
+
}
|
|
453
|
+
doc.setIn([COPILOT_CONFIG_KEY, COPILOT_CLOUD_AGENT_KEY], value);
|
|
454
|
+
await FileSystemUtils.writeFile(configPath, doc.toString());
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Return the managed cloud-file paths (relative to the project root) that
|
|
458
|
+
* currently hold user-owned, non-managed content — i.e. files OpenSpec will
|
|
459
|
+
* deliberately leave untouched. Used to tell an opted-in user that we preserved
|
|
460
|
+
* their existing file rather than silently doing nothing, which is the honest
|
|
461
|
+
* answer to "will this affect my existing Copilot cloud setup?".
|
|
462
|
+
*/
|
|
463
|
+
export async function findUnmanagedCloudFiles(projectPath) {
|
|
464
|
+
const collisions = [];
|
|
465
|
+
for (const relPath of Object.values(COPILOT_CLOUD_FILES)) {
|
|
466
|
+
const fullPath = FileSystemUtils.resolveProjectArtifactPath(projectPath, relPath);
|
|
467
|
+
if (!(await FileSystemUtils.fileExists(fullPath))) {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
const content = await FileSystemUtils.readFile(fullPath);
|
|
471
|
+
if (!isManagedCopilotCloudFile(relPath, content)) {
|
|
472
|
+
collisions.push(relPath);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return collisions;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Return the managed cloud-file paths (relative to the project root) that
|
|
479
|
+
* currently exist and hold OpenSpec-generated content. Callers report this
|
|
480
|
+
* rather than the intended paths, so output never claims a file that a write
|
|
481
|
+
* skipped (user already owns it) or that reconciliation removed.
|
|
482
|
+
*/
|
|
483
|
+
export async function listManagedCloudFiles(projectPath) {
|
|
484
|
+
const present = [];
|
|
485
|
+
for (const relPath of Object.values(COPILOT_CLOUD_FILES)) {
|
|
486
|
+
const fullPath = FileSystemUtils.resolveProjectArtifactPath(projectPath, relPath);
|
|
487
|
+
if (!(await FileSystemUtils.fileExists(fullPath))) {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const content = await FileSystemUtils.readFile(fullPath);
|
|
491
|
+
if (isManagedCopilotCloudFile(relPath, content)) {
|
|
492
|
+
present.push(relPath);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return present;
|
|
496
|
+
}
|
|
497
|
+
//# sourceMappingURL=cloud-agent.js.map
|
|
@@ -3,13 +3,29 @@ export declare const GLOBAL_CONFIG_FILE_NAME = "config.json";
|
|
|
3
3
|
export declare const GLOBAL_DATA_DIR_NAME = "openspec";
|
|
4
4
|
export type Profile = 'core' | 'custom';
|
|
5
5
|
export type Delivery = 'both' | 'skills' | 'commands';
|
|
6
|
+
/** Telemetry section of global config (identity + opt-out). */
|
|
7
|
+
export interface TelemetryConfig {
|
|
8
|
+
/** When false, telemetry is disabled. Unset means enabled (opt-out model). */
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
/** User identity (email or username) for telemetry. Stored in separate file for security. */
|
|
11
|
+
identity?: string;
|
|
12
|
+
/** Whether the first-run telemetry notice has been shown. */
|
|
13
|
+
noticeSeen?: boolean;
|
|
14
|
+
}
|
|
6
15
|
export interface GlobalConfig {
|
|
7
16
|
featureFlags?: Record<string, boolean>;
|
|
8
17
|
profile?: Profile;
|
|
9
18
|
delivery?: Delivery;
|
|
10
19
|
workflows?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Machine-level fallback store id, consulted during root resolution only
|
|
22
|
+
* when no --store flag, local root, or project-level store: pointer resolves.
|
|
23
|
+
*/
|
|
24
|
+
defaultStore?: string;
|
|
11
25
|
/** Workset opener rows (slice 7.1); hand-edited, validated on use. */
|
|
12
26
|
openers?: unknown;
|
|
27
|
+
/** Identity-based usage analytics settings and identity. */
|
|
28
|
+
telemetry?: TelemetryConfig;
|
|
13
29
|
}
|
|
14
30
|
/**
|
|
15
31
|
* Gets the global configuration directory path following XDG Base Directory Specification.
|
package/dist/core/init.d.ts
CHANGED
|
@@ -9,23 +9,81 @@ type InitCommandOptions = {
|
|
|
9
9
|
force?: boolean;
|
|
10
10
|
interactive?: boolean;
|
|
11
11
|
profile?: string;
|
|
12
|
+
/** Commander's --no-animation flag: false disables the welcome animation. */
|
|
13
|
+
animation?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Explicit opt-in/out for GitHub Copilot cloud coding-agent files.
|
|
16
|
+
* `--copilot-cloud` sets true, `--no-copilot-cloud` sets false; undefined
|
|
17
|
+
* leaves the decision to config, migration, or an interactive prompt.
|
|
18
|
+
*/
|
|
19
|
+
copilotCloud?: boolean;
|
|
12
20
|
};
|
|
13
21
|
export declare class InitCommand {
|
|
14
22
|
private readonly toolsArg?;
|
|
15
23
|
private readonly force;
|
|
16
24
|
private readonly interactiveOption?;
|
|
17
25
|
private readonly profileOverride?;
|
|
26
|
+
private readonly animation;
|
|
27
|
+
private readonly copilotCloudOption?;
|
|
18
28
|
constructor(options?: InitCommandOptions);
|
|
19
29
|
execute(targetPath: string): Promise<void>;
|
|
20
30
|
private validate;
|
|
21
31
|
private canPromptInteractively;
|
|
32
|
+
/**
|
|
33
|
+
* Decide whether to generate GitHub Copilot cloud files, and whether to
|
|
34
|
+
* persist that decision. Precedence:
|
|
35
|
+
* 1. `--copilot-cloud` / `--no-copilot-cloud` flag (explicit this run)
|
|
36
|
+
* 2. persisted opt-in in config.yaml
|
|
37
|
+
* 3. managed files already present (migration for pre-opt-in projects)
|
|
38
|
+
* 4. interactive confirm (default No)
|
|
39
|
+
* 5. non-interactive with no signal: skip, and don't persist a default
|
|
40
|
+
*
|
|
41
|
+
* @returns `write` — generate the files this run; `persist` — value to write
|
|
42
|
+
* back to config (undefined = leave config untouched); `optedOut` — the user
|
|
43
|
+
* explicitly declined, so any already-generated managed files should be
|
|
44
|
+
* removed; `skippedUndecided` — selected but no signal and couldn't ask, so
|
|
45
|
+
* the caller can hint that the opt-in exists.
|
|
46
|
+
*/
|
|
47
|
+
private resolveCopilotCloudDecision;
|
|
22
48
|
private resolveProfileOverride;
|
|
49
|
+
/**
|
|
50
|
+
* Resolves the workflows the effective profile installs, so onboarding output
|
|
51
|
+
* only mentions commands that will actually exist.
|
|
52
|
+
*/
|
|
53
|
+
private getActiveWorkflows;
|
|
54
|
+
/**
|
|
55
|
+
* Cleans repo-local legacy artifacts immediately and defers global Codex prompt
|
|
56
|
+
* cleanup until replacement skills have been installed.
|
|
57
|
+
*/
|
|
23
58
|
private handleLegacyCleanup;
|
|
59
|
+
/**
|
|
60
|
+
* Applies the safe subset of legacy cleanup that does not depend on newly
|
|
61
|
+
* generated Codex skills.
|
|
62
|
+
*/
|
|
63
|
+
private performImmediateLegacyCleanup;
|
|
64
|
+
/**
|
|
65
|
+
* Removes only the legacy global Codex prompts whose workflows now have
|
|
66
|
+
* replacement skills in the project.
|
|
67
|
+
*/
|
|
68
|
+
private finalizeDeferredLegacyCleanup;
|
|
69
|
+
/**
|
|
70
|
+
* Reads the currently installed workflow IDs for a single tool from the
|
|
71
|
+
* generated skill layout on disk.
|
|
72
|
+
*/
|
|
73
|
+
private getInstalledWorkflowsForTool;
|
|
24
74
|
private performLegacyCleanup;
|
|
25
75
|
private getSelectedTools;
|
|
26
76
|
private resolveToolsArg;
|
|
27
77
|
private validateTools;
|
|
28
78
|
private createDirectoryStructure;
|
|
79
|
+
/**
|
|
80
|
+
* Generates skill files and slash commands for each selected tool,
|
|
81
|
+
* honoring the configured delivery mode (skills, commands, or both).
|
|
82
|
+
*
|
|
83
|
+
* @param projectPath - Absolute path to the project root
|
|
84
|
+
* @param tools - Selected tools with their skill directory metadata
|
|
85
|
+
* @returns Created, refreshed, and failed tools plus removed artifact counts
|
|
86
|
+
*/
|
|
29
87
|
private generateSkillsAndCommands;
|
|
30
88
|
private createConfig;
|
|
31
89
|
private displaySuccessMessage;
|