opensdd 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ <p align="center">
2
+ <b>OpenSDD</b>
3
+ </p>
4
+
5
+ <p align="center">
6
+ <b>--Open Spec-Driven Development--</b>
7
+ </p>
8
+
9
+ ---
10
+
11
+ AI agents are great at writing code, but they need to know *what* to build. OpenSDD is a protocol and CLI for writing, sharing, and consuming **behavioral specs** — language-agnostic contracts that tell agents what software should do, without dictating how.
12
+
13
+ Think of it like a package manager, but for specifications instead of code.
14
+
15
+ ```bash
16
+ npm install -g opensdd
17
+
18
+ # Initialize OpenSDD in your project
19
+ opensdd init
20
+
21
+ # Install a spec from the registry
22
+ opensdd install slugify
23
+ ```
24
+
25
+ Then tell your coding agent: "implement the slugify spec" — it reads the spec, writes the code, and generates tests.
26
+
27
+ ## How It Works
28
+
29
+ 1. **Write a spec** — Define what your software does in `opensdd/spec.md` using a simple markdown format with behavioral contracts, edge cases, and invariants.
30
+ 2. **Share it** — Publish your spec to a registry with `opensdd publish`. Others can install it with `opensdd install`.
31
+ 3. **Implement it** — Your AI agent reads the spec and generates a bespoke implementation in whatever language and framework your project uses.
32
+
33
+ Specs are the source of truth. Code flows from the spec, not the other way around.
34
+
35
+ ## Features
36
+
37
+ - **Language-agnostic specs** — One spec, any implementation. The same spec produces TypeScript, Python, Rust, or whatever your project needs.
38
+ - **Registry & versioning** — Publish, install, and update specs with semver. Updates produce changesets so your agent knows exactly what changed.
39
+ - **Multi-agent support** — Skills auto-install for Claude Code, Codex CLI, Cursor, GitHub Copilot, Gemini CLI, and Amp.
40
+ - **Deviations** — Need to diverge from a spec? Document it formally instead of silently drifting.
41
+ - **Spec dependencies** — Specs can reference other specs for shared types and contracts.
42
+
43
+ ## Quick Example
44
+
45
+ A spec (`spec.md`) looks like this:
46
+
47
+ ```markdown
48
+ # slugify
49
+
50
+ > Converts a string into a URL-friendly slug.
51
+
52
+ ## Behavioral Contract
53
+
54
+ ### Core Behavior
55
+
56
+ Accepts a string and returns a lowercase, hyphen-separated slug.
57
+
58
+ - `slugify("Hello World")` MUST return `"hello-world"`
59
+ - `slugify("Déjà Vu")` MUST return `"deja-vu"`
60
+
61
+ ## Invariants
62
+
63
+ - For any string `x`: `slugify(slugify(x)) === slugify(x)` (idempotent)
64
+ ```
65
+
66
+ ## Documentation
67
+
68
+ - [Spec Format](opensdd/spec-format.md) — How to write specs
69
+ - [CLI Reference](opensdd/cli.md) — All CLI commands
70
+ - [SDD Manager Skill](opensdd/sdd-manager.md) — How agents implement and verify specs
71
+ - [SDD Generate Skill](opensdd/sdd-generate.md) — How agents generate specs from existing code
72
+
73
+ ## Development
74
+
75
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and workflows.
76
+
77
+ ## License
78
+
79
+ MIT — see [LICENSE](LICENSE) for details.
package/opensdd/cli.md CHANGED
@@ -81,38 +81,38 @@ Initialized OpenSDD:
81
81
 
82
82
  ### Skill Installation Mapping
83
83
 
84
- `opensdd init` installs both skills (sdd-manager and sdd-generate) into the native configuration format of each supported coding agent. The canonical skill content is authored as markdown source files (`sdd-manager.md`, `sdd-generate.md`, `spec-format.md`). The CLI transforms these into each agent's expected format during installation.
84
+ `opensdd init` installs both skills (sdd-manager and sdd-generate) into the native configuration format of each supported coding agent. The canonical skill content is authored as markdown source files in `opensdd/skills/` (`skills/sdd-manager.md`, `skills/sdd-generate.md`) and `opensdd/` (`spec-format.md`). Source skill files use Agent Skills frontmatter (`name`, `description`) which the CLI parses and transforms per-agent format during installation.
85
85
 
