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 +70 -1
- package/README.md +89 -6
- package/bin/showdar.js +163 -5
- package/config/custom-workflow.schema.json +1 -1
- package/package.json +1 -1
- package/src/extension-errors.js +90 -0
- package/src/pack-compat.js +197 -0
- package/src/pack-inspect.js +373 -0
- package/src/pack-plan.js +623 -0
- package/src/pack-scaffold.js +335 -0
- package/src/pack-update.js +14 -0
- package/src/project.js +91 -5
- package/src/validate-pack.js +1 -1
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
|
-
## [
|
|
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.
|
|
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
|
|
570
|
-
showdar
|
|
571
|
-
showdar
|
|
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
|
|
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
|
|
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
|
-
|
|
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":
|
|
10
|
+
"description": { "type": "string", "minLength": 10 },
|
|
11
11
|
"stages": {
|
|
12
12
|
"type": "array",
|
|
13
13
|
"minItems": 1,
|
package/package.json
CHANGED
|
@@ -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;
|