github-delivery-os 1.1.0 → 1.2.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.
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: delivery-ops
3
+ description: Operate a repo that has GitHub Delivery OS installed — create sprint/production-release/QA-request/bug issues that actually trigger its automation, comment as an approver in phrasing its workflows recognize, and check status (labels, latest comments, burn-down). Targets a specific repo via --repo; defaults to the current repo if this skill was installed into it and none is named. Use when asked to create a sprint, request a release, approve/decline a release, check release or sprint status, or demo/test Delivery OS against a given repo.
4
+ ---
5
+
6
+ # Operate Delivery OS
7
+
8
+ This drives the actual product — the workflows Delivery OS installs into a consumer repo — as a user of that repo would, not the tooling that ships the `github-delivery-os` package itself (that's the separate `release` skill). Use it to create issues that correctly trigger the installed automation, comment in a way the automation actually recognizes, and check what state something is in.
9
+
10
+ ## Which repo?
11
+
12
+ Every command below takes `--repo <owner>/<name>` explicitly — never assume based on the working directory alone. Two ways this gets decided:
13
+
14
+ - **This skill was installed via `npx github-delivery-os install --with-skill`, into a specific repo's own `.claude/skills/`.** In that case the current working directory *is* the repo Delivery OS is installed in, so it's a reasonable default target if the user doesn't name a different one — just confirm that's what they mean before acting.
15
+ - **This skill is installed generically** (copied into `~/.claude/skills/`, available across every project). Here there's no natural default — the working directory could be anything. Ask which repo if it isn't named.
16
+
17
+ Creating issues and comments in a repo is a visible, outward action — other collaborators see it. Confirm the target repo and intent before creating anything real, the same as any other action that shows up in someone else's GitHub activity.
18
+
19
+ ## Pre-flight check
20
+
21
+ Before creating anything that depends on configuration, check the target repo actually has Delivery OS installed and configured — a silent no-op (nothing happens because a variable is unset) is more confusing than an upfront "this won't do much yet":
22
+
23
+ - **Installed?** `gh api repos/<owner>/<repo>/contents/.github/workflows/authorize-deployment.yml --silent` (404 = not installed — suggest `npx github-delivery-os status .` or `install --with-templates` in that repo).
24
+ - **Labels set up?** `gh label list --repo <owner>/<repo>` — look for `production`, `qa`, `qa-request`, `sprint`, `sprint-active`, `planning`, `declined`, `ready-for-deploy`. Missing labels mean `Setup Labels` hasn't been run there yet — offer to fix it directly rather than just reporting the gap: `gh workflow run setup-labels.yml --repo <owner>/<repo>` (it's a `workflow_dispatch` trigger, so this actually creates them on the spot). Confirm with the user first since it's a real change to their repo.
25
+ - **Repo variables set?** `gh variable list --repo <owner>/<repo>` — look for `RELEASE_APPROVER`, `QA_APPROVER`, `QA_ASSIGNEES`. If unset, say so plainly: the issue will still get created, but `notify-release-approver` will ping the literal placeholder `release-approver`/`qa-approver`, not a real person. Setting these requires repo admin access (`gh variable set NAME --repo <owner>/<repo> --body <value>`) — don't set them without being asked to, since they name a real person as approver.
26
+
27
+ ## Creating issues
28
+
29
+ `gh issue create` does not render GitHub's Issue Forms (the `.github/ISSUE_TEMPLATE/*.yml` files) — those only exist in the web UI. So the body has to be hand-built to match what a form submission would actually produce: `### <Field Label>` headings with the answer beneath each, because that heading text is exactly what the workflows regex-parse. Labels have to be passed explicitly too, since the template's auto-applied labels are also bypassed.
30
+
31
+ **Show the constructed title, body, and labels before actually creating the issue** — this is a real, visible action in someone else's repo, not a preview in this conversation. Get confirmation on the content, not just the target repo, before calling `gh issue create`.
32
+
33
+ **Sprint Planning** — triggers `sprint-child-creator` (one child issue per feature line, each labeled `sprint-active`, on open):
34
+ - Title **must contain** the literal string `SPRINT -`, e.g. `SPRINT - Sprint 14`
35
+ - Labels: `sprint`, `planning`
36
+ - Body:
37
+ ```
38
+ ### Sprint Name
39
+
40
+ <name>
41
+
42
+ ### Sprint Start
43
+
44
+ YYYY-MM-DD
45
+
46
+ ### Sprint End
47
+
48
+ YYYY-MM-DD
49
+
50
+ ### Sprint Goal
51
+
52
+ <goal>
53
+
54
+ ### Sprint Features (One Per Line)
55
+
56
+ <feature one>
57
+ <feature two>
58
+ <feature three>
59
+
60
+ ### Sprint Approved
61
+
62
+ Pending
63
+ ```
64
+ One feature per line, no bullets/numbering (matches the template's own instruction — `sprint-child-creator`'s parser just splits on newlines).
65
+
66
+ **Production Release** — triggers `notify-release-approver` on open (posts a comment tagging `RELEASE_APPROVER`), and later `authorize-deployment` on comments:
67
+ - Title: `PRODUCTION RELEASE - <project> - vX.X.X`
68
+ - Labels: `release`, `production`, `approval`
69
+ - Body:
70
+ ```
71
+ ### Sprint Reference (Sprint Planning Issue #)
72
+
73
+ #<N>
74
+
75
+ ### Version / Build Number
76
+
77
+ vX.X.X
78
+
79
+ ### Release Summary
80
+
81
+ <summary>
82
+
83
+ ### QA Summary + Evidence Links
84
+
85
+ <links, or "None yet">
86
+
87
+ ### Overall QA Recommendation
88
+
89
+ Approve for Production
90
+
91
+ ### Deployment Authorized
92
+
93
+ No
94
+ ```
95
+ (`qa_recommendation` drives the "QA Recommendation" line `notify-release-approver` puts in its comment — use `Approve for Production`, `Reject Release`, or `Conditional Approval` verbatim, those are the three strings it checks for.)
96
+
97
+ **QA Request** — triggers `auto-assign-qa` (assigns `QA_ASSIGNEES`) on open:
98
+ - Title: `QA REQUEST - <feature/issue>`
99
+ - Labels: `qa-request`
100
+ - Body:
101
+ ```
102
+ ### Related Sprint Task Issue (#)
103
+
104
+ #<N>
105
+
106
+ ### What to Test
107
+
108
+ <what to test>
109
+
110
+ ### Environment + Build Link
111
+
112
+ <build link>
113
+
114
+ ### Acceptance Criteria
115
+
116
+ <criteria>
117
+
118
+ ### QA Outcome
119
+
120
+ Pending
121
+ ```
122
+
123
+ **Bug Report**:
124
+ - Title: `[BUG] <one-line summary>`
125
+ - Labels: `bug`, `qa`
126
+ - Body: mirror `bug_report.yml`'s fields (`Platform(s) Affected`, `Severity`, `Build / Version`, `Bug Summary`, `Steps to Reproduce`, `Expected Result`, `Actual Result`, `Test Environment`) as `### <label>` / answer pairs.
127
+
128
+ **Task** — no automation trigger, just structured tracking:
129
+ - Title: `TASK - <one-line summary>`
130
+ - Labels: `task`
131
+ - Body: mirror `task.yml`'s fields (`Task Summary`, `Description`, `Owner`, `Priority` — `P0 - Critical` / `P1 - High` / `P2 - Medium` / `P3 - Low`, `Status` — `Backlog` / `In Progress` / `Blocked` / `Ready for Review` / `Done`, `Acceptance Criteria`, `Artifacts / Links`) as `### <label>` / answer pairs.
132
+
133
+ ## Commenting as an approver
134
+
135
+ `authorize-deployment` only registers a comment if **both** of these hold:
136
+ - It's posted by the exact GitHub login configured in the repo's `RELEASE_APPROVER` or `QA_APPROVER` variable. `gh issue comment` posts as whichever account `gh auth status` shows — if that's not the configured approver, the comment is just a comment, nothing fires.
137
+ - The comment **leads with** one of the recognized keywords (case-insensitive; anything after the keyword is fine, but the keyword itself has to be at the start):
138
+ - Release approve: `approved`, `approve`, `ok`, `go ahead`
139
+ - Release decline: `declined`, `rejected`, `reject`, `not approved`
140
+ - QA approve: `qa approved`, `approved`, `qa ok`, `looks good`
141
+
142
+ A later qualifying comment from the same approver overrides an earlier one — a decline can be superseded by a later approval once fixes land, and vice versa.
143
+
144
+ **Before posting, check that the authenticated login actually matches the approver you're commenting as** — `gh auth status` (or `gh api user --jq .login`) against the `RELEASE_APPROVER`/`QA_APPROVER` value from the pre-flight check. If they don't match, say so and stop: the comment would still post, look successful, and do nothing — a silent no-op that's easy to miss without this check, since `gh issue comment` succeeds either way.
145
+
146
+ ```
147
+ gh issue comment <number> --repo <owner>/<repo> --body "Approved, ship it"
148
+ ```
149
+
150
+ ## Checking status
151
+
152
+ - **Latest comment(s):** `gh issue view <number> --repo <owner>/<repo> --comments`
153
+ - **Current labels:** `gh issue view <number> --repo <owner>/<repo> --json labels`
154
+ - **Sprint burn-down:** read the sprint (parent) issue's body — `gh issue view <sprint-number> --repo <owner>/<repo> --json body` — and look for the `## 🚦 Sprint Status` section `auto-close-sprint` maintains (progress %, time elapsed %, health emoji, burn-down bar). It only exists after at least one child issue has closed.
155
+
156
+ ## Advancing a sprint
157
+
158
+ Closing a sprint task (child) issue is what actually moves the burn-down — creating the sprint only creates the children, nothing updates until they close:
159
+
160
+ ```
161
+ gh issue close <number> --repo <owner>/<repo>
162
+ ```
163
+
164
+ `auto-close-sprint` fires on close, re-reads every `sprint-active` issue whose body contains `Parent Sprint: #<N>`, recomputes progress, and rewrites the sprint issue's `## 🚦 Sprint Status` section. At 100% it also closes the sprint issue itself and posts a completion comment. Re-check the sprint issue's body afterward to see the update — it happens as a side effect of closing the child, not as a response visible on the child issue itself.
165
+
166
+ ## Finding things
167
+
168
+ When there's no issue number in hand yet:
169
+ - **Production releases awaiting a decision:** `gh issue list --repo <owner>/<repo> --label production --state open`
170
+ - **Active sprints:** `gh issue list --repo <owner>/<repo> --label sprint --state open` (title contains `SPRINT -`)
171
+ - **Open QA requests:** `gh issue list --repo <owner>/<repo> --label qa-request --state open`
172
+ - **A sprint's own children:** `gh issue list --repo <owner>/<repo> --label sprint-active --search "\"Parent Sprint: #<N>\" in:body"` — the exact-phrase quotes matter, otherwise the search matches "Parent", "Sprint", and the number as separate free-text terms instead of the literal phrase
package/README.md CHANGED
@@ -30,7 +30,7 @@ The **GitHub Delivery Operating System (Delivery OS)** embeds structured intake,
30
30
  npx github-delivery-os install --with-templates .