86
86
  All installed skill files are **spec-owned** — they are overwritten on every `opensdd init` and MUST NOT be edited by the user.
87
87
 
88
88
  #### Claude Code (Agent Skills standard)
89
89
 
90
- Each skill is a directory with a `SKILL.md` and a `references/` subdirectory. Claude Code auto-discovers skills in `.claude/skills/`.
90
+ Each skill is a directory with a `SKILL.md` and a `references/` subdirectory. Claude Code auto-discovers skills in `.claude/skills/`. The `SKILL.md` files include Agent Skills frontmatter with `name` and `description` fields, copied as-is from the source skill files.
91
91
 
92
92
  ```
93
93
  .claude/skills/
94
94
  sdd-manager/
95
- SKILL.md ← sdd-manager.md
95
+ SKILL.md ← skills/sdd-manager.md
96
96
  references/
97
97
  spec-format.md ← spec-format.md
98
98
  sdd-generate/
99
- SKILL.md ← sdd-generate.md
99
+ SKILL.md ← skills/sdd-generate.md
100
100
  references/
101
101
  spec-format.md ← spec-format.md
102
102
  ```
103
103
 
104
104
  #### OpenAI Codex CLI (Agent Skills standard)
105
105
 
106
- Codex CLI follows the same Agent Skills standard but discovers skills in `.agents/skills/`.
106
+ Codex CLI follows the same Agent Skills standard but discovers skills in `.agents/skills/`. The `SKILL.md` files include Agent Skills frontmatter with `name` and `description` fields, copied as-is from the source skill files.
107
107
 
108
108
  ```
109
109
  .agents/skills/
110
110
  sdd-manager/
111
- SKILL.md ← sdd-manager.md
111
+ SKILL.md ← skills/sdd-manager.md
112
112
  references/
113
113
  spec-format.md ← spec-format.md
114
114
  sdd-generate/
115
- SKILL.md ← sdd-generate.md
115
+ SKILL.md ← skills/sdd-generate.md
116
116
  references/
117
117
  spec-format.md ← spec-format.md
118
118
  ```
@@ -123,9 +123,9 @@ Cursor discovers rules as `.md` or `.mdc` files in `.cursor/rules/`. Each skill
123
123
 
124
124
  ```
125
125
  .cursor/rules/
126
- sdd-manager.md ← sdd-manager.md with frontmatter
127
- sdd-generate.md ← sdd-generate.md with frontmatter
128
- opensdd-spec-format.md ← spec-format.md with frontmatter
126
+ sdd-manager.md ← skills/sdd-manager.md with Cursor frontmatter
127
+ sdd-generate.md ← skills/sdd-generate.md with Cursor frontmatter
128
+ opensdd-spec-format.md ← spec-format.md with Cursor frontmatter
129
129
  ```
130
130
 
131
131
  Frontmatter for `sdd-manager.md`:
@@ -158,15 +158,32 @@ Copilot discovers instructions as `.instructions.md` files in `.github/instructi
158
158
 
159
159
  ```
160
160
  .github/instructions/
161
- sdd-manager.instructions.md ← sdd-manager.md with frontmatter
162
- sdd-generate.instructions.md ← sdd-generate.md with frontmatter
163
- opensdd-spec-format.instructions.md ← spec-format.md with frontmatter
161
+ sdd-manager.instructions.md ← skills/sdd-manager.md with Copilot frontmatter
162
+ sdd-generate.instructions.md ← skills/sdd-generate.md with Copilot frontmatter
163
+ opensdd-spec-format.instructions.md ← spec-format.md with Copilot frontmatter
164
164
  ```
165
165
 
166
- Frontmatter for each file:
166
+ Frontmatter for `sdd-manager.instructions.md`:
167
167
  ```yaml
168
168
  ---
169
169
  applyTo: "**"
