opensdd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepAgents
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/bin/opensdd.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../src/index.js';
package/opensdd/cli.md ADDED
@@ -0,0 +1,633 @@
1
+ # CLI
2
+
3
+ > A thin Node.js command-line tool that installs, updates, publishes, and manages OpenSDD specs.
4
+
5
+ ## Version
6
+
7
+ 0.1.0
8
+
9
+ ## Overview
10
+
11
+ The CLI is the distribution mechanism for OpenSDD specs. It handles initializing projects, fetching specs from a registry, managing `opensdd.json`, producing changesets for dependency updates, and publishing authored specs to the registry. It does not perform AI operations — all implementation, testing, and conformance checking is delegated to the consumer's coding agent via the sdd-manager skill.
12
+
13
+ The CLI is published to npm as `opensdd` and invoked as `opensdd`.
14
+
15
+ ## Behavioral Contract
16
+
17
+ ### `opensdd init`
18
+
19
+ Initializes the OpenSDD protocol in the current project.
20
+
21
+ #### Behavior
22
+
23
+ 1. Verify the current directory is a project root (contains `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `.git`, `opensdd.json`, or similar project markers). If no project marker is found, warn the user and ask for confirmation to proceed.
24
+ 2. Install both skills (sdd-manager and sdd-generate) into all supported agent configuration directories. If already present, overwrite — skills are always spec-owned. The full installation mapping is defined in the **Skill Installation Mapping** section below.
25
+ 3. If `opensdd.json` does not exist at the project root, create it with default contents: `{ "opensdd": "0.1.0", "specs_dir": "opensdd", "deps_dir": ".opensdd.deps" }`. The `registry`, `publish`, and `dependencies` fields are intentionally omitted from the default — they are optional. When `registry` is absent, the CLI defaults to `https://github.com/deepagents-ai/opensdd` per the registry source resolution logic. If `opensdd.json` already exists, leave it untouched.
26
+ 4. Create the `opensdd/` directory (or the directory specified by `specs_dir` in an existing `opensdd.json`) if it does not exist.
27
+ 5. If `<specs_dir>/spec.md` does not exist, create a skeleton `spec.md` with placeholder sections:
28
+ ```markdown
29
+ # {project-name}
30
+
31
+ > TODO: One-line description of what this software does.
32
+
33
+ ## Behavioral Contract
34
+
35
+ <!-- Define behaviors here. -->
36
+
37
+ ## NOT Specified (Implementation Freedom)
38
+
39
+ <!-- List aspects left to the implementer's discretion. -->
40
+
41
+ ## Invariants
42
+
43
+ <!-- List properties that must hold true across all inputs and states. -->
44
+ ```
45
+ The `{project-name}` placeholder SHOULD be inferred from the nearest project manifest (e.g., `name` in `package.json`) or default to the directory name. If `spec.md` already exists, leave it untouched.
46
+ 6. Create the `.opensdd.deps/` directory (or the directory specified by `deps_dir` in an existing `opensdd.json`) if it does not exist.
47
+ 7. Print a success message.
48
+
49
+ - `opensdd init` in a fresh project MUST produce the output below
50
+ - `opensdd init` in a project that already has OpenSDD initialized MUST overwrite all skill installation files across all agent formats but MUST NOT overwrite `opensdd.json`
51
+
52
+ #### Output
53
+
54
+ ```
55
+ Initialized OpenSDD:
56
+ Skills installed for: Claude Code, Codex CLI, Cursor, GitHub Copilot, Gemini CLI, Amp
57
+ sdd-manager installed (6 agent formats)
58
+ sdd-generate installed (6 agent formats)
59
+ opensdd.json created
60
+ opensdd/ created
61
+ opensdd/spec.md created (skeleton)
62
+ .opensdd.deps/ created
63
+ ```
64
+
65
+ If already initialized:
66
+ ```
67
+ Initialized OpenSDD:
68
+ Skills installed for: Claude Code, Codex CLI, Cursor, GitHub Copilot, Gemini CLI, Amp
69
+ sdd-manager updated (6 agent formats)
70
+ sdd-generate updated (6 agent formats)
71
+ opensdd.json already exists (preserved)
72
+ opensdd/ already exists
73
+ opensdd/spec.md already exists (preserved)
74
+ .opensdd.deps/ already exists
75
+ ```
76
+
77
+ #### Errors
78
+
79
+ - If `.claude/` directory cannot be created (permissions error), print error and exit with code 1.
80
+ - If `opensdd.json` exists but is malformed JSON, print error and exit with code 1.
81
+
82
+ ### Skill Installation Mapping
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.
85
+
86
+ All installed skill files are **spec-owned** — they are overwritten on every `opensdd init` and MUST NOT be edited by the user.
87
+
88
+ #### Claude Code (Agent Skills standard)
89
+
90
+ Each skill is a directory with a `SKILL.md` and a `references/` subdirectory. Claude Code auto-discovers skills in `.claude/skills/`.
91
+
92
+ ```
93
+ .claude/skills/
94
+ sdd-manager/
95
+ SKILL.md ← sdd-manager.md
96
+ references/
97
+ spec-format.md ← spec-format.md
98
+ sdd-generate/
99
+ SKILL.md ← sdd-generate.md
100
+ references/
101
+ spec-format.md ← spec-format.md
102
+ ```
103
+
104
+ #### OpenAI Codex CLI (Agent Skills standard)
105
+
106
+ Codex CLI follows the same Agent Skills standard but discovers skills in `.agents/skills/`.
107
+
108
+ ```
109
+ .agents/skills/
110
+ sdd-manager/
111
+ SKILL.md ← sdd-manager.md
112
+ references/
113
+ spec-format.md ← spec-format.md
114
+ sdd-generate/
115
+ SKILL.md ← sdd-generate.md
116
+ references/
117
+ spec-format.md ← spec-format.md
118
+ ```
119
+
120
+ #### Cursor
121
+
122
+ Cursor discovers rules as `.md` or `.mdc` files in `.cursor/rules/`. Each skill becomes a single rule file with YAML frontmatter. The frontmatter uses `description` for intelligent matching (Cursor decides when to apply the rule based on relevance). The body contains the full skill content followed by a reference to `spec-format.md`.
123
+
124
+ ```
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
129
+ ```
130
+
131
+ Frontmatter for `sdd-manager.md`:
132
+ ```yaml
133
+ ---
134
+ 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."
135
+ alwaysApply: false
136
+ ---
137
+ ```
138
+
139
+ Frontmatter for `sdd-generate.md`:
140
+ ```yaml
141
+ ---
142
+ 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."
143
+ alwaysApply: false
144
+ ---
145
+ ```
146
+
147
+ Frontmatter for `opensdd-spec-format.md`:
148
+ ```yaml
149
+ ---
150
+ description: "OpenSDD spec format reference. Defines the structure and rules for behavioral specifications. Referenced by sdd-manager and sdd-generate skills."
151
+ alwaysApply: false
152
+ ---
153
+ ```
154
+
155
+ #### GitHub Copilot
156
+
157
+ Copilot discovers instructions as `.instructions.md` files in `.github/instructions/`. Each skill becomes an instruction file with YAML frontmatter. The `applyTo` field is set to `"**"` so the instructions are available across the project.
158
+
159
+ ```
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
164
+ ```
165
+
166
+ Frontmatter for each file:
167
+ ```yaml
168
+ ---
169
+ applyTo: "**"
170
+ ---
171
+ ```
172
+
173
+ #### Gemini CLI
174
+
175
+ Gemini CLI discovers `GEMINI.md` files in the project directory and supports `@file.md` import syntax for referencing other files. Rather than duplicating skill content, the CLI appends import directives to `GEMINI.md` (creating it if it does not exist) that reference the canonical skill files from the Claude Code installation.
176
+
177
+ Appended to `GEMINI.md`:
178
+ ```markdown
179
+ <!-- OpenSDD Skills (managed by opensdd init — do not edit this section) -->
180
+ @.claude/skills/sdd-manager/SKILL.md
181
+ @.claude/skills/sdd-manager/references/spec-format.md
182
+ @.claude/skills/sdd-generate/SKILL.md
183
+ @.claude/skills/sdd-generate/references/spec-format.md
184
+ ```
185
+
186
+ The CLI MUST only modify the clearly delimited OpenSDD section. If a `GEMINI.md` already exists with an OpenSDD section, the CLI MUST replace that section. Content outside the section MUST NOT be modified.
187
+
188
+ #### Amp
189
+
190
+ Amp discovers `AGENTS.md` files in the project directory and supports `@` reference syntax for including other files. The CLI appends references to `AGENTS.md` (creating it if it does not exist) that point to the canonical skill files.
191
+
192
+ Appended to `AGENTS.md`:
193
+ ```markdown
194
+ <!-- OpenSDD Skills (managed by opensdd init — do not edit this section) -->
195
+ @.claude/skills/sdd-manager/SKILL.md
196
+ @.claude/skills/sdd-manager/references/spec-format.md
197
+ @.claude/skills/sdd-generate/SKILL.md
198
+ @.claude/skills/sdd-generate/references/spec-format.md
199
+ ```
200
+
201
+ The CLI MUST only modify the clearly delimited OpenSDD section. If an `AGENTS.md` already exists with an OpenSDD section, the CLI MUST replace that section. Content outside the section MUST NOT be modified.
202
+
203
+ #### Installation notes
204
+
205
+ - The Claude Code installation (`.claude/skills/`) serves as the canonical source that Gemini CLI and Amp reference via imports. It MUST always be installed, even if the user only uses Gemini or Amp.
206
+ - All installed files are overwritten on every `opensdd init`. The CLI MUST NOT prompt for confirmation before overwriting skill files.
207
+ - If a target directory cannot be created (e.g., permissions), the CLI SHOULD warn and continue installing to other agent directories rather than failing entirely.
208
+ - For Gemini CLI and Amp, the CLI MUST NOT overwrite user content in `GEMINI.md` or `AGENTS.md` — it MUST only manage the clearly delimited OpenSDD section.
209
+
210
+ ### `opensdd list`
211
+
212
+ Lists specs available in the registry.
213
+
214
+ #### Behavior
215
+
216
+ 1. Fetch the directory listing of the `registry/` folder from the configured registry source (default: the OpenSDD GitHub repository).
217
+ 2. For each subdirectory in `registry/`, read `index.json` to extract `name`, `latest` version, `description`.
218
+ 3. Print a formatted list showing each spec's name, latest version, and description.
219
+
220
+ - `opensdd list` MUST work from any directory (no project markers required)
221
+
222
+ #### Input
223
+
224
+ Optional flag: `--registry <url>` to specify an alternative registry source.
225
+
226
+ #### Output
227
+
228
+ ```
229
+ Available specs:
230
+
231
+ slugify v2.2.0 String to URL-friendly slug
232
+ http-retry v1.0.0 HTTP request retry with backoff
233
+ payments v1.3.0 Payment provider integrations
234
+ ```
235
+
236
+ #### Errors
237
+
238
+ - If the registry is unreachable, print error with the URL that failed and exit with code 1.
239
+ - If a spec's `index.json` is malformed or missing, skip it and print a warning.
240
+
241
+ ### `opensdd install <name> [version]`
242
+
243
+ Fetches a spec from the registry and installs it as a dependency.
244
+
245
+ #### Behavior
246
+
247
+ 1. Verify `opensdd.json` exists at the project root. If not, print a message suggesting `opensdd init` first and exit with code 1.
248
+ 2. Check if the spec `<name>` already exists as a key in `opensdd.json`'s `dependencies` object. If it does AND the spec directory exists in `<deps_dir>`, print a message indicating the spec is already installed and suggest `opensdd update` instead. Exit with code 1. If the entry exists BUT the spec directory is missing, treat as a re-install: log a message noting the stale entry, then continue to step 4 using the version from the existing entry (unless `[version]` is explicitly provided, in which case use that).
249
+ 3. Validate the spec name (lowercase alphanumeric and hyphens only).
250
+ 4. Fetch `index.json` from `registry/<name>/` in the configured registry source. If `[version]` is provided, use that version; otherwise use `latest` from `index.json`.
251
+ 5. Read `manifest.json` from `registry/<name>/<version>/` to get spec_format and dependencies.
252
+ 6. Copy all files from `registry/<name>/<version>/` into `<deps_dir>/<name>/` (including `manifest.json`, `spec.md`, and any supplementary files).
253
+ 7. Add an entry to `opensdd.json` under `dependencies.<name>` with fields from `manifest.json` (`version`, `spec_format`), the resolved registry URL as `source`, and consumer-managed fields initialized to defaults: `implementation: null`, `tests: null`, `has_deviations: false`.
254
+ 8. If the spec has `dependencies`, check whether each dependency name exists as a key in `opensdd.json`'s `dependencies` object. If any are missing, print a warning listing the missing dependencies and suggesting `opensdd install` for each.
255
+ 9. Print a success message.
256
+
257
+ - `opensdd install slugify` MUST create `.opensdd.deps/slugify/` with all spec files and add a `slugify` entry to `opensdd.json` `dependencies`
258
+
259
+ #### Input
260
+
261
+ - `<name>` (required): The spec name as it appears in the registry.
262
+ - `[version]` (optional): Specific semver version to install. Defaults to latest.
263
+ - `--registry <url>` (optional): Alternative registry source.
264
+
265
+ #### Output
266
+
267
+ ```
268
+ Installed slugify v2.2.0 to .opensdd.deps/slugify/
269
+
270
+ Run "implement the slugify spec" in your agent to generate an implementation.
271
+ ```
272
+
273
+ #### Errors
274
+
275
+ - Spec not found in registry: print error listing available specs and exit with code 1.
276
+ - Requested version not found: print error listing available versions and exit with code 1.
277
+ - Spec already installed (entry and directory both exist): print message suggesting `opensdd update` and exit with code 1.
278
+ - OpenSDD not initialized: print message suggesting `opensdd init` and exit with code 1.
279
+
280
+ ### `opensdd update [name]`
281
+
282
+ Fetches the latest version of installed dependency specs from the registry, updates spec files in `.opensdd.deps/`, and stages the update for the agent to process. Does NOT modify `opensdd.json` — the dependency entry remains at the old version until `opensdd update apply` is called after the agent has confirmed the migration.
283
+
284
+ #### Behavior
285
+
286
+ 1. If `<name>` is provided, update that single spec. If no name is provided, update all installed dependencies.
287
+ 2. For each spec being updated:
288
+ a. Read the spec's entry in `opensdd.json` `dependencies` to get the installed version and `spec_format` version.
289
+ b. Fetch `index.json` from the registry to get the latest version. Read `manifest.json` from the latest version directory.
290
+ c. If the registry version matches the installed version, skip with a message "already up to date".
291
+ d. Before overwriting, compute unified diffs of all spec-owned files that will change.
292
+ e. Overwrite all spec-owned files in `<deps_dir>/<name>/` with the new version from the registry (`manifest.json`, `spec.md`, and any supplementary files).
293
+ f. MUST NOT overwrite or delete `deviations.md`. The CLI MUST NOT create, modify, or delete `deviations.md` under any circumstances.
294
+ g. Create the staging directory `.opensdd.deps/.updates/<name>/` and write two files. If a pending update already exists for this spec, overwrite it and note the replacement in the output.
295
+ - `changeset.md` — contains previous and new version, `spec_format` version change (if any), and unified diffs from step (d).
296
+ - `manifest.json` — contains the metadata needed to finalize the update in `opensdd.json`: `name`, `previous_version`, `version`, `source`, `spec_format`.
297
+ 3. Print a summary of what was updated.
298
+
299
+ #### Input
300
+
301
+ - `[name]` (optional): Dependency spec to update. If omitted, update all installed dependencies.
302
+ - `--registry <url>` (optional): Alternative registry source.
303
+
304
+ #### Output
305
+
306
+ For a single spec:
307
+ ```
308
+ Updated slugify: v2.1.0 -> v2.2.0
309
+
310
+ Changed files:
311
+ spec.md updated
312
+
313
+ Preserved:
314
+ deviations.md (consumer-owned, not modified)
315
+
316
+ Staged update:
317
+ .opensdd.deps/.updates/slugify/changeset.md
318
+ .opensdd.deps/.updates/slugify/manifest.json
319
+
320
+ Run "process the slugify spec update" in your agent.
321
+ After confirming, run: opensdd update apply slugify
322
+ ```
323
+
324
+ For all specs:
325
+ ```
326
+ Updated 2 of 3 installed specs:
327
+
328
+ slugify v2.1.0 -> v2.2.0 staged
329
+ payments v1.3.0 -> v1.4.0 staged
330
+ http-retry v1.0.0 already up to date
331
+
332
+ Run "process spec updates" in your agent.
333
+ After confirming each update, run:
334
+ opensdd update apply slugify
335
+ opensdd update apply payments
336
+ ```
337
+
338
+ #### Errors
339
+
340
+ - Spec not installed: print error and exit with code 1.
341
+ - Registry unreachable: print error and exit with code 1.
342
+
343
+ ### `opensdd update apply [name]`
344
+
345
+ Applies a staged update to `opensdd.json`, confirming that the migration is complete.
346
+
347
+ #### Behavior
348
+
349
+ 1. If `<name>` is provided, apply that single update. If no name is provided, apply all pending updates.
350
+ 2. Print a warning: "This will finalize the update in opensdd.json. Only proceed if you have confirmed that all spec changes have been implemented and tests pass."
351
+ 3. Prompt the user for confirmation (y/n). If declined, exit with code 0.
352
+ 4. For each pending update:
353
+ a. Read `.opensdd.deps/.updates/<name>/manifest.json` to get the update metadata.
354
+ b. Update the `opensdd.json` `dependencies.<name>` entry: set `version`, `source`, and `spec_format` from the manifest. Preserve all consumer-managed fields (`implementation`, `tests`, `has_deviations`).
355
+ c. Delete the `.opensdd.deps/.updates/<name>/` directory.
356
+ 5. If `.opensdd.deps/.updates/` is now empty, delete it.
357
+ 6. Print a summary.
358
+
359
+ #### Input
360
+
361
+ - `[name]` (optional): Dependency spec to apply. If omitted, apply all pending updates.
362
+
363
+ #### Output
364
+
365
+ For a single spec:
366
+ ```
367
+ ⚠ This will finalize the update in opensdd.json.
368
+ Only proceed if you have confirmed that all spec changes
369
+ have been implemented and tests pass.
370
+
371
+ Apply update for slugify v2.1.0 -> v2.2.0? (y/n) y
372
+
373
+ Applied update for slugify: v2.1.0 -> v2.2.0
374
+
375
+ opensdd.json updated
376
+ staged files cleaned up
377
+ ```
378
+
379
+ For all pending:
380
+ ```
381
+ ⚠ This will finalize the update in opensdd.json.
382
+ Only proceed if you have confirmed that all spec changes
383
+ have been implemented and tests pass.
384
+
385
+ Apply 2 pending updates? (y/n) y
386
+
387
+ Applied 2 updates:
388
+
389
+ slugify v2.1.0 -> v2.2.0 applied
390
+ payments v1.3.0 -> v1.4.0 applied
391
+
392
+ opensdd.json updated.
393
+ ```
394
+
395
+ #### Errors
396
+
397
+ - No pending update for the specified spec: print error and exit with code 1.
398
+ - No pending updates at all (when no name provided): print "No pending updates." and exit with code 0.
399
+ - `.opensdd.deps/.updates/<name>/manifest.json` is missing or malformed: print error and exit with code 1.
400
+
401
+ ### `opensdd publish`
402
+
403
+ Publishes an authored spec to the registry.
404
+
405
+ #### Behavior
406
+
407
+ 1. Verify `opensdd.json` exists at the project root. If not, print a message suggesting `opensdd init` first and exit with code 1.
408
+ 2. Verify `opensdd.json` has a `publish` object. If not, print an error suggesting the user add a `publish` section and exit with code 1.
409
+ 3. Read the `publish` object to get `name`, `version`, `description`, `spec_format`, `dependencies`.
410
+ 4. Verify `<specs_dir>/spec.md` exists. If not, print error and exit with code 1.
411
+ 5. Run validation on the `<specs_dir>/` directory (same logic as `opensdd validate`). If validation fails with errors, print them and exit with code 1.
412
+ 6. Resolve the registry source. The registry MUST be a GitHub repository URL for publishing.
413
+ 7. Fetch `index.json` from `registry/<name>/` if it exists. If the version being published already exists in `index.json`, print error suggesting a version bump and exit with code 1.
414
+ 8. Construct the registry entry:
415
+ a. Build `manifest.json` from the `opensdd.json` `publish` fields (`name`, `version`, `description`, `spec_format`, `dependencies`).
416
+ b. Collect all files from `<specs_dir>/`.
417
+ 9. If `--branch <name>` was provided, use that as the branch name. Otherwise, prompt the user for a branch name.
418
+ 10. Clone the registry repo (shallow), create a new branch with the chosen name, and:
419
+ a. Create `registry/<name>/<version>/` with `manifest.json` and all spec files.
420
+ b. Update (or create) `registry/<name>/index.json` — set `latest` to the new version, add the version to `versions`.
421
+ 11. Commit the changes, push the branch, and open a pull request using `gh pr create`.
422
+ 12. Print a success message with the PR URL.
423
+
424
+ - `opensdd publish` MUST create a PR adding `registry/<name>/<version>/` with the spec files
425
+ - `opensdd publish` MUST NOT allow publishing a version that already exists in the registry
426
+
427
+ #### Input
428
+
429
+ - `--branch <name>` (optional): Branch name for the registry PR. If omitted, the CLI prompts the user.
430
+ - `--registry <url>` (optional): Alternative registry source (must be a GitHub URL).
431
+
432
+ #### Output
433
+
434
+ ```
435
+ Publishing auth v1.0.0 to registry...
436
+
437
+ Validated spec ok
438
+ Created branch opensdd/auth-v1.0.0
439
+ Created registry entry registry/auth/1.0.0/
440
+ Updated index.json latest: 1.0.0
441
+ Opened pull request https://github.com/deepagents-ai/opensdd/pull/42
442
+
443
+ Published. Spec will be available after PR is merged.
444
+ ```
445
+
446
+ #### Errors
447
+
448
+ - OpenSDD not initialized: print message suggesting `opensdd init` and exit with code 1.
449
+ - No `publish` section in `opensdd.json`: print error and exit with code 1.
450
+ - `<specs_dir>/spec.md` missing: print error and exit with code 1.
451
+ - Spec validation fails: print validation errors and exit with code 1.
452
+ - Version already exists in registry: print error suggesting version bump and exit with code 1.
453
+ - Registry is not a GitHub URL: print error (publishing requires a GitHub registry) and exit with code 1.
454
+ - Git or `gh` CLI not available: print error with installation guidance and exit with code 1.
455
+ - Git authentication fails: print error guiding the user to authenticate and exit with code 1.
456
+
457
+ ### `opensdd status`
458
+
459
+ Shows the status of the authored spec and all installed dependency specs in the current project.
460
+
461
+ #### Behavior
462
+
463
+ 1. Read `opensdd.json`.
464
+ 2. If `publish` exists, print authored spec section showing the spec's name, version, and directory.
465
+ 3. If `dependencies` exists and has entries, iterate the `dependencies` object. For each entry, read its consumer-managed fields and check for the presence of `deviations.md` in `<deps_dir>/<name>/`. Print a dependency status table.
466
+ 4. Check for untracked directories: spec directories in `<deps_dir>` that have no corresponding `opensdd.json` dependency entry. Warn about any found.
467
+
468
+ #### Output
469
+
470
+ ```
471
+ Authored spec:
472
+
473
+ auth v1.0.0 opensdd/
474
+
475
+ Installed dependencies:
476
+
477
+ slugify v2.1.0 implemented src/utils/slugify.ts
478
+ payments v1.3.0 implemented src/payments/index.ts 2 deviations
479
+ http-retry v1.0.0 not implemented
480
+ ```
481
+
482
+ #### Errors
483
+
484
+ - If `opensdd.json` does not exist, print "OpenSDD not initialized. Run `opensdd init` to get started."
485
+ - If no specs are published or installed, print "No specs found. Run `opensdd install <name>` to install a dependency or add a `publish` entry to opensdd.json."
486
+
487
+ ### `opensdd validate [path]`
488
+
489
+ Validates that a spec directory conforms to the OpenSDD spec-format.
490
+
491
+ #### Behavior
492
+
493
+ 1. If `[path]` is provided, use it as the directory to validate. If omitted, look for the `opensdd/` directory in the current working directory and use that.
494
+ 2. Read the directory.
495
+ 3. Check for required files: `spec.md` MUST exist.
496
+ 4. If `manifest.json` exists, validate it:
497
+ a. `name` MUST be present.
498
+ b. `spec_format` MUST be present and be a recognized version.
499
+ c. `version` MUST be present and be valid semver.
500
+ 5. Validate `spec.md`:
501
+ a. MUST start with an H1 header followed by a blockquote summary.
502
+ b. MUST contain `## Behavioral Contract` section.
503
+ c. `## NOT Specified` section SHOULD be present (warn if missing).
504
+ d. `## Invariants` section SHOULD be present (warn if missing).
505
+ e. `## Edge Cases` SHOULD be present (warn if missing).
506
+ 6. Verify no `deviations.md` exists (specs intended for the registry MUST NOT contain deviations).
507
+ 7. Print a summary of validation results.
508
+
509
+ - `opensdd validate` MUST work on any local directory (no project markers required)
510
+ - `opensdd validate` MUST NOT require OpenSDD to be initialized
511
+
512
+ #### Input
513
+
514
+ - `[path]` (optional): Path to a spec directory to validate. If omitted, defaults to `opensdd/` in the current directory.
515
+
516
+ #### Output
517
+
518
+ For a valid spec:
519
+ ```
520
+ Validated slugify v2.1.0
521
+
522
+ spec.md structure ok
523
+ manifest.json ok
524
+ no deviations.md ok
525
+
526
+ Valid. Ready for publishing to registry.
527
+ ```
528
+
529
+ For a spec with issues:
530
+ ```
531
+ Validated slugify v2.1.0
532
+
533
+ spec.md structure 2 warnings
534
+ - Missing ## NOT Specified section (recommended)
535
+ - Missing ## Edge Cases section (recommended)
536
+ manifest.json ok
537
+ no deviations.md ok
538
+
539
+ Valid with warnings. Review warnings before publishing.
540
+ ```
541
+
542
+ For an invalid spec:
543
+ ```
544
+ Validation failed for slugify
545
+
546
+ spec.md structure error
547
+ - Missing required: H1 header with blockquote summary
548
+ manifest.json error
549
+ - Missing required field: version
550
+
551
+ 2 errors. Fix errors before publishing.
552
+ ```
553
+
554
+ #### Errors
555
+
556
+ - Path does not exist or is not a directory: print error and exit with code 1.
557
+ - No `[path]` provided and the default `opensdd/` directory does not exist: print error ("No spec directory found. Provide a path or run from a directory containing `opensdd/`.") and exit with code 1.
558
+ - Missing required file (`spec.md`): report as validation error (do not exit early — continue checking what can be checked).
559
+ - `opensdd validate` MUST exit with code 0 if validation passes (including with warnings) and code 1 if any errors are found.
560
+
561
+ ### Manifest Resolution
562
+
563
+ Commands that require `opensdd.json` (all commands except `opensdd list` and `opensdd validate`) MUST resolve it by searching upward from the current working directory, stopping at the first `opensdd.json` found. This supports monorepos where each sub-project has its own `opensdd.json`. If no `opensdd.json` is found in any ancestor directory, the command fails with the appropriate "not initialized" error.
564
+
565
+ `opensdd init` always creates `opensdd.json` in the current working directory.
566
+
567
+ ### Registry Source Resolution
568
+
569
+ The CLI fetches specs from a registry source. The default source is the `registry/` directory in the OpenSDD GitHub repository.
570
+
571
+ The CLI MUST resolve the registry source in this order:
572
+ 1. `--registry` flag on the command (if provided)
573
+ 2. `registry` field in `opensdd.json` (if present)
574
+ 3. Default: `https://github.com/deepagents-ai/opensdd` (the canonical registry)
575
+
576
+ For GitHub sources, the CLI MUST use the GitHub API to list directory contents and fetch raw file content. This avoids requiring a full git clone (except for `opensdd publish`, which requires a clone to create a branch and PR).
577
+
578
+ For local paths, the CLI MUST read directly from the filesystem. This supports development and testing of specs before publishing to a registry.
579
+
580
+ ### Consumer-Managed Field Preservation
581
+
582
+ During `opensdd update apply`, the CLI MUST preserve these `opensdd.json` fields for each affected dependency entry:
583
+ - `implementation`
584
+ - `tests`
585
+ - `has_deviations`
586
+
587
+ The CLI reads the existing `opensdd.json` dependency entry, applies updated metadata from the staged manifest, then re-applies the consumer-managed field values. Note that `opensdd update` does NOT touch `opensdd.json` at all — it only stages the update. The `opensdd.json` entry continues to reflect the old version until `opensdd update apply` is called.
588
+
589
+ ## Edge Cases
590
+
591
+ - Running `opensdd install` for a spec that was previously installed and then manually deleted (directory gone, `opensdd.json` entry gone): treat as a fresh install.
592
+ - Running `opensdd install` when the `opensdd.json` dependency entry exists but the directory is missing: treat as a re-install — re-fetch spec files for the version in the existing entry and recreate the directory.
593
+ - Spec directory exists in `.opensdd.deps/` but has no `opensdd.json` dependency entry: `opensdd status` MUST warn about untracked spec directories.
594
+ - Running `opensdd update` when a spec's registry entry has been removed: print a warning that the spec is no longer available in the registry but leave local files and `opensdd.json` entry untouched.
595
+ - Running `opensdd update` when a pending update already exists for the spec: overwrite the existing staged update with the new one.
596
+ - Running `opensdd update apply` when no pending updates exist: print "No pending updates." and exit with code 0 (not an error).
597
+ - Running `opensdd update apply <name>` when the agent hasn't finished processing the changeset: the CLI has no way to verify this — it's the user's responsibility to confirm the migration is complete before applying.
598
+ - Running `opensdd init` in a project that already has OpenSDD initialized: overwrite all skill installation files across all agent formats, leave `opensdd.json` untouched.
599
+ - Running any command outside a project directory (no project markers found): warn but allow with confirmation, except `opensdd list` and `opensdd validate` which work anywhere.
600
+ - Spec name contains characters invalid for directory names: reject with an error listing allowed characters (lowercase alphanumeric and hyphens).
601
+ - Publishing a version that already exists in the registry: reject with an error suggesting a version bump.
602
+ - Publishing when the registry is a local path (not GitHub): reject with an error (publishing requires a GitHub registry for PR workflow).
603
+ - Running `opensdd publish` when `gh` CLI is not installed: print error with installation guidance.
604
+ - Running `opensdd install` with a version that doesn't exist in the registry: print error listing available versions.
605
+
606
+ ## NOT Specified (Implementation Freedom)
607
+
608
+ - HTTP client library choice for GitHub API requests
609
+ - Caching strategy for registry requests
610
+ - CLI framework (commander, yargs, or plain process.argv parsing)
611
+ - Output coloring/formatting approach
612
+ - Whether to use TypeScript or plain JavaScript internally
613
+ - Progress indicator style for network requests
614
+ - The exact format of the registry's internal files beyond the required fields
615
+ - The git branching strategy used during `opensdd publish` (branch name is user-provided)
616
+ - Whether `opensdd publish` does a shallow or full clone of the registry repo
617
+
618
+ ## Invariants
619
+
620
+ - The CLI MUST NOT create, modify, or delete `deviations.md` under any circumstances
621
+ - `opensdd install` MUST NOT create a `deviations.md` file
622
+ - `opensdd update` MUST NOT modify `opensdd.json` — it only stages the update
623
+ - `opensdd update` MUST create a staging directory in `.opensdd.deps/.updates/<name>/` for every spec that was updated (not for specs already up to date)
624
+ - `opensdd update apply` MUST update `opensdd.json` and delete the staging directory
625
+ - All skill installation files across all agent formats MUST always be overwritten on `opensdd init` (they are fully spec-owned)
626
+ - The Claude Code skill installation (`.claude/skills/`) MUST always be present since Gemini CLI and Amp reference it
627
+ - `opensdd.json` MUST be created by `opensdd init` if it does not exist, and MUST NOT be overwritten if it already exists
628
+ - Consumer-managed `opensdd.json` fields MUST survive all update operations
629
+ - Every installed dependency MUST have both a directory in `deps_dir` and an entry in `opensdd.json` `dependencies`
630
+ - All commands MUST exit with code 0 on success and code 1 on error
631
+ - The CLI MUST NOT invoke an AI model or coding agent
632
+ - `opensdd publish` MUST NOT allow overwriting an existing version in the registry
633
+ - `.opensdd.deps/` MUST be committed to the repo (NOT gitignored)