31
31
  ```
32
32
 
33
- From your repo root. Add `--with-labels` to create labels via `gh` CLI (requires `gh auth`). Use `--dry-run` to preview first.
33
+ From your repo root. Add `--with-labels` to create labels via `gh` CLI (requires `gh auth`). Add `--with-skill` to also drop in a [Claude Code](https://claude.com/claude-code) skill for operating this repo's Delivery OS from Claude Code — creating sprint/release/QA issues, commenting as an approver, checking status. Use `--dry-run` to preview first.
34
34
 
35
35
  **Alternative — clone and run script:**
36
36
 
@@ -77,6 +77,8 @@ Workflows and templates are **copied directly** into your repo. No `workflow_cal
77
77
 
78
78
  Installing via `npx github-delivery-os` (not the `scripts/install.sh` clone path) also writes `.github/delivery-os.json`, a small manifest recording the installed version — this is what powers the update check in `status`. It's only written when the files it describes are actually current (a fresh install, or `--overwrite`); a skip-mode install over existing files leaves it untouched rather than claiming a version that isn't really on disk. `uninstall` removes it.
79
79
 
80
+ With `--with-skill`, a `.claude/skills/delivery-ops/SKILL.md` file is also written — a Claude Code skill scoped to this repo, so anyone working here with Claude Code can create issues that correctly trigger the workflows above, comment as an approver, and check status without knowing the underlying `gh` commands or issue-body formats by heart. It's opt-in and retroactive: `--with-skill` on any later `install` call adds it if it isn't there yet.
81
+
80
82
  ---