170
+ description: "Implement, update, and verify installed OpenSDD dependency specs. Use when the user asks to implement a spec, process a spec update, check conformance, or create a deviation."
171
+ ---
172
+ ```
173
+
174
+ Frontmatter for `sdd-generate.instructions.md`:
175
+ ```yaml
176
+ ---
177
+ applyTo: "**"
178
+ description: "Generate an OpenSDD behavioral spec from existing code. Use when the user asks to generate, create, or extract a spec from a repository or codebase."
179
+ ---
180
+ ```
181
+
182
+ Frontmatter for `opensdd-spec-format.instructions.md`:
183
+ ```yaml
184
+ ---
185
+ applyTo: "**"
186
+ description: "OpenSDD spec format reference. Defines the structure and rules for behavioral specifications. Referenced by sdd-manager and sdd-generate skills."
170
187
  ---
171
188
  ```
172
189
 
@@ -1,3 +1,7 @@
1
+ ---
2
+ name: sdd-generate
3
+ description: "Generate an OpenSDD behavioral spec from existing code. Use when the user asks to generate, create, or extract a spec from a repository or codebase."
4
+ ---
1
5
  # SDD Generate
2
6
 
3
7
  > Guides an AI agent through analyzing a repository and generating a behavioral spec in the OpenSDD format.
@@ -12,7 +16,7 @@ Before starting, you need:
12
16
 
13
17
  1. A **target repository** — a GitHub URL or local path provided by the user.
14
18
  2. A **scope** — which capability, module, or function to spec. If the user provides a whole-repo URL without scoping, ask them to narrow it before proceeding. A spec for "the entire lodash library" is not useful. A spec for "lodash's string slugification" is.
15
- 3. The **spec-format reference** — read [spec-format.md](spec-format.md) to understand the required output structure.
19
+ 3. The **spec-format reference** — read [spec-format.md](../spec-format.md) to understand the required output structure.
16
20
  4. A **working directory** — confirm with the user where the generated spec should be written. If the project has `opensdd.json`, use the directory specified by `specs_dir` (default: `opensdd/`). If the project is not yet initialized, ask the user where to output the spec — a common default is `opensdd/` in the current project root. The agent does not need `opensdd.json` to exist before generating a spec.
17
21
 
18
22
  ## Output
@@ -153,7 +157,7 @@ Always update `_notes/gaps.md` at the end of each pass with what still needs to
153
157
  - The full `spec.md` draft
154
158
  - `_notes/inventory.md` — verify every public API item is covered
155
159
  - `_notes/gaps.md` — verify no critical gaps remain
156
- - [spec-format.md](spec-format.md) — verify structural compliance
160
+ - [spec-format.md](../spec-format.md) — verify structural compliance
157
161
 
158
162
  **Write:**
159
163
  - `spec.md` — add or complete:
@@ -1,3 +1,7 @@
1
+ ---
2
+ name: sdd-manager
3
+ description: "Implement, update, and verify installed OpenSDD dependency specs. Use when the user asks to implement a spec, process a spec update, check conformance, or create a deviation."
4
+ ---
1
5
  # SDD Manager
2
6
 
3
7
  > Teaches agents how to implement, update, and verify installed dependency specs in an OpenSDD-compliant project.
@@ -440,11 +440,11 @@ This is a transient artifact. `opensdd update apply` reads this file, applies th
440
440
 
441
441
  ### SDD-Manager Skill
442
442
 
443
- The sdd-manager skill teaches agents how to implement, update, and verify installed dependency specs. It is installed once per project via `opensdd init` alongside the sdd-generate skill, into each supported agent's configuration directory. See [sdd-manager.md](sdd-manager.md) for the full skill workflow, including implementation defaults, the project conventions check, and the verification protocol.
443
+ The sdd-manager skill teaches agents how to implement, update, and verify installed dependency specs. It is installed once per project via `opensdd init` alongside the sdd-generate skill, into each supported agent's configuration directory. See [sdd-manager.md](skills/sdd-manager.md) for the full skill workflow, including implementation defaults, the project conventions check, and the verification protocol.
444
444
 
445
445
  ### SDD-Generate Skill
446
446
 
447
- The sdd-generate skill teaches agents how to generate a spec from existing code. See [sdd-generate.md](sdd-generate.md) for the full skill workflow.
447
+ The sdd-generate skill teaches agents how to generate a spec from existing code. See [sdd-generate.md](skills/sdd-generate.md) for the full skill workflow.
448
448
 
449
449
  ### Versioning
450
450
 
@@ -0,0 +1,88 @@
1
+ # OpenSDD
2
+
3
+ > A protocol, CLI, and agent skills for spec-driven development — write behavioral specs, share them via a registry, and let AI agents generate bespoke implementations.
4
+
5
+ ## Version
6
+
7
+ 0.1.0
8
+
9
+ ## Overview
10
+
11
+ OpenSDD (Open Spec-Driven Development) treats behavioral specifications as the source of truth for software. A spec defines **what** software does and **what constraints** it must satisfy, while leaving **how** it is implemented to the consuming agent. Specs are language-agnostic by default — the same spec can produce implementations in any language or framework.
12
+
13
+ The system has four components:
14
+
15
+ - **[Spec Format](spec-format.md)** — The standard format for behavioral specifications. Defines spec structure, the `opensdd.json` manifest, the registry layout, and the protocol rules that all other components depend on.
16
+ - **[CLI](cli.md)** — A Node.js command-line tool (`opensdd`) that initializes projects, installs and updates specs from a registry, publishes authored specs, and installs agent skills.
17
+ - **[SDD Manager](sdd-manager.md)** — A skill installed into coding agents that teaches them how to implement, update, verify, and deviate from installed dependency specs.
18
+ - **[SDD Generate](sdd-generate.md)** — A skill installed into coding agents that teaches them how to generate a behavioral spec from an existing codebase.
19
+
20
+ ## Behavioral Contract
21
+
22
+ ### Spec Authoring
23
+
24
+ A project authors a spec by placing a `spec.md` in its `opensdd/` directory (configurable via `opensdd.json`). The spec MUST contain an H1 header with a blockquote summary and a `## Behavioral Contract` section. The author MAY include supplementary files in the same directory; all supplementary files MUST be reachable by following links from `spec.md`.
25
+
26
+ Development is spec-first: behavior changes start in the spec, then code is updated to match.
27
+
28
+ See [Spec Format](spec-format.md) for the full format definition, required and recommended sections, and inline example conventions.
29
+
30
+ ### Spec Distribution
31
+
32
+ Specs are distributed via a registry — a versioned store of published specs. The default registry is the `registry/` directory in the OpenSDD GitHub repository.
33
+
34
+ - `opensdd publish` packages the authored spec and opens a PR against the registry.
35
+ - `opensdd install <name>` fetches a spec from the registry and places it in `.opensdd.deps/<name>/`.
36
+ - `opensdd update [name]` pulls newer versions and stages changesets for the agent to process.
37
+
38
+ Installed spec files are spec-owned and MUST NOT be edited by the consumer. The `.opensdd.deps/` directory MUST be committed to the repo.
39
+
40
+ See [CLI](cli.md) for the full command reference, registry source resolution, and update staging behavior.
41
+
42
+ ### Spec Implementation
43
+
44
+ When a consumer installs a spec, their AI agent implements it using the sdd-manager skill. The agent reads the spec, checks project conventions (language, framework, test runner), generates an implementation, and runs a verification protocol that includes both a test suite and a spec compliance audit.
45
+
46
+ The agent MUST NOT implement or modify code based on an OpenSDD spec outside of the workflows defined by the sdd-manager skill.
47
+
48
+ See [SDD Manager](sdd-manager.md) for the implementation, update, conformance check, and deviation workflows.
49
+
50
+ ### Spec Generation
51
+
52
+ The sdd-generate skill teaches agents to generate a behavioral spec from an existing codebase via a multi-pass, artifact-driven strategy. The generated spec follows the spec format and is written to `opensdd/spec.md`.
53
+
54
+ See [SDD Generate](sdd-generate.md) for the multi-pass strategy, prerequisites, and output format.
55
+
56
+ ### Agent Skill Installation
57
+
58
+ `opensdd init` installs both skills (sdd-manager and sdd-generate) into the native configuration format of each supported coding agent:
59
+
60
+ - **Claude Code** — `.claude/skills/<name>/SKILL.md` with `references/`
61
+ - **OpenAI Codex CLI** — `.agents/skills/<name>/SKILL.md` with `references/`
62
+ - **Cursor** — `.cursor/rules/<name>.md` with YAML frontmatter
63
+ - **GitHub Copilot** — `.github/instructions/<name>.instructions.md` with YAML frontmatter
64
+ - **Gemini CLI** — `GEMINI.md` with `@` imports to canonical skill files
65
+ - **Amp** — `AGENTS.md` with `@` references to canonical skill files
66
+
67
+ All skill files are spec-owned and overwritten on every `opensdd init`. The Claude Code installation serves as the canonical source that Gemini CLI and Amp reference.
68
+
69
+ See [CLI — Skill Installation Mapping](cli.md#skill-installation-mapping) for the full mapping and installation rules.
70
+
71
+ ## NOT Specified (Implementation Freedom)
72
+
73
+ - The internal prompting strategy of the sdd-manager and sdd-generate skills
74
+ - How agents discover skills (defined by each agent's native mechanism)
75
+ - The specific testing framework or test runner (determined by the consumer's project)
76
+ - How agents generate implementations (model choice, temperature, etc.)
77
+ - The transport mechanism for fetching specs from a registry (defined by the CLI)
78
+ - File encoding (assumed UTF-8)
79
+
80
+ ## Invariants
81
+
82
+ - A spec MUST always contain an H1 header with blockquote summary and a `## Behavioral Contract` section
83
+ - Spec-owned files in `.opensdd.deps/` MUST NOT be modified by the consumer
84
+ - `deviations.md` MUST NOT be created, modified, or deleted by the CLI or any automated tooling
85
+ - Consumer-managed `opensdd.json` fields MUST survive all update operations
86
+ - The `.opensdd.deps/` directory MUST be committed to the repo
87
+ - Publishing MUST NOT allow overwriting an existing version in the registry
88
+ - All supplementary files in a spec directory MUST be reachable by following links from `spec.md`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opensdd",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The official OpenSDD (Open Spec-Driven Development) CLI, spec, and spec registry",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,4 +28,4 @@
28
28
  "dependencies": {
29
29
  "diff": "^7.0.0"
30
30
  }
31
- }
31
+ }
@@ -145,14 +145,13 @@ export async function publishCommand(options) {
145
145
  }
146
146
 
147
147
  const { owner, repo } = parseGitHubUrl(registrySource);
148
- const repoUrl = `https://github.com/${owner}/${repo}.git`;
149
148
 
150
149
  // Step 10: Clone, create branch, add files, push, create PR
151
150
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opensdd-publish-'));
152
151
 
153
152
  try {
154
- // Clone
155
- execSync(`git clone --depth 1 "${repoUrl}" "${tmpDir}"`, { stdio: 'pipe' });
153
+ // Clone using gh to respect the user's configured git protocol (ssh vs https)
154
+ execSync(`gh repo clone "${owner}/${repo}" "${tmpDir}" -- --depth 1`, { stdio: 'pipe' });
156
155
 
157
156
  // Create branch
158
157
  execSync(`git checkout -b "${branchName}"`, { cwd: tmpDir, stdio: 'pipe' });
package/src/lib/skills.js CHANGED
@@ -8,11 +8,30 @@ const __dirname = path.dirname(__filename);
8
8
  const OPENSDD_SECTION_START = '<!-- OpenSDD Skills (managed by opensdd init \u2014 do not edit this section) -->';
9
9
  const OPENSDD_SECTION_END = '<!-- /OpenSDD Skills -->';
10
10
 
11
+ function parseFrontmatter(content) {
12
+ if (!content.startsWith('---\n')) return { frontmatter: {}, body: content };
13
+ const endIdx = content.indexOf('\n---\n', 4);
14
+ if (endIdx === -1) return { frontmatter: {}, body: content };
15
+
16
+ const yamlStr = content.substring(4, endIdx);
17
+ const body = content.substring(endIdx + 5);
18
+
19
+ const frontmatter = {};
20
+ for (const line of yamlStr.split('\n')) {
21
+ const match = line.match(/^(\w+):\s*"([^"]*)"\s*$/) || line.match(/^(\w+):\s*(.*?)\s*$/);
22
+ if (match) {
23
+ frontmatter[match[1]] = match[2];
24
+ }
25
+ }
26
+ return { frontmatter, body };
27
+ }
28
+
11
29
  function getSkillContent() {
12
30
  const opensddDir = path.resolve(__dirname, '../../opensdd');
31
+ const skillsDir = path.join(opensddDir, 'skills');
13
32
  return {
14
- sddManager: fs.readFileSync(path.join(opensddDir, 'sdd-manager.md'), 'utf-8'),
15
- sddGenerate: fs.readFileSync(path.join(opensddDir, 'sdd-generate.md'), 'utf-8'),
33
+ sddManager: fs.readFileSync(path.join(skillsDir, 'sdd-manager.md'), 'utf-8'),
34
+ sddGenerate: fs.readFileSync(path.join(skillsDir, 'sdd-generate.md'), 'utf-8'),
16
35
  specFormat: fs.readFileSync(path.join(opensddDir, 'spec-format.md'), 'utf-8'),
17
36
  };
18
37
  }
@@ -121,19 +140,22 @@ export function installSkills(projectRoot) {
121
140
  const cursorBase = path.join(projectRoot, '.cursor', 'rules');
122
141
  ensureDir(cursorBase);
123
142
 
143
+ const { frontmatter: managerFm, body: managerBody } = parseFrontmatter(skills.sddManager);
144
+ const { frontmatter: generateFm, body: generateBody } = parseFrontmatter(skills.sddGenerate);
145
+
124
146
  const sddManagerCursor = `---
125
- description: "Implement, update, and verify installed OpenSDD dependency specs. Use when the user asks to implement a spec, process a spec update, check conformance, or create a deviation."
147
+ description: "${managerFm.description}"
126
148
  alwaysApply: false
127
149
  ---
128
150
 
129
- ${skills.sddManager}`;
151
+ ${managerBody}`;
130
152
 
131
153
  const sddGenerateCursor = `---
132
- description: "Generate an OpenSDD behavioral spec from existing code. Use when the user asks to generate, create, or extract a spec from a repository or codebase."
154
+ description: "${generateFm.description}"
133
155
  alwaysApply: false
134
156
  ---
135
157
 
136
- ${skills.sddGenerate}`;
158
+ ${generateBody}`;
137
159
 
138
160
  const specFormatCursor = `---
139
161
  description: "OpenSDD spec format reference. Defines the structure and rules for behavioral specifications. Referenced by sdd-manager and sdd-generate skills."
@@ -154,23 +176,20 @@ ${skills.specFormat}`;
154
176
  const copilotBase = path.join(projectRoot, '.github', 'instructions');
155
177
  ensureDir(copilotBase);
156
178
 
157
- const copilotFrontmatter = `---
158
- applyTo: "**"
159
- ---
160
-
161
- `;
179
+ const { frontmatter: managerFmCp, body: managerBodyCp } = parseFrontmatter(skills.sddManager);
180
+ const { frontmatter: generateFmCp, body: generateBodyCp } = parseFrontmatter(skills.sddGenerate);
162
181
 
163
182
  writeFileSync(
164
183
  path.join(copilotBase, 'sdd-manager.instructions.md'),
165
- copilotFrontmatter + skills.sddManager
184
+ `---\napplyTo: "**"\ndescription: "${managerFmCp.description}"\n---\n\n${managerBodyCp}`
166
185
  );
167
186
  writeFileSync(
168
187
  path.join(copilotBase, 'sdd-generate.instructions.md'),
169
- copilotFrontmatter + skills.sddGenerate
188
+ `---\napplyTo: "**"\ndescription: "${generateFmCp.description}"\n---\n\n${generateBodyCp}`
170
189
  );
171
190
  writeFileSync(
172
191
  path.join(copilotBase, 'opensdd-spec-format.instructions.md'),
173
- copilotFrontmatter + skills.specFormat
192
+ `---\napplyTo: "**"\ndescription: "OpenSDD spec format reference. Defines the structure and rules for behavioral specifications. Referenced by sdd-manager and sdd-generate skills."\n---\n\n${skills.specFormat}`
174
193
  );
175
194
  } catch (err) {
176
195
  warnings.push(`Could not install GitHub Copilot skills: ${err.message}`);