showdar-skills 0.8.1 → 0.10.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/CHANGELOG.md CHANGED
@@ -4,7 +4,76 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
- ## [0.8.1]
7
+ ## [Unreleased]
8
+
9
+ ## [0.10.0]
10
+
11
+ ### Added
12
+
13
+ - `showdar update-pack <path> --dry-run` read-only snapshot preview using the same canonical planning logic as execution; explicit public projection with deterministic ordering, descriptive change categories (`source-only`, `skill-content`, `workflow-definition`, `profile-definition`, `metadata`, `reference`, `ownership`, `installed-drift`), and `executable` status.
14
+ - Canonical internal pack update planning (`src/pack-plan.js`): `planPackUpdate()`, `verifyPlanPreconditions()`, `executePackUpdate()` with same-lifecycle TOCTOU fingerprint protection (source, installed owned files, relevant manifest state, overrides bytes). Stale plans abort with zero mutation.
15
+ - `showdar inspect-pack <path> --checkpoint <file>` checkpoint compatibility explanation against the candidate effective catalog (current project + candidate pack + project overrides). Deterministic semantic reason codes (`workflow-missing`, `workflow-state-compat-unsupported`, `stage-removed`, `selected-stage-invalid`, `required-stage-conflict`, `skip-policy-invalid`, `recorded-skip-invalid`) with fixed precedence; malformed checkpoints report `schema-invalid` / `malformed-checkpoint`.
16
+ - `showdar list --extensions --json` and `showdar doctor --extensions --json` using the common CLI envelope (`schemaVersion: 1`). Doctor reports `checkpointCompatibility: "not-assessed"` without a checkpoint. Diagnostics exit 0 on successful report.
17
+
18
+ ### Changed
19
+
20
+ - `src/pack-update.js` refactored around canonical planning; v0.9 staged replacement and rollback safety preserved.
21
+ - `list --extensions` human output now shows pack health status and pack profiles.
22
+ - `create-pack` skill scaffold cleaned up (full required sections, substantive guidance, no filler) with validation guidance output.
23
+ - TOCTOU lifecycle clarified: `--dry-run` is a read-only snapshot, not an authorization token. A later `update-pack` re-plans from current state. Stale-plan protection applies within a single execution lifecycle (`planPackUpdate` → `verifyPlanPreconditions` → `executePackUpdate`).
24
+
25
+ ### Fixed
26
+
27
+ - `inspectCustomWorkflows` read `validateCustomWorkflowDoc` as `{ok, errors}` but the validator returns an error array, so `valid` was always `undefined` and doctor reported every workflow invalid. Now maps `errors.length === 0` to `valid`. Internal helper shape only; public CLI output unchanged.
28
+ - `executePackUpdate` referenced undefined `manifestPath`; now resolves the project manifest path before writing. Covered by real update-path regression test.
29
+
30
+ ### Compatibility / Safety
31
+
32
+ - v0.9 `validate-pack --json` (`{ok, errors}`) and plain `inspect-pack --json` shapes unchanged; backward-compat fixture tests added.
33
+ - No persisted preview plans or tokens. No manifest v3, no workflow-state schema change, no WorkflowState key change.
34
+ - Phase 6G authority, 15 primitives, 4 workflows, 6 profiles, 10 trace events frozen.
35
+ - Dry-run is a snapshot, not a stored plan. No migration required. No tarball, remote registry, or executable plugins.
36
+
37
+ ## [0.9.0]
38
+
39
+ ### Added
40
+
41
+ - Pack authoring CLI: `showdar create-pack <path>` scaffolds a minimal valid extension pack with `--vendor`, `--description`, `--with-workflow`, `--with-profile` flags; no interactive wizard.
42
+ - Pack validation CLI: `showdar validate-pack <path> [--json]` performs dry-run validation without installation; deterministic JSON output.
43
+ - Pack inspection CLI: `showdar inspect-pack <path> [--json]` outputs normalized read-only model (source, identity, skills, workflows, profiles, domains, full-tree hash, drift, compatibility, warnings, errors).
44
+ - Pack diagnostics CLI: `showdar doctor --extensions` read-only diagnostics for installed extensions (source drift, ownership, catalog, overrides, profile refs, catalog build failures, collisions).
45
+ - Pack update CLI: `showdar update-pack <local-path>` safe staged replacement with rollback; validates candidate before replacement; preserves overrides/foreign files; refuses unsafe overwrite; no network access.
46
+ - Checkpoint compatibility assessment layer: `assessCheckpointCompatibility(checkpoint, extensionCatalog)` returns ephemeral `{compatible, replanRequired, reason}`; strict deserialization preserved; invalid checkpoints yield `workflow-incompatible` + `replanRequired` without fabricating BLOCKED WorkflowState; malformed checkpoints distinct from policy incompatibility.
47
+ - Structured extension error model: 12 categories (`schema-invalid`, `namespace-invalid`, `collision`, `protected-field`, `unsafe-path`, `source-unavailable`, `source-unsupported`, `ownership-conflict`, `workflow-incompatible`, `profile-reference-invalid`, `override-invalid`, `drift-detected`) with stable codes and human messages; separate from drift.
48
+ - Custom workflow description minimum lowered from 30 to 10 characters.
49
+ - Source drift vs workflow incompatibility separation: source drift (`source-drift`) is hash-based identity change; workflow incompatibility (`workflow-incompatible`) is current-catalog validation failure; never conflated.
50
+ - `showdar doctor --extensions` reports source drift and workflow compatibility separately (`source.drift`, `workflow.compatibility`); `inspect-pack --json` exposes normalized model; `list --extensions` grouped output with drift status.
51
+ - Override precedence inspection: effective value + source (`built-in`/`pack:<name>`/`project-override`) + `protected` flag via `--json` surfaces.
52
+ - Update-pack safety: staged replacement with rollback; validates candidate before replacement; preserves overrides/foreign files; refuses unsafe overwrite; no network access; fails on ownership conflict/missing managed file; workflow removal validation; manifest updated only after successful replacement.
53
+ - Override precedence inspection via `computePrecedence` in `pack-inspect.js`: per-field effective value, source (`built-in`/`pack:<name>`/`project-override`), protected flag.
54
+ - Custom workflow description minimum lowered from 30 to 10 characters (schema + validator).
55
+ - Domain cap remains 8 with improved validation messages; domains remain discovery-only hints.
56
+ - Error model: 12 structured categories with stable machine-readable codes + human messages; `drift-detected` distinct from `workflow-incompatible`.
57
+
58
+ ### Changed
59
+
60
+ - `showdar doctor --extensions` now supports `--extensions` flag for extension diagnostics.
61
+ - `showdar list --extensions` output improved: grouped PACKS/WORKFLOWS/PROFILES/OVERRIDES with drift status.
62
+ - Custom workflow description minimum lowered 30 → 10 (schema + validator).
63
+ - `validate-pack` uses existing canonical validation path; `--json` for machine-readable output.
64
+ - `inspect-pack` outputs deterministic normalized model (human + `--json`).
65
+ - Source drift (`source-drift`) and workflow incompatibility (`workflow-incompatible`) are distinct concepts with separate reporting.
66
+ - `update-pack` warns on workflow definition changes: "Existing checkpoints referencing changed custom workflows will be revalidated on resume."
67
+
68
+ ### Security
69
+
70
+ - `update-pack` refuses unsafe overwrite; validates candidate before replacement; staged replacement with atomic finalization; rollback on failure; overrides and foreign files preserved byte-identical; no network access; no lifecycle scripts/hooks; local directory sources only.
71
+
72
+ ### Fixed
73
+
74
+ - Custom workflow description minimum lowered from 30 to 10 characters.
75
+ - Extension error categories now structured with stable codes and human messages.
76
+ - Source drift and workflow incompatibility are no longer conflated in reporting.
8
77
 
9
78
  ### Fixed
10
79
 
package/README.md CHANGED
@@ -557,7 +557,7 @@ is idempotent, preserves the configured profile, supports `--ai`/`--scope`
557
557
  overrides, and refuses to overwrite a foreign same-name skill directory that
558
558
  Showdar does not own.
559
559
 
560
- ## Extensions (0.8.0)
560
+ ## Extensions (0.9.0)
561
561
 
562
562
  Extension packs are local, static, declarative directories installed from a
563
563
  local directory or workspace-relative path. A pack carries `pack.json`
@@ -565,16 +565,65 @@ metadata (name, version, skills, workflows, pack-local profiles), skill
565
565
  directories, custom workflow definitions, and docs. Packs contain no
566
566
  executable hooks, lifecycle scripts, or remote code.
567
567
 
568
+ **Pack authoring & validation**
569
+
568
570
  ```bash
569
- showdar add-pack ../acme-pack
570
- showdar list --extensions
571
- showdar remove-pack acme
571
+ showdar create-pack <path> [--vendor <v>] [--description <text>] [--with-workflow <id>] [--with-profile <name>]
572
+ showdar validate-pack <local-path> [--json]
573
+ showdar inspect-pack <local-path> [--json] [--checkpoint <file>]
574
+ ```
575
+
576
+ **Install & lifecycle**
577
+
578
+ ```bash
579
+ showdar add-pack <local-path>
580
+ showdar list --extensions [--json]
581
+ showdar remove-pack <name>
582
+ showdar update-pack <local-path> [--dry-run] [--json]
583
+ ```
584
+
585
+ **Diagnostics**
586
+
587
+ ```bash
588
+ showdar doctor --extensions [--json]
589
+ ```
590
+
591
+ `update-pack --dry-run` previews changes without mutation: file add/replace/remove
592
+ counts, descriptive change categories (`source-only`, `skill-content`,
593
+ `workflow-definition`, `profile-definition`, `metadata`, `reference`,
594
+ `ownership`, `installed-drift`), ownership conflicts, and whether the update is
595
+ currently executable. Categories are descriptive only and never claim checkpoint
596
+ compatibility. The preview is a read-only snapshot, not an authorization token:
597
+ a later `update-pack` re-plans from current state. Within a single update
598
+ execution, if source, installed files, manifest, or overrides change between
599
+ planning and applying that same plan, execution aborts as a stale plan with
600
+ zero mutation.
601
+
602
+ `inspect-pack --checkpoint` validates a workflow checkpoint against the candidate
603
+ effective catalog (current project state + candidate pack + project overrides) and
604
+ reports `compatible`, `workflow-incompatible` (with deterministic reason code and
605
+ `replanRequired`), or `malformed`. Malformed checkpoints report `schema-invalid` /
606
+ `malformed-checkpoint`, never `workflow-incompatible`.
607
+
608
+ `list --extensions --json` and `doctor --extensions --json` use the common CLI
609
+ envelope (`schemaVersion: 1`, `command`, `ok`, `data`, `warnings`, `errors`).
610
+ Existing `validate-pack --json` and plain `inspect-pack --json` shapes are
611
+ unchanged. `doctor` without a checkpoint reports `checkpointCompatibility:
612
+ "not-assessed"`. Diagnostics exit 0 when they successfully report state, even when
613
+ unhealthy; validation and execution failures exit 1.
614
+
615
+ **Pack metadata**
616
+
617
+ ```bash
618
+ showdar add-workflow <local-path>
619
+ showdar init --pack <local-path>
572
620
  ```
573
621
 
574
622
  Pack skill IDs use the `vendor/skill` namespace (for example,
575
623
  `acme/lint`); the `showdar-` prefix is reserved for built-ins. Skill
576
624
  `domains` are lowercase kebab-case discovery hints only (at most 8 per
577
625
  skill) — they never create capabilities, routes, or authority.
626
+ Custom workflow description minimum is 10 characters.
578
627
 
579
628
  Custom workflows compose built-in primitive stages under a `vendor-name`
580
629
  ID (for example, `acme-release`). Stages, skip rules, and completion
@@ -599,12 +648,23 @@ Showdar computes a full-tree SHA-256 over the validated pack source at
599
648
  install and records it in `.showdar.json` (`extensions.packs[].hash`).
600
649
  The hash is source-tree identity — a docs-only edit changes it without
601
650
  implying any behavior change. Drift means the source tree differs from
602
- the recorded installation source.
651
+ the recorded installation source. Source drift (`source-drift`) and
652
+ workflow incompatibility (`workflow-incompatible`) are separate concerns.
653
+
654
+ **Checkpoint compatibility**: Custom workflow checkpoints are revalidated
655
+ against the current explicit extension catalog at resume. A valid checkpoint
656
+ resumes normally. A checkpoint with a skip or stage no longer permitted by
657
+ the current workflow definition yields a `workflow-incompatible` outcome
658
+ with `replanRequired=true` — it is never fabricated into a `BLOCKED`
659
+ WorkflowState. Malformed checkpoints remain distinct from workflow
660
+ incompatibility. No pack hash, workflow fingerprint, or catalog snapshot is
661
+ persisted in checkpoints; `schemaVersion` remains 1.
603
662
 
604
663
  Extensions cannot create capabilities, grant authority, modify Phase 6G,
605
664
  change built-in workflow semantics or profiles, or execute arbitrary
606
665
  code. Supported sources are local directories and workspace-relative
607
- paths; tarball, URL, Git, and npm/registry sources are rejected in 0.8.
666
+ paths; tarball, URL, Git, and npm/registry sources are rejected.
667
+ Executable plugins/hooks are not supported.
608
668
 