81
83
 
82
84
  ## Quick Start (After Install)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-delivery-os",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A GitHub-native Delivery Governance Framework for structured sprint execution, QA review, and collaborative production release control.",
5
5
  "main": "src/install.js",
6
6
  "bin": {
@@ -38,8 +38,15 @@
38
38
  "files": [
39
39
  "bin",
40
40
  "src",
41
- ".github/workflows",
42
- ".github/ISSUE_TEMPLATE"
41
+ ".github/workflows/sprint-child-creator.yml",
42
+ ".github/workflows/auto-close-sprint.yml",
43
+ ".github/workflows/notify-release-approver.yml",
44
+ ".github/workflows/authorize-deployment.yml",
45
+ ".github/workflows/auto-assign-qa.yml",
46
+ ".github/workflows/telegram-issues.yml",
47
+ ".github/workflows/setup-labels.yml",
48
+ ".github/ISSUE_TEMPLATE",
49
+ ".claude/skills/delivery-ops"
43
50
  ],
44
51
  "engines": {
45
52
  "node": ">=14.0.0"
package/src/cli.js CHANGED
@@ -20,6 +20,7 @@ program
20
20
  .description('Install workflows and templates into a repository')
21
21
  .option('-t, --with-templates', 'Copy issue templates (sprint, task, bug, QA, production release)')
22
22
  .option('-l, --with-labels', 'Create labels via gh CLI (requires gh auth)')
23
+ .option('-s, --with-skill', 'Add the delivery-ops Claude Code skill (.claude/skills/delivery-ops/SKILL.md)')
23
24
  .option('-o, --overwrite', 'Replace existing workflow/template files')
24
25
  .option('--no-overwrite', 'Skip existing files (default)')
25
26
  .option('-d, --dry-run', 'Show what would happen without changing files')
@@ -29,6 +30,7 @@ program
29
30
  targetDir,
30
31
  withTemplates: options.withTemplates ?? false,
31
32
  withLabels: options.withLabels ?? false,
33
+ withSkill: options.withSkill ?? false,
32
34
  overwrite: options.overwrite ?? false,
33
35
  dryRun: options.dryRun ?? false,
34
36
  });
@@ -46,11 +48,13 @@ program
46
48
  .command('uninstall [target]')
47
49
  .description('Remove Delivery OS workflows (and optionally templates)')
48
50
  .option('-t, --with-templates', 'Also remove issue templates')
51
+ .option('-s, --with-skill', 'Also remove the delivery-ops Claude Code skill')
49
52
  .option('-d, --dry-run', 'Show what would be removed without deleting')
50
53
  .action((target, options) => {
51
54
  runUninstall({
52
55
  targetDir: target || '.',
53
56
  withTemplates: options.withTemplates ?? false,
57
+ withSkill: options.withSkill ?? false,
54
58
  dryRun: options.dryRun ?? false,
55
59
  });
56
60
  });
package/src/install.js CHANGED
@@ -4,7 +4,12 @@ const https = require('https');
4
4
  const { execFileSync } = require('child_process');
5
5
 
6
6
  const MANIFEST_FILE = 'delivery-os.json'; // written to .github/delivery-os.json in the target repo
7
+ const SKILL_REL_PATH = path.join('.claude', 'skills', 'delivery-ops', 'SKILL.md'); // opt-in via --with-skill
7
8
 
9
+ // If you add/remove/rename an entry here, also update package.json's "files"
10
+ // array — it lists these paths explicitly (not the whole .github/workflows
11
+ // directory) so this package's own maintainer workflows (ci.yml, release.yml,
12
+ // pages.yml) don't get bundled into what ships to consumers.
8
13
  const WORKFLOWS = [
9
14
  'sprint-child-creator',
10
15
  'auto-close-sprint',
@@ -37,6 +42,10 @@ function manifestPath(targetAbs) {
37
42
  return path.join(targetAbs, '.github', MANIFEST_FILE);
38
43
  }
39
44
 
45
+ function skillPath(targetAbs) {
46
+ return path.join(targetAbs, SKILL_REL_PATH);
47
+ }
48
+
40
49
  function readManifest(targetAbs) {
41
50
  try {
42
51
  return JSON.parse(fs.readFileSync(manifestPath(targetAbs), 'utf8'));
@@ -112,6 +121,7 @@ function runInstall(options) {
112
121
  targetDir = '.',
113
122
  withTemplates = false,
114
123
  withLabels = false,
124
+ withSkill = false,
115
125
  overwrite = false,
116
126
  dryRun = false,
117
127
  } = options;
@@ -119,6 +129,7 @@ function runInstall(options) {
119
129
  const pkgRoot = getPackageRoot();
120
130
  const workflowsSrc = path.join(pkgRoot, '.github', 'workflows');
121
131
  const templatesSrc = path.join(pkgRoot, '.github', 'ISSUE_TEMPLATE');
132
+ const skillSrc = path.join(pkgRoot, SKILL_REL_PATH);
122
133
  const targetAbs = path.resolve(process.cwd(), targetDir);
123
134
 
124
135
  console.log('=== GitHub Delivery Operating System ===');
@@ -148,8 +159,10 @@ function runInstall(options) {
148
159
 
149
160
  let workflowsCopied = 0;
150
161
  let templatesCopied = 0;
162
+ let skillCopied = 0;
151
163
  let workflowsSkipped = 0;
152
164
  let templatesSkipped = 0;
165
+ let skillSkipped = 0;
153
166
 
154
167
  // Copy workflows
155
168
  for (const wf of WORKFLOWS) {
@@ -197,6 +210,25 @@ function runInstall(options) {
197
210
  }
198
211
  }
199
212
 
213
+ // Copy the delivery-ops Claude Code skill (opt-in — most consumer repos
214
+ // aren't using Claude Code, so this is never written unless asked for)
215
+ if (withSkill && fs.existsSync(skillSrc)) {
216
+ const skillDest = skillPath(targetAbs);
217
+
218
+ if (fs.existsSync(skillDest) && !overwrite) {
219
+ console.log(` Skipped (exists): ${SKILL_REL_PATH}`);
220
+ skillSkipped++;
221
+ } else if (dryRun) {
222
+ console.log(` [dry-run] Would create: ${SKILL_REL_PATH}`);
223
+ skillCopied++;
224
+ } else {
225
+ fs.mkdirSync(path.dirname(skillDest), { recursive: true });
226
+ fs.copyFileSync(skillSrc, skillDest);
227
+ console.log(` Created: ${SKILL_REL_PATH}`);
228
+ skillCopied++;
229
+ }
230
+ }
231
+
200
232
  // Create labels via gh
201
233
  let labelsCreated = 0;
202
234
  let labelsSkipReason = '';
@@ -264,7 +296,7 @@ function runInstall(options) {
264
296
  // skip-mode install would leave older file content on disk, so don't
265
297
  // overwrite a previously recorded (possibly accurate, possibly newer)
266
298
  // version with a number that isn't actually true on disk yet.
267
- const cleanInstall = overwrite || (workflowsSkipped === 0 && templatesSkipped === 0);
299
+ const cleanInstall = overwrite || (workflowsSkipped === 0 && templatesSkipped === 0 && skillSkipped === 0);
268
300
  if (!dryRun && cleanInstall) {
269
301
  const pkgVersion = require(path.join(pkgRoot, 'package.json')).version;
270
302
  writeManifest(targetAbs, pkgVersion);
@@ -278,13 +310,15 @@ function runInstall(options) {
278
310
  console.log(' all files (and the recorded version) to the latest release.');
279
311
  console.log('');
280
312
  }
281
- if (workflowsCopied > 0 || templatesCopied > 0 || labelsCreated > 0) {
313
+ if (workflowsCopied > 0 || templatesCopied > 0 || skillCopied > 0 || labelsCreated > 0) {
282
314
  if (dryRun) {
283
315
  if (workflowsCopied > 0) console.log(`Would install ${workflowsCopied} workflow(s).`);
284
316
  if (templatesCopied > 0) console.log(`Would copy ${templatesCopied} issue template(s).`);
317
+ if (skillCopied > 0) console.log('Would add the Claude Code delivery-ops skill.');
285
318
  } else {
286
319
  if (workflowsCopied > 0) console.log(`Installed ${workflowsCopied} workflow(s).`);
287
320
  if (templatesCopied > 0) console.log(`Copied ${templatesCopied} issue template(s).`);
321
+ if (skillCopied > 0) console.log('Added the Claude Code delivery-ops skill.');
288
322
  if (labelsCreated > 0) console.log(`Created ${labelsCreated} label(s).`);
289
323
  }
290
324
  console.log('');
@@ -296,8 +330,14 @@ function runInstall(options) {
296
330
  console.log(' - QA_APPROVER: GitHub username of QA approver');
297
331
  console.log(' - QA_ASSIGNEES: Comma-separated usernames for QA assignment');
298
332
  console.log(' 3. Add secrets (optional, for Telegram): TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID');
333
+ let nextStep = 4;
299
334
  if (!withTemplates) {
300
- console.log(' 4. Copy templates: re-run with --with-templates');
335
+ console.log(` ${nextStep}. Copy templates: re-run with --with-templates`);
336
+ nextStep++;
337
+ }
338
+ if (!withSkill) {
339
+ console.log(` ${nextStep}. Add the Claude Code delivery-ops skill (optional, for Claude Code users): re-run with --with-skill`);
340
+ nextStep++;
301
341
  }
302
342
  console.log('');
303
343
  console.log('See https://phaneroo.github.io/github-delivery-operating-system/ for full docs.');
@@ -338,6 +378,7 @@ async function runStatus(options) {
338
378
  const installedTemplates = TEMPLATES.filter((t) =>
339
379
  fs.existsSync(path.join(templatesDest, t))
340
380
  );
381
+ const skillInstalled = fs.existsSync(skillPath(targetAbs));
341
382
 
342
383
  if (installedWorkflows.length > 0 || installedTemplates.length > 0) {
343
384
  const manifest = readManifest(targetAbs);
@@ -383,18 +424,29 @@ async function runStatus(options) {
383
424
  console.log('');
384
425
  }
385
426
 
427
+ if (installedWorkflows.length > 0 || installedTemplates.length > 0) {
428
+ console.log('Claude Code skill:');
429
+ console.log(
430
+ skillInstalled
431
+ ? ' ✓ delivery-ops'
432
+ : ' ○ delivery-ops (not installed — re-run install with --with-skill)'
433
+ );
434
+ console.log('');
435
+ }
436
+
386
437
  if (installedWorkflows.length === 0 && installedTemplates.length === 0) {
387
438
  console.log('Delivery OS is not installed in this repository.');
388
439
  console.log('Run: npx github-delivery-os install --with-templates .');
389
440
  } else {
390
- const total = installedWorkflows.length + installedTemplates.length;
391
- console.log(`Summary: ${installedWorkflows.length}/${WORKFLOWS.length} workflows, ${installedTemplates.length}/${TEMPLATES.length} templates`);
441
+ console.log(
442
+ `Summary: ${installedWorkflows.length}/${WORKFLOWS.length} workflows, ${installedTemplates.length}/${TEMPLATES.length} templates, skill: ${skillInstalled ? 'yes' : 'no'}`
443
+ );
392
444
  }
393
445
  console.log('');
394
446
  }
395
447
 
396
448
  function runUninstall(options) {
397
- const { targetDir = '.', withTemplates = false, dryRun = false } = options;
449
+ const { targetDir = '.', withTemplates = false, withSkill = false, dryRun = false } = options;
398
450
  const targetAbs = path.resolve(process.cwd(), targetDir);
399
451
  const workflowsDest = path.join(targetAbs, '.github', 'workflows');
400
452
  const templatesDest = path.join(targetAbs, '.github', 'ISSUE_TEMPLATE');
@@ -435,6 +487,20 @@ function runUninstall(options) {
435
487
  }
436
488
  }
437
489
 
490
+ let skillRemoved = 0;
491
+ if (withSkill) {
492
+ const skillDest = skillPath(targetAbs);
493
+ if (fs.existsSync(skillDest)) {
494
+ if (dryRun) {
495
+ console.log(` [dry-run] Would remove: ${SKILL_REL_PATH}`);
496
+ } else {
497
+ fs.unlinkSync(skillDest);
498
+ console.log(` Removed: ${SKILL_REL_PATH}`);
499
+ }
500
+ skillRemoved++;
501
+ }
502
+ }
503
+
438
504
  // Remove the version manifest too — it has no meaning once Delivery OS is
439
505
  // gone, and leaving it behind would make a later install/status think a
440
506
  // stale version is still installed.
@@ -449,14 +515,19 @@ function runUninstall(options) {
449
515
  }
450
516
 
451
517
  console.log('');
452
- if (workflowsRemoved > 0 || templatesRemoved > 0) {
518
+ if (workflowsRemoved > 0 || templatesRemoved > 0 || skillRemoved > 0) {
519
+ const templateNote = withTemplates ? `, ${templatesRemoved} template(s)` : '';
520
+ const skillNote = withSkill ? `, ${skillRemoved} skill file(s)` : '';
453
521
  if (dryRun) {
454
- console.log(`Would remove ${workflowsRemoved} workflow(s)${withTemplates ? `, ${templatesRemoved} template(s)` : ''}.`);
522
+ console.log(`Would remove ${workflowsRemoved} workflow(s)${templateNote}${skillNote}.`);
455
523
  } else {
456
- console.log(`Removed ${workflowsRemoved} workflow(s)${withTemplates ? `, ${templatesRemoved} template(s)` : ''}.`);
524
+ console.log(`Removed ${workflowsRemoved} workflow(s)${templateNote}${skillNote}.`);
457
525
  if (!withTemplates) {
458
526
  console.log('Templates were kept. Re-run with --with-templates to remove them.');
459
527
  }
528
+ if (!withSkill) {
529
+ console.log('Claude Code skill (if installed) was kept. Re-run with --with-skill to remove it.');
530
+ }
460
531
  }
461
532
  } else {
462
533
  console.log('No Delivery OS files found to remove.');
@@ -470,5 +541,14 @@ module.exports = {
470
541
  runStatus,
471
542
  runUninstall,
472
543
  // Exposed for tests only — not part of the CLI's public API.
473
- __test__: { manifestPath, readManifest, writeManifest, fetchLatestVersion, WORKFLOWS, TEMPLATES },
544
+ __test__: {
545
+ manifestPath,
546
+ readManifest,
547
+ writeManifest,
548
+ fetchLatestVersion,
549
+ skillPath,
550
+ SKILL_REL_PATH,
551
+ WORKFLOWS,
552
+ TEMPLATES,
553
+ },
474
554
  };
@@ -1,23 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
-
8
- jobs:
9
- test:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: actions/checkout@v6
13
-
14
- - name: Set up Node.js
15
- uses: actions/setup-node@v4
16
- with:
17
- node-version: '20'
18
-
19
- - name: Install dependencies
20
- run: npm ci
21
-
22
- - name: Run tests
23
- run: npm test
@@ -1,46 +0,0 @@
1
- # Deploy docs/ to GitHub Pages
2
- # Requires: Settings → Pages → Source: GitHub Actions
3
-
4
- name: Deploy to GitHub Pages
5
-
6
- on:
7
- push:
8
- branches: [main]
9
- workflow_dispatch:
10
-
11
- permissions:
12
- contents: read
13
- pages: write
14
- id-token: write
15
-
16
- concurrency:
17
- group: pages
18
- cancel-in-progress: false
19
-
20
- env:
21
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
22
-
23
- jobs:
24
- deploy:
25
- runs-on: ubuntu-latest
26
- environment:
27
- name: github-pages
28
- url: ${{ steps.deployment.outputs.page_url }}
29
- steps:
30
- - name: Opt into Node.js 24
31
- run: echo "FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true" >> $GITHUB_ENV
32
-
33
- - name: Checkout
34
- uses: actions/checkout@v6
35
-
36
- - name: Setup Pages
37
- uses: actions/configure-pages@v5
38
-
39
- - name: Upload artifact
40
- uses: actions/upload-pages-artifact@v4
41
- with:
42
- path: ./docs
43
-
44
- - name: Deploy to GitHub Pages
45
- id: deployment
46
- uses: actions/deploy-pages@v4
@@ -1,38 +0,0 @@
1
- # Create GitHub release when a version tag is pushed
2
- # Usage: git tag v1.0.2 && git push origin v1.0.2
3
-
4
- name: Release
5
-
6
- on:
7
- push:
8
- tags:
9
- - 'v*'
10
-
11
- permissions:
12
- contents: write
13
-
14
- jobs:
15
- release:
16
- runs-on: ubuntu-latest
17
- steps:
18
- - uses: actions/checkout@v6
19
- with:
20
- fetch-depth: 0
21
-
22
- - name: Set up Node.js
23
- uses: actions/setup-node@v4
24
- with:
25
- node-version: '20'
26
-
27
- - name: Install dependencies
28
- run: npm ci
29
-
30
- - name: Run tests
31
- run: npm test
32
-
33
- - name: Create Release
34
- uses: softprops/action-gh-release@v2
35
- with:
36
- generate_release_notes: true
37
- env:
38
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}