609
669
  Opt-in custom workflow evaluation (never part of the release gate):
610
670
 
@@ -615,6 +675,22 @@ node scripts/custom-workflows-eval.mjs \
615
675
  --pack .tmp/custom-eval-fixture/acme-pack/pack.json
616
676
  ```
617
677
 
678
+ ## Diagnostics (0.9.0)
679
+
680
+ ```bash
681
+ showdar doctor --extensions
682
+ ```
683
+
684
+ Read-only diagnostics for installed extension state:
685
+ - manifest entries valid, installed files exist, ownership intact
686
+ - source drift (`source-drift`, `source-unavailable`, `installed-file-drift`, `ownership-conflict`)
687
+ - invalid overrides, duplicate/collision, broken profile references
688
+ - catalog construction failures
689
+ - source drift vs workflow incompatibility reported separately
690
+
691
+ Byte-for-byte override preservation is enforced; `.showdar/overrides.json` is
692
+ never rewritten by Showdar during any lifecycle operation.
693
+
618
694
  ## Routing
619
695
 
620
696
  Showdar routes each request through progressive disclosure: the host discovers
@@ -656,6 +732,13 @@ showdar list
656
732
  showdar list --extensions
657
733
  showdar status [--scope <project|global>]
658
734
  showdar doctor [--scope <project|global>]
735
+ showdar doctor --extensions
736
+ showdar validate
737
+ showdar remove [--scope <project|global>]
738
+ showdar create-pack <path> [--vendor <v>] [--description <text>] [--with-workflow <id>] [--with-profile <name>]
739
+ showdar validate-pack <local-path> [--json]
740
+ showdar inspect-pack <local-path> [--json]
741
+ showdar update-pack <local-path>
659
742
  showdar validate
660
743
  showdar remove [--scope <project|global>]
661
744
  ```
package/bin/showdar.js CHANGED
@@ -4,7 +4,9 @@ import { homedir } from 'node:os';
4
4
  import { readFile } from 'node:fs/promises';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { AI_TARGETS, PRIMITIVE_COUNT, PROFILE_ALIASES, PROFILES, SKILLS, TOTAL_COUNT, WORKFLOW_COUNT, canonicalProfile, isDeprecatedProfile, resolveProfile } from '../src/catalog.js';
7
- import { addPack, addSkill, addWorkflow, globalManifestPath, initGlobal, initProject, inspectGlobal, inspectProject, listExtensions, removeGlobal, removePack, removeProject } from '../src/project.js';
7
+ import { addPack, addSkill, addWorkflow, globalManifestPath, initGlobal, initProject, inspectGlobal, inspectProject, listExtensions, listExtensionsDetailed, removeGlobal, removePack, removeProject, validatePackSource, createPack, inspectPack, doctor, updatePack, readProjectOverrides } from '../src/project.js';
8
+ import { formatPlanPreviewHuman, projectPackUpdatePreview } from '../src/pack-plan.js';
9
+ import { assessCheckpointAgainstCandidate } from '../src/pack-inspect.js';
8
10
  import { validateRepository } from '../src/validate.js';
9
11
 
10
12
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
@@ -43,7 +45,7 @@ function printHelp(version, command = null) {
43
45
  console.log(`Showdar Skills ${version}\n\nUsage:\n showdar add <skill> [--ai <universal|codex|opencode|cursor|claude>] [--scope <project|global>]\n\nExamples:\n showdar add debug\n showdar add showdar-security\n showdar add test --ai cursor\n showdar add review --scope global --ai claude\n\nDefault scope: project. Default AI target: universal, or the configured .showdar.json value when present.`);
44
46
  return;
45
47
  }
46
- console.log(`Showdar Skills ${version}\n\nUsage:\n showdar init ${scopeUsage} [--profile <name>] [--ai <universal|codex|opencode|cursor|claude|all>] [--pack <local-path>]\n showdar add <skill> [--ai <universal|codex|opencode|cursor|claude>] [--scope <project|global>]\n showdar add-pack <local-path>\n showdar remove-pack <name>\n showdar add-workflow <local-path>\n showdar status ${scopeUsage}\n showdar doctor ${scopeUsage}\n showdar validate\n showdar list [--extensions]\n showdar remove ${scopeUsage}\n\nExtension packs accept local directories/workspace paths only; tarball, URL, Git, and registry sources are rejected.\n\nDefaults: scope project, profile full, AI target universal.\nProfiles: ${Object.keys(PROFILES).join(', ')}\nDeprecated aliases: ${Object.entries(PROFILE_ALIASES).map(([alias, target]) => `${alias} -> ${target}`).join(', ')}\nAI targets: ${AI_TARGETS.join(', ')}`);
48
+ console.log(`Showdar Skills ${version}\n\nUsage:\n showdar init ${scopeUsage} [--profile <name>] [--ai <universal|codex|opencode|cursor|claude|all>] [--pack <local-path>]\n showdar add <skill> [--ai <universal|codex|opencode|cursor|claude>] [--scope <project|global>]\n showdar add-pack <local-path>\n showdar remove-pack <name>\n showdar add-workflow <local-path>\n showdar status ${scopeUsage}\n showdar doctor ${scopeUsage}\n showdar validate\n showdar list [--extensions]\n showdar remove ${scopeUsage}\n showdar create-pack <path> [--vendor <vendor>] [--description <text>] [--with-workflow <id>] [--with-profile <name>]\n showdar validate-pack <local-path> [--json]\n showdar inspect-pack <local-path> [--json] [--checkpoint <file>]\n showdar doctor ${scopeUsage} [--extensions] [--json]\n showdar update-pack <local-path> [--dry-run] [--json]\n showdar validate\n showdar list [--extensions] [--json]\n showdar remove ${scopeUsage}\n\nExtension packs accept local directories/workspace paths only; tarball, URL, Git, and registry sources are rejected.\n\nDefaults: scope project, profile full, AI target universal.\nProfiles: ${Object.keys(PROFILES).join(', ')}\nDeprecated aliases: ${Object.entries(PROFILE_ALIASES).map(([alias, target]) => `${alias} -> ${target}`).join(', ')}\nAI targets: ${AI_TARGETS.join(', ')}`);
47
49
  }
48
50
 
49
51
  async function main() {
@@ -63,11 +65,20 @@ async function main() {
63
65
 
64
66
  if (command === 'list') {
65
67
  if (args.includes('--extensions')) {
66
- const result = await listExtensions({ cwd: projectRoot });
68
+ const isJson = args.includes('--json');
69
+ const result = await listExtensionsDetailed({ cwd: projectRoot });
70
+ if (isJson) {
71
+ console.log(JSON.stringify({ schemaVersion: 1, command: 'list', ok: true, data: result, warnings: [], errors: [] }, null, 2));
72
+ return;
73
+ }
67
74
  console.log('Packs:');
68
- for (const pack of result.packs) console.log(` ${pack.name}@${pack.version} ${pack.hash}`);
75
+ for (const pack of result.packs) console.log(` ${pack.name}@${pack.version} ${pack.status ?? 'healthy'} ${pack.hash}`);
69
76
  console.log('Custom workflows:');
70
77
  for (const workflow of result.customWorkflows) console.log(` ${workflow.id} [${workflow.source}] ${workflow.path}`);
78
+ if (result.profiles?.length) {
79
+ console.log('Pack profiles:');
80
+ for (const profile of result.profiles) console.log(` ${profile.name} [${profile.source}] members: ${(profile.members ?? []).join(', ')}`);
81
+ }
71
82
  console.log(`Project overrides: ${result.overrides.present ? result.overrides.status : 'absent'}`);
72
83
  return;
73
84
  }
@@ -140,6 +151,31 @@ async function main() {
140
151
  }
141
152
 
142
153
  if (command === 'status' || command === 'doctor') {
154
+ if (command === 'doctor' && args.includes('--extensions')) {
155
+ const isJson = args.includes('--json');
156
+ const result = await doctor({ cwd: projectRoot });
157
+ if (isJson) {
158
+ console.log(JSON.stringify({
159
+ schemaVersion: 1,
160
+ command: 'doctor',
161
+ ok: true,
162
+ data: { ...result, checkpointCompatibility: 'not-assessed' },
163
+ warnings: result.warnings ?? [],
164
+ errors: [],
165
+ }, null, 2));
166
+ return;
167
+ }
168
+ console.log(`Extension diagnostics\nHealth: ${result.healthy ? 'OK' : 'BROKEN'}`);
169
+ for (const pack of result.packs) console.log(` ${pack.name}@${pack.version} ${pack.drift}`);
170
+ for (const wf of result.customWorkflows) console.log(` workflow ${wf.id}: ${wf.valid ? 'valid' : 'invalid'}`);
171
+ for (const issue of result.issues) console.log(`- ${issue}`);
172
+ for (const warning of result.warnings ?? []) console.log(`warning: ${warning}`);
173
+ if (!result.healthy) {
174
+ console.log(`Checkpoint compatibility: not-assessed (supply a checkpoint via inspect-pack --checkpoint)`);
175
+ if (result.customWorkflows.length) console.log(`Note: checkpoints referencing changed workflow definitions will be revalidated on resume.`);
176
+ }
177
+ return;
178
+ }
143
179
  const result = scope === 'global' ? await inspectGlobal() : await inspectProject(projectRoot);
144
180
  if (!result.installed) {
145
181
  console.log(`Showdar Skills is not installed in the ${scope} scope.`);
@@ -180,7 +216,129 @@ async function main() {
180
216
  return;
181
217
  }
182
218
 
183
- throw new Error(`Unknown command "${command}". Run "showdar --help".`);
219
+ if (command === 'create-pack') {
220
+ const packPath = args[1];
221
+ if (!packPath) throw new Error('Pack path is required. Usage: showdar create-pack <path> [--vendor <vendor>] [--description <text>] [--with-workflow <id>] [--with-profile <name>]');
222
+ const vendor = valueAfter(args, '--vendor', 'custom');
223
+ const description = valueAfter(args, '--description', null);
224
+ const withWorkflow = valueAfter(args, '--with-workflow', null);
225
+ const withProfile = valueAfter(args, '--with-profile', null);
226
+ const result = await createPack({ cwd: projectRoot, path: packPath, vendor, description, withWorkflow, withProfile });
227
+ console.log(`Showdar pack scaffolded.\nPack: ${result.manifest.name}\nVersion: ${result.manifest.version}\nSkill: ${result.skillId}${result.workflowId ? `\nWorkflow: ${result.workflowId}` : ''}${result.profileName ? `\nProfile: ${result.profileName}` : ''}\nPath: ${result.packDir}`);
228
+ return;
229
+ }
230
+
231
+ if (command === 'validate-pack') {
232
+ const packPath = args[1];
233
+ if (!packPath) throw new Error('Pack path is required. Usage: showdar validate-pack <local-path> [--json]');
234
+ const isJson = args.includes('--json');
235
+ const result = await validatePackSource({ cwd: projectRoot, source: packPath });
236
+ if (isJson) {
237
+ console.log(JSON.stringify(result, null, 2));
238
+ } else {
239
+ if (result.ok) {
240
+ console.log(`Pack validation OK`);
241
+ } else {
242
+ console.log(`Pack validation FAILED (${result.errors.length} errors).`);
243
+ for (const error of result.errors) console.log(`- ${error}`);
244
+ process.exitCode = 1;
245
+ }
246
+ }
247
+ return;
248
+ }
249
+
250
+ if (command === 'inspect-pack') {
251
+ const packPath = args[1];
252
+ if (!packPath) throw new Error('Pack path is required. Usage: showdar inspect-pack <local-path> [--json] [--checkpoint <file>]');
253
+ const isJson = args.includes('--json');
254
+ const checkpointIdx = args.indexOf('--checkpoint');
255
+ const checkpointFile = checkpointIdx === -1 ? null : args[checkpointIdx + 1];
256
+ if (checkpointIdx !== -1 && (!checkpointFile || checkpointFile.startsWith('--'))) {
257
+ throw new Error('--checkpoint requires a file path');
258
+ }
259
+ if (checkpointFile) {
260
+ const { readFile } = await import('node:fs/promises');
261
+ const checkpoint = await readFile(path.resolve(projectRoot, checkpointFile), 'utf8');
262
+ const assessment = await assessCheckpointAgainstCandidate({ cwd: projectRoot, candidatePath: path.resolve(projectRoot, packPath), checkpoint });
263
+ if (isJson) {
264
+ console.log(JSON.stringify({ schemaVersion: 1, command: 'inspect-pack', ok: true, data: { checkpoint: assessment }, warnings: [], errors: [] }, null, 2));
265
+ return;
266
+ }
267
+ if (assessment.compatible) {
268
+ console.log(`Checkpoint compatibility: compatible\nReplan required: no`);
269
+ return;
270
+ }
271
+ console.log(`Checkpoint compatibility: ${assessment.category === 'schema-invalid' ? 'malformed' : 'workflow-incompatible'}\nReplan required: ${assessment.replanRequired ? 'yes' : 'no'}\nCategory: ${assessment.category}\nReason: ${assessment.reason}\nDetail: ${assessment.detail}`);
272
+ if (assessment.affectedWorkflow) console.log(`Affected workflow: ${assessment.affectedWorkflow}`);
273
+ if (assessment.affectedStage) console.log(`Affected stage: ${assessment.affectedStage}`);
274
+ console.log(`\nRun \`showdar update-pack <path> --dry-run\` to preview update.`);
275
+ return;
276
+ }
277
+ const result = await inspectPack({ cwd: projectRoot, source: packPath });
278
+ if (isJson) {
279
+ console.log(JSON.stringify(result, null, 2));
280
+ } else {
281
+ if (result.ok) {
282
+ console.log(`Pack: ${result.name}@${result.version}`);
283
+ console.log(`Description: ${result.description}`);
284
+ console.log(`Source: ${result.source}`);
285
+ console.log(`Full-tree hash: ${result.fullTreeHash}`);
286
+ console.log(`Skills: ${result.skills.length}`);
287
+ console.log(`Workflows: ${result.workflows.length}`);
288
+ console.log(`Profiles: ${Object.keys(result.profiles).length}`);
289
+ if (result.errors.length > 0) {
290
+ console.log(`Errors: ${result.errors.length}`);
291
+ for (const error of result.errors) console.log(` - ${error}`);
292
+ }
293
+ if (result.warnings.length > 0) {
294
+ console.log(`Warnings: ${result.warnings.length}`);
295
+ for (const warning of result.warnings) console.log(` - ${warning}`);
296
+ }
297
+ } else {
298
+ console.log(`Inspection FAILED (${result.errors.length} errors).`);
299
+ for (const error of result.errors) console.log(`- ${error}`);
300
+ process.exitCode = 1;
301
+ }
302
+ }
303
+ return;
304
+ }
305
+
306
+ if (command === 'update-pack') {
307
+ const packSource = args[1];
308
+ if (!packSource) throw new Error('Pack source is required. Usage: showdar update-pack <local-path> [--dry-run] [--json]');
309
+ const dryRun = args.includes('--dry-run');
310
+ const isJson = args.includes('--json');
311
+ if (dryRun) {
312
+ const { planPackUpdate } = await import('../src/pack-plan.js');
313
+ const plan = await planPackUpdate({ cwd: projectRoot, source: packSource });
314
+ if (isJson) {
315
+ console.log(JSON.stringify(projectPackUpdatePreview(plan), null, 2));
316
+ if (!plan.ok || !plan.executable) process.exitCode = 1;
317
+ return;
318
+ }
319
+ console.log(formatPlanPreviewHuman(plan));
320
+ if (!plan.ok || !plan.executable) process.exitCode = 1;
321
+ return;
322
+ }
323
+ if (isJson) {
324
+ try {
325
+ const result = await updatePack({ cwd: projectRoot, source: packSource });
326
+ console.log(JSON.stringify({ schemaVersion: 1, command: 'update-pack', ok: true, data: result, warnings: result.plan?.warnings ?? [], errors: [] }, null, 2));
327
+ } catch (error) {
328
+ console.log(JSON.stringify({ schemaVersion: 1, command: 'update-pack', ok: false, data: null, warnings: [], errors: [{ category: 'drift-detected', code: 'DRIFT_DETECTED', message: error.message }] }, null, 2));
329
+ process.exitCode = 1;
330
+ }
331
+ return;
332
+ }
333
+ try {
334
+ const result = await updatePack({ cwd: projectRoot, source: packSource });
335
+ console.log(`Showdar pack ${result.status}.\nPack: ${result.pack}\nVersion: ${result.version}${result.oldHash ? `\nOld hash: ${result.oldHash}\nNew hash: ${result.newHash}` : ''}\nFiles: ${result.files}`);
336
+ } catch (error) {
337
+ console.error(`showdar: ${error.message}`);
338
+ process.exitCode = 1;
339
+ }
340
+ return;
341
+ }
184
342
  }
185
343
 
186
344
  main().catch((error) => {
@@ -7,7 +7,7 @@
7
7
  "additionalProperties": false,
8
8
  "properties": {
9
9
  "id": { "type": "string", "pattern": "^[a-z][a-z0-9]*-[a-z][a-z0-9-]*$" },
10
- "description": { "type": "string", "minLength": 30 },
10
+ "description": { "type": "string", "minLength": 10 },
11
11
  "stages": {
12
12
  "type": "array",
13
13
  "minItems": 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "showdar-skills",
3
- "version": "0.8.1",
3
+ "version": "0.10.0",
4
4
  "description": "Production-grade software engineering lifecycle skills for coding agents.",
5
5
  "type": "module",
6
6
  "bin": { "showdar": "./bin/showdar.js" },
@@ -0,0 +1,90 @@
1
+ const EXTENSION_ERROR_CATEGORIES = {
2
+ SCHEMA_INVALID: 'schema-invalid',
3
+ NAMESPACE_INVALID: 'namespace-invalid',
4
+ COLLISION: 'collision',
5
+ PROTECTED_FIELD: 'protected-field',
6
+ UNSAFE_PATH: 'unsafe-path',
7
+ SOURCE_UNAVAILABLE: 'source-unavailable',
8
+ SOURCE_UNSUPPORTED: 'source-unsupported',
9
+ OWNERSHIP_CONFLICT: 'ownership-conflict',
10
+ WORKFLOW_INCOMPATIBLE: 'workflow-incompatible',
11
+ PROFILE_REFERENCE_INVALID: 'profile-reference-invalid',
12
+ OVERRIDE_INVALID: 'override-invalid',
13
+ DRIFT_DETECTED: 'drift-detected',
14
+ };
15
+
16
+ const ERROR_MESSAGES = {
17
+ [EXTENSION_ERROR_CATEGORIES.SCHEMA_INVALID]: 'Schema validation failed',
18
+ [EXTENSION_ERROR_CATEGORIES.NAMESPACE_INVALID]: 'Invalid namespace/identifier',
19
+ [EXTENSION_ERROR_CATEGORIES.COLLISION]: 'Identifier collision detected',
20
+ [EXTENSION_ERROR_CATEGORIES.PROTECTED_FIELD]: 'Protected field modification not allowed',
21
+ [EXTENSION_ERROR_CATEGORIES.UNSAFE_PATH]: 'Unsafe path detected',
22
+ [EXTENSION_ERROR_CATEGORIES.SOURCE_UNAVAILABLE]: 'Source path unavailable',
23
+ [EXTENSION_ERROR_CATEGORIES.SOURCE_UNSUPPORTED]: 'Source type not supported',
24
+ [EXTENSION_ERROR_CATEGORIES.OWNERSHIP_CONFLICT]: 'Ownership conflict with existing managed files',
25
+ [EXTENSION_ERROR_CATEGORIES.WORKFLOW_INCOMPATIBLE]: 'Workflow definition incompatible with checkpoint',
26
+ [EXTENSION_ERROR_CATEGORIES.PROFILE_REFERENCE_INVALID]: 'Invalid profile reference',
27
+ [EXTENSION_ERROR_CATEGORIES.OVERRIDE_INVALID]: 'Invalid project override',
28
+ [EXTENSION_ERROR_CATEGORIES.DRIFT_DETECTED]: 'Source drift detected',
29
+ };
30
+
31
+ export class ExtensionError extends Error {
32
+ constructor(category, message, details = {}) {
33
+ super(message);
34
+ this.name = 'ExtensionError';
35
+ this.category = category;
36
+ this.code = category.toUpperCase().replace(/-/g, '_');
37
+ this.details = details;
38
+ }
39
+ }
40
+
41
+ export function createExtensionError(category, details = {}) {
42
+ const message = details.message ?? ERROR_MESSAGES[category] ?? 'Extension error';
43
+ return new ExtensionError(category, message, details);
44
+ }
45
+
46
+ export function isExtensionError(error) {
47
+ return error instanceof ExtensionError;
48
+ }
49
+
50
+ export function formatErrorForCli(error, verbose = false) {
51
+ if (isExtensionError(error)) {
52
+ const lines = [`[${error.category}] ${error.message}`];
53
+ if (verbose && Object.keys(error.details).length > 0) {
54
+ lines.push('Details:', JSON.stringify(error.details, null, 2));
55
+ }
56
+ return lines.join('\n');
57
+ }
58
+ return error.message;
59
+ }
60
+
61
+ export function formatErrorForJson(error) {
62
+ if (isExtensionError(error)) {
63
+ return {
64
+ category: error.category,
65
+ code: error.code,
66
+ message: error.message,
67
+ details: error.details,
68
+ };
69
+ }
70
+ return {
71
+ category: 'unknown',
72
+ code: 'UNKNOWN',
73
+ message: error.message,
74
+ details: {},
75
+ };
76
+ }
77
+
78
+ export { EXTENSION_ERROR_CATEGORIES, ERROR_MESSAGES };
79
+
80
+ export const extensionErrorsAPI = {
81
+ ExtensionError,
82
+ createExtensionError,
83
+ isExtensionError,
84
+ formatErrorForCli,
85
+ formatErrorForJson,
86
+ categories: EXTENSION_ERROR_CATEGORIES,
87
+ messages: ERROR_MESSAGES,
88
+ };
89
+
90
+ export default extensionErrorsAPI;