showdar-skills 0.2.2 → 0.3.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 +49 -0
- package/MIGRATION.md +52 -0
- package/README.md +80 -16
- package/bin/showdar.js +28 -5
- package/package.json +5 -2
- package/skills/showdar-review/scripts/collect-diff.mjs +9 -2
- package/src/adapters.js +20 -3
- package/src/capabilities.js +93 -0
- package/src/capability-score.js +82 -0
- package/src/catalog.js +14 -1
- package/src/evidence-state.js +526 -0
- package/src/intent-resolver/composition.js +1080 -0
- package/src/intent-resolver/confidence.js +71 -0
- package/src/intent-resolver/constraints.js +138 -0
- package/src/intent-resolver/evidence.js +182 -0
- package/src/intent-resolver/frame/action-frame.js +380 -0
- package/src/intent-resolver/frame/authority/adjudicator.js +104 -0
- package/src/intent-resolver/frame/authority/candidate.js +227 -0
- package/src/intent-resolver/frame/authority/diagnostics.js +58 -0
- package/src/intent-resolver/frame/authority/evidence.js +241 -0
- package/src/intent-resolver/frame/authority/index.js +166 -0
- package/src/intent-resolver/frame/authority/projectors.js +198 -0
- package/src/intent-resolver/frame/authority/relations.js +61 -0
- package/src/intent-resolver/frame/authority/shadow.js +51 -0
- package/src/intent-resolver/frame/authority/types.js +14 -0
- package/src/intent-resolver/frame/clause-frame.js +205 -0
- package/src/intent-resolver/frame/projectors/constraints.js +332 -0
- package/src/intent-resolver/frame/projectors/metadata.js +514 -0
- package/src/intent-resolver/frame/relations.js +242 -0
- package/src/intent-resolver/frame/request-frame.js +187 -0
- package/src/intent-resolver/frame/surface-map.js +450 -0
- package/src/intent-resolver/index.js +476 -0
- package/src/intent-resolver/mutation.js +634 -0
- package/src/intent-resolver/object.js +191 -0
- package/src/intent-resolver/risks.js +202 -0
- package/src/intent-resolver/scoring.js +630 -0
- package/src/intent-resolver/secondary.js +343 -0
- package/src/intent-resolver/segments.js +576 -0
- package/src/intent-resolver/signals.js +435 -0
- package/src/intent-resolver.js +8 -0
- package/src/intent.js +81 -0
- package/src/path-safety.js +39 -0
- package/src/project.js +113 -27
- package/src/route-plan.js +100 -0
- package/src/validate.js +3 -0
- package/src/verification-budget.js +182 -0
- package/src/verification-executor.js +515 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.3.0]
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Explicit harness targets for `showdar init` and `showdar add`: `codex`,
|
|
14
|
+
`opencode`, `cursor`, `claude`, `universal`, and `all`.
|
|
15
|
+
- `showdar add <skill>` for installing a single primitive skill without
|
|
16
|
+
re-running a whole profile (for example `showdar add debug`,
|
|
17
|
+
`showdar add security --ai cursor`,
|
|
18
|
+
`showdar add review --scope global --ai claude`).
|
|
19
|
+
- Native Cursor skill roots (`.cursor/skills` for projects,
|
|
20
|
+
`~/.cursor/skills` for global installs).
|
|
21
|
+
- Deny-by-default routing authority coverage for conditional, modal,
|
|
22
|
+
contextual, hypothetical, and negated actions.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Each harness target now installs into its native skill directory instead of
|
|
27
|
+
sharing one compatibility root.
|
|
28
|
+
- Routing uses a single authoritative engine: current request → structural
|
|
29
|
+
interpretation → authority classification → primary capability → skill.
|
|
30
|
+
- Context, log, and example text no longer becomes requested work on its own;
|
|
31
|
+
conditional and hypothetical actions stay non-authoritative until current
|
|
32
|
+
request semantics permit them.
|
|
33
|
+
- Risk metadata no longer overrides an explicit governing action.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Conditional, modal, context, and negation authority safety cases.
|
|
38
|
+
- Security-risk ownership cases where risk signals previously stole primary
|
|
39
|
+
ownership from the governing action.
|
|
40
|
+
- Debug, upgrade, build, and test imperative recognition for explicit
|
|
41
|
+
governing requests.
|
|
42
|
+
|
|
43
|
+
### Removed
|
|
44
|
+
|
|
45
|
+
- The executable legacy 6F authority engine and the old route-scoring path.
|
|
46
|
+
|
|
47
|
+
## [0.2.3]
|
|
48
|
+
|
|
49
|
+
See git history for changes before the 0.3.0 changelog was started.
|
package/MIGRATION.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Migrating to 0.3.0
|
|
2
|
+
|
|
3
|
+
## Skill install roots
|
|
4
|
+
|
|
5
|
+
`--ai universal` still installs to `.agents/skills` (project) and
|
|
6
|
+
`~/.agents/skills` (global). Explicit harness targets now use their native
|
|
7
|
+
roots:
|
|
8
|
+
|
|
9
|
+
- OpenCode: `.opencode/skills` / `~/.config/opencode/skills`
|
|
10
|
+
- Cursor: `.cursor/skills` / `~/.cursor/skills`
|
|
11
|
+
- Claude Code: `.claude/skills` / `~/.claude/skills`
|
|
12
|
+
- Codex: `.agents/skills` / `~/.agents/skills`
|
|
13
|
+
|
|
14
|
+
One invocation installs to one resolved root only; no compatibility copies
|
|
15
|
+
are made automatically.
|
|
16
|
+
|
|
17
|
+
## Existing projects
|
|
18
|
+
|
|
19
|
+
Existing `.showdar.json` v2 configs remain valid. No config-version migration
|
|
20
|
+
is required. Re-running `showdar init` with an explicit `--ai` target moves
|
|
21
|
+
managed skills to the newly requested native root; the previous root is not
|
|
22
|
+
silently deleted unless existing Showdar stale-cleanup semantics apply.
|
|
23
|
+
|
|
24
|
+
## Profiles
|
|
25
|
+
|
|
26
|
+
Profile names and composition are unchanged:
|
|
27
|
+
|
|
28
|
+
- `minimal` (8), `developer` (12), `backend` (14), `qa` (9), `product` (6),
|
|
29
|
+
`full` (15).
|
|
30
|
+
- `mobile` and `web` remain accepted as deprecated aliases for `developer`;
|
|
31
|
+
manifests store the canonical name.
|
|
32
|
+
|
|
33
|
+
## Single-skill additions
|
|
34
|
+
|
|
35
|
+
Use `showdar add <skill>` instead of re-running a larger profile to add one
|
|
36
|
+
skill:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
showdar add debug
|
|
40
|
+
showdar add security --ai cursor
|
|
41
|
+
showdar add review --scope global --ai claude
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Additions preserve the configured profile and are idempotent.
|
|
45
|
+
|
|
46
|
+
## Routing behavior
|
|
47
|
+
|
|
48
|
+
0.3.0 uses stricter deny-by-default authority semantics. Conditional,
|
|
49
|
+
hypothetical, contextual, and negated actions stay non-authoritative until
|
|
50
|
+
current request semantics permit them, and risk metadata no longer overrides
|
|
51
|
+
an explicit governing action. Do not assume byte-identical routing with older
|
|
52
|
+
releases where behavior intentionally tightened.
|
package/README.md
CHANGED
|
@@ -17,10 +17,16 @@ Install the CLI, then install a role-oriented skill profile into your project:
|
|
|
17
17
|
```bash
|
|
18
18
|
npm install -g showdar-skills
|
|
19
19
|
cd my-project
|
|
20
|
-
showdar init
|
|
20
|
+
showdar init
|
|
21
21
|
showdar doctor
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
```bash
|
|
25
|
+
showdar init --ai cursor
|
|
26
|
+
showdar init --ai claude --scope global
|
|
27
|
+
showdar init --profile developer --ai opencode
|
|
28
|
+
```
|
|
29
|
+
|
|
24
30
|
To install from source instead:
|
|
25
31
|
|
|
26
32
|
```bash
|
|
@@ -29,9 +35,10 @@ cd showdar-skills
|
|
|
29
35
|
npm install -g .
|
|
30
36
|
```
|
|
31
37
|
|
|
32
|
-
Showdar works with Codex, OpenCode,
|
|
33
|
-
|
|
34
|
-
more precise context; use `full` when
|
|
38
|
+
Showdar works with Universal Agent Skills, Codex, OpenCode, Cursor, and
|
|
39
|
+
Claude Code as supported installation targets. Choose `backend`, `qa`, or
|
|
40
|
+
`product` when that gives discovery a more precise context; use `full` when
|
|
41
|
+
you want all capabilities available.
|
|
35
42
|
|
|
36
43
|
## Why Showdar?
|
|
37
44
|
|
|
@@ -71,15 +78,23 @@ knowledge progressively.
|
|
|
71
78
|
|
|
72
79
|
## Supported agents
|
|
73
80
|
|
|
74
|
-
|
|
|
75
|
-
| --- | --- | --- |
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
| OpenCode
|
|
79
|
-
|
|
|
81
|
+
| Harness | Project path | Global path | Status |
|
|
82
|
+
| --- | --- | --- | --- |
|
|
83
|
+
| Universal Agent Skills | `.agents/skills/` | `~/.agents/skills/` | Supported installation target |
|
|
84
|
+
| Codex | `.agents/skills/` | `~/.agents/skills/` | Supported installation target |
|
|
85
|
+
| OpenCode | `.opencode/skills/` | `~/.config/opencode/skills/` | Supported installation target |
|
|
86
|
+
| Cursor | `.cursor/skills/` | `~/.cursor/skills/` | Supported installation target |
|
|
87
|
+
| Claude Code | `.claude/skills/` | `~/.claude/skills/` | Supported installation target |
|
|
88
|
+
|
|
89
|
+
Universal uses `.agents/skills/`. Explicit harness targets use their native
|
|
90
|
+
skill directories. Codex and Universal intentionally share `.agents/skills/`.
|
|
91
|
+
OpenCode additionally receives native `/showdar/...` command files in
|
|
92
|
+
`.opencode/commands/showdar/` (project) and
|
|
93
|
+
`~/.config/opencode/commands/showdar/` (global).
|
|
80
94
|
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
"Supported installation target" means skills install to the harness-native
|
|
96
|
+
directory. It does not promise identical implicit invocation, cloud,
|
|
97
|
+
agent/subagent, or MCP behavior across harnesses.
|
|
83
98
|
|
|
84
99
|
## Project and global installation
|
|
85
100
|
|
|
@@ -238,10 +253,57 @@ OpenCode exposes native commands after initialization with `--ai opencode` or
|
|
|
238
253
|
| `showdar-security` | Performs defensive, evidence-based analysis and never exposes secret values. |
|
|
239
254
|
| `showdar-requirements` | Records assumptions and open decisions instead of inventing business decisions. |
|
|
240
255
|
|
|
256
|
+
## Adding a single skill
|
|
257
|
+
|
|
258
|
+
Install one primitive skill without re-running a whole profile:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
showdar add debug
|
|
262
|
+
showdar add showdar-security
|
|
263
|
+
showdar add test --ai cursor
|
|
264
|
+
showdar add review --scope global --ai claude
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Accepted names are the short form (`debug`) or the canonical form
|
|
268
|
+
(`showdar-debug`). The release ships exactly 15 primitive skills. `showdar add`
|
|
269
|
+
is idempotent, preserves the configured profile, supports `--ai`/`--scope`
|
|
270
|
+
overrides, and refuses to overwrite a foreign same-name skill directory that
|
|
271
|
+
Showdar does not own.
|
|
272
|
+
|
|
273
|
+
## Routing
|
|
274
|
+
|
|
275
|
+
Showdar routes each request through progressive disclosure: the host discovers
|
|
276
|
+
lightweight skill metadata, loads the relevant skill, and pulls deeper
|
|
277
|
+
guides and data only when needed.
|
|
278
|
+
|
|
279
|
+
```text
|
|
280
|
+
current request
|
|
281
|
+
|
|
|
282
|
+
v
|
|
283
|
+
structural interpretation
|
|
284
|
+
|
|
|
285
|
+
v
|
|
286
|
+
authority classification
|
|
287
|
+
|
|
|
288
|
+
v
|
|
289
|
+
primary capability
|
|
290
|
+
|
|
|
291
|
+
v
|
|
292
|
+
skill
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Product behavior notes:
|
|
296
|
+
|
|
297
|
+
- Context, log, and example text does not automatically become requested work.
|
|
298
|
+
- Conditional and hypothetical actions remain non-authoritative until current
|
|
299
|
+
request semantics permit them.
|
|
300
|
+
- Risk metadata does not override an explicit governing action.
|
|
301
|
+
|
|
241
302
|
## CLI reference
|
|
242
303
|
|
|
243
304
|
```bash
|
|
244
305
|
showdar init [--scope <project|global>] --ai <target> --profile <profile>
|
|
306
|
+
showdar add <skill> [--ai <target>] [--scope <project|global>]
|
|
245
307
|
showdar list
|
|
246
308
|
showdar status [--scope <project|global>]
|
|
247
309
|
showdar doctor [--scope <project|global>]
|
|
@@ -249,10 +311,12 @@ showdar validate
|
|
|
249
311
|
showdar remove [--scope <project|global>]
|
|
250
312
|
```
|
|
251
313
|
|
|
252
|
-
Main flags are `--ai`, `--profile`, and `--scope`. `--ai` accepts `
|
|
253
|
-
`opencode`, `
|
|
254
|
-
`--
|
|
255
|
-
|
|
314
|
+
Main flags are `--ai`, `--profile`, and `--scope`. `--ai` accepts `universal`,
|
|
315
|
+
`codex`, `opencode`, `cursor`, `claude`, or `all` for `init` (single targets
|
|
316
|
+
for `add`). `--scope` accepts `project` or `global` and defaults to `project`;
|
|
317
|
+
`--profile` accepts the six canonical profiles and the deprecated
|
|
318
|
+
`mobile`/`web` aliases. Run `showdar --help` or a command's `--help` for
|
|
319
|
+
current options.
|
|
256
320
|
|
|
257
321
|
`showdar validate` validates the installed Showdar package. `showdar doctor`
|
|
258
322
|
checks managed files against ownership hashes, while `showdar remove` removes
|
package/bin/showdar.js
CHANGED
|
@@ -4,7 +4,7 @@ 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, PROFILE_ALIASES, PROFILES, SKILLS, canonicalProfile, isDeprecatedProfile, resolveProfile } from '../src/catalog.js';
|
|
7
|
-
import { globalManifestPath, initGlobal, initProject, inspectGlobal, inspectProject, removeGlobal, removeProject } from '../src/project.js';
|
|
7
|
+
import { addSkill, globalManifestPath, initGlobal, initProject, inspectGlobal, inspectProject, removeGlobal, removeProject } from '../src/project.js';
|
|
8
8
|
import { validateRepository } from '../src/validate.js';
|
|
9
9
|
|
|
10
10
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
@@ -32,14 +32,18 @@ function scopeAfter(args) {
|
|
|
32
32
|
function printHelp(version, command = null) {
|
|
33
33
|
const scopeUsage = '[--scope <project|global>]';
|
|
34
34
|
if (command === 'init') {
|
|
35
|
-
console.log(`Showdar Skills ${version}\n\nUsage:\n showdar init ${scopeUsage} [--profile <name>] [--ai <codex|opencode|claude|
|
|
35
|
+
console.log(`Showdar Skills ${version}\n\nUsage:\n showdar init ${scopeUsage} [--profile <name>] [--ai <universal|codex|opencode|cursor|claude|all>]\n\nDefaults: scope project, profile full, AI target universal.\nProject scope writes native skills and project .showdar.json. Global scope writes verified user skill directories and ~/.showdar/global.json without project files. Codex and universal use .agents/skills in project scope and ~/.agents/skills in global scope; cursor uses .cursor/skills in project scope and ~/.cursor/skills in global scope; --ai all writes each shared destination once.\n\nProfiles: ${Object.keys(PROFILES).join(', ')}\nDeprecated aliases: ${Object.entries(PROFILE_ALIASES).map(([alias, target]) => `${alias} -> ${target}`).join(', ')}\nAI targets: ${AI_TARGETS.join(', ')}`);
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
if (['status', 'doctor', 'remove'].includes(command)) {
|
|
39
39
|
console.log(`Showdar Skills ${version}\n\nUsage:\n showdar ${command} ${scopeUsage}\n\nDefault scope: project. Use --scope global for the user installation.`);
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
if (command === 'add') {
|
|
43
|
+
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
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.log(`Showdar Skills ${version}\n\nUsage:\n showdar init ${scopeUsage} [--profile <name>] [--ai <universal|codex|opencode|cursor|claude|all>]\n showdar add <skill> [--ai <universal|codex|opencode|cursor|claude>] [--scope <project|global>]\n showdar status ${scopeUsage}\n showdar doctor ${scopeUsage}\n showdar validate\n showdar list\n showdar remove ${scopeUsage}\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(', ')}`);
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
async function main() {
|
|
@@ -55,7 +59,7 @@ async function main() {
|
|
|
55
59
|
}
|
|
56
60
|
if (args.includes('--help') || args.includes('-h')) return printHelp(version, command);
|
|
57
61
|
|
|
58
|
-
const scope = ['init', 'status', 'doctor', 'remove'].includes(command) ? scopeAfter(args) : null;
|
|
62
|
+
const scope = ['init', 'status', 'doctor', 'remove', 'add'].includes(command) ? scopeAfter(args) : null;
|
|
59
63
|
|
|
60
64
|
if (command === 'list') {
|
|
61
65
|
console.log(`Profiles: ${Object.keys(PROFILES).join(', ')}\nDeprecated aliases: ${Object.entries(PROFILE_ALIASES).map(([alias, target]) => `${alias} -> ${target}`).join(', ')}\n\nSkills:`);
|
|
@@ -76,7 +80,7 @@ async function main() {
|
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
if (command === 'init') {
|
|
79
|
-
if (args.includes('--agent')) throw new Error('--agent is no longer supported in V0.2. Use --ai <codex|opencode|claude|
|
|
83
|
+
if (args.includes('--agent')) throw new Error('--agent is no longer supported in V0.2. Use --ai <universal|codex|opencode|cursor|claude|all>.');
|
|
80
84
|
const requestedProfile = valueAfter(args, '--profile', 'full');
|
|
81
85
|
const profile = canonicalProfile(requestedProfile);
|
|
82
86
|
const ai = valueAfter(args, '--ai', 'universal');
|
|
@@ -112,6 +116,25 @@ async function main() {
|
|
|
112
116
|
return;
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
if (command === 'add') {
|
|
120
|
+
const positional = args.filter((a, i) => i > 0 && !a.startsWith('--') && args[i - 1] !== '--ai' && args[i - 1] !== '--scope');
|
|
121
|
+
const skillArg = positional[0];
|
|
122
|
+
if (!skillArg) throw new Error('Skill name is required. Usage: showdar add <skill> [--ai <target>] [--scope <project|global>]');
|
|
123
|
+
const hasAiFlag = args.includes('--ai');
|
|
124
|
+
const hasScopeFlag = args.includes('--scope');
|
|
125
|
+
const result = await addSkill({
|
|
126
|
+
cwd: projectRoot,
|
|
127
|
+
skill: skillArg,
|
|
128
|
+
ai: hasAiFlag ? valueAfter(args, '--ai', 'universal') : null,
|
|
129
|
+
scope: hasScopeFlag ? scope : null,
|
|
130
|
+
home: homedir(),
|
|
131
|
+
packageRoot,
|
|
132
|
+
packageVersion: version,
|
|
133
|
+
});
|
|
134
|
+
console.log(`Showdar skill ${result.added ? 'added' : 'already installed'}.\nSkill: ${result.skill}\nScope: ${result.scope}\nAI: ${result.ai}\nPath: ${result.destination}`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
115
138
|
if (command === 'remove') {
|
|
116
139
|
if (scope === 'global') await removeGlobal();
|
|
117
140
|
else await removeProject(projectRoot);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "showdar-skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Production-grade software engineering lifecycle skills for coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": { "showdar": "./bin/showdar.js" },
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"release:check": "node scripts/check-release-version.mjs"
|
|
15
15
|
},
|
|
16
16
|
"engines": { "node": ">=20" },
|
|
17
|
-
"files": ["bin", "src", "skills", "commands", "router", "bundles", "profiles", "engine", "README.md", "LICENSE"],
|
|
17
|
+
"files": ["bin", "src", "skills", "commands", "router", "bundles", "profiles", "engine", "README.md", "LICENSE", "CHANGELOG.md", "MIGRATION.md"],
|
|
18
18
|
"keywords": ["agent-skills", "coding-agents", "codex", "opencode", "claude-code", "software-engineering", "developer-tools", "requirements", "qa", "security", "devops", "workflow"],
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"repository": {
|
|
@@ -24,5 +24,8 @@
|
|
|
24
24
|
"homepage": "https://github.com/caongocquy/showdar-skills#readme",
|
|
25
25
|
"bugs": {
|
|
26
26
|
"url": "https://github.com/caongocquy/showdar-skills/issues"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"ajv": "^8.20.0"
|
|
27
30
|
}
|
|
28
31
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
3
|
const cwd=process.argv[2]??process.cwd(); const base=process.argv[3]??'HEAD';
|
|
4
|
-
const run=
|
|
5
|
-
|
|
4
|
+
const run=(args)=>{const r=spawnSync('git',args,{cwd,encoding:'utf8',shell:false});if(r.error)throw r.error;if(r.status!==0)throw new Error((r.stderr||r.stdout).trim()||`git ${args.join(' ')} failed`);return r.stdout.trim()};
|
|
5
|
+
try {
|
|
6
|
+
if (base.startsWith('-')) throw new Error('revision must not begin with "-"');
|
|
7
|
+
const resolved=run(['rev-parse','--verify',`${base}^{commit}`]);
|
|
8
|
+
console.log(JSON.stringify({base,status:run(['status','--short']),files:run(['diff','--no-ext-diff','--name-only',resolved])?.split('\n').filter(Boolean)??[],stat:run(['diff','--no-ext-diff','--stat',resolved]),diff:run(['diff','--no-ext-diff','--unified=3',resolved])},null,2));
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.error(`collect-diff: ${error.message}`);
|
|
11
|
+
process.exitCode=1;
|
|
12
|
+
}
|
package/src/adapters.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
|
|
4
|
-
export const NATIVE_TARGETS = ['codex', 'opencode', 'claude', 'universal'];
|
|
4
|
+
export const NATIVE_TARGETS = ['codex', 'opencode', 'cursor', 'claude', 'universal'];
|
|
5
5
|
|
|
6
6
|
const ROOTS = {
|
|
7
7
|
codex: ['.agents', 'skills'],
|
|
8
8
|
opencode: ['.opencode', 'skills'],
|
|
9
|
+
cursor: ['.cursor', 'skills'],
|
|
9
10
|
claude: ['.claude', 'skills'],
|
|
10
11
|
universal: ['.agents', 'skills'],
|
|
11
12
|
};
|
|
@@ -13,13 +14,14 @@ const ROOTS = {
|
|
|
13
14
|
const GLOBAL_ROOTS = {
|
|
14
15
|
codex: ({ homeRoot }) => path.join(homeRoot, '.agents', 'skills'),
|
|
15
16
|
opencode: ({ homeRoot }) => path.join(homeRoot, '.config', 'opencode', 'skills'),
|
|
17
|
+
cursor: ({ homeRoot }) => path.join(homeRoot, '.cursor', 'skills'),
|
|
16
18
|
claude: ({ homeRoot }) => path.join(homeRoot, '.claude', 'skills'),
|
|
17
19
|
universal: ({ homeRoot }) => path.join(homeRoot, '.agents', 'skills'),
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export function resolveTargets(ai) {
|
|
21
23
|
if (ai === 'all') return [...NATIVE_TARGETS];
|
|
22
|
-
if (!NATIVE_TARGETS.includes(ai)) throw new Error(`Unknown AI target "${ai}". Expected codex, opencode, claude, universal, or all.`);
|
|
24
|
+
if (!NATIVE_TARGETS.includes(ai)) throw new Error(`Unknown AI target "${ai}". Expected codex, opencode, cursor, claude, universal, or all.`);
|
|
23
25
|
return [ai];
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -35,10 +37,25 @@ export function globalSkillRootFor(target, { homeRoot = homedir() } = {}) {
|
|
|
35
37
|
return resolve({ homeRoot });
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
export function resolveSkillRoot({ ai, scope, cwd, home = homedir() }) {
|
|
41
|
+
if (!NATIVE_TARGETS.includes(ai)) throw new Error(`Unknown AI target "${ai}".`);
|
|
42
|
+
if (scope !== 'project' && scope !== 'global') throw new Error(`Unknown scope "${scope}". Expected project or global.`);
|
|
43
|
+
if (scope === 'project') return skillRootFor(ai, cwd);
|
|
44
|
+
return globalSkillRootFor(ai, { homeRoot: home });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const COMPATIBILITY_MATRIX = {
|
|
48
|
+
codex: { projectNative: true, globalNative: true },
|
|
49
|
+
opencode: { projectNative: true, globalNative: true },
|
|
50
|
+
cursor: { projectNative: true, globalNative: true },
|
|
51
|
+
claude: { projectNative: true, globalNative: true },
|
|
52
|
+
universal: { project: true, global: true },
|
|
53
|
+
};
|
|
54
|
+
|
|
38
55
|
export function opencodeCommandRoot(projectRoot) {
|
|
39
56
|
return path.join(projectRoot, '.opencode', 'commands', 'showdar');
|
|
40
57
|
}
|
|
41
58
|
|
|
42
59
|
export function globalCommandRootFor({ homeRoot = homedir() } = {}) {
|
|
43
60
|
return path.join(homeRoot, '.config', 'opencode', 'commands', 'showdar');
|
|
44
|
-
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { SKILLS } from './catalog.js';
|
|
2
|
+
import { EVIDENCE_KEYS, INTENT_PHASES, MUTATION_CLASSES, RISK_CAPABILITIES } from './intent.js';
|
|
3
|
+
|
|
4
|
+
const readOnly = ['read-only'];
|
|
5
|
+
const localWrite = ['local-write'];
|
|
6
|
+
const readAndLocalWrite = ['read-only', 'local-write'];
|
|
7
|
+
|
|
8
|
+
// This array is the canonical capability source; SKILL.md remains the human-facing source.
|
|
9
|
+
export const CAPABILITIES = Object.freeze([
|
|
10
|
+
{ skill: 'showdar-understand', phases: ['discovery', 'planning'], actions: ['understand', 'investigate', 'assess'], objects: ['repository', 'architecture', 'dependency', 'api', 'data', 'runtime'], risks: [], mutations: readOnly },
|
|
11
|
+
{ skill: 'showdar-requirements', phases: ['definition'], actions: ['define', 'assess', 'review'], objects: ['repository', 'backend', 'api', 'ui', 'data'], risks: [], mutations: readAndLocalWrite, evidence: { prefer: { behaviorDefined: false } } },
|
|
12
|
+
{ skill: 'showdar-plan', phases: ['planning'], actions: ['plan', 'define', 'review'], objects: ['repository', 'architecture', 'backend', 'api', 'ui', 'data'], risks: [], mutations: readAndLocalWrite },
|
|
13
|
+
{ skill: 'showdar-design', phases: ['design'], actions: ['design', 'review', 'modify'], objects: ['ui', 'frontend', 'mobile'], risks: [], mutations: readAndLocalWrite },
|
|
14
|
+
{ skill: 'showdar-build', phases: ['implementation'], actions: ['implement', 'modify', 'fix'], objects: ['repository', 'ui', 'backend', 'api', 'data', 'runtime'], risks: ['regression', 'performance', 'data-integrity'], mutations: localWrite, evidence: { prefer: { behaviorDefined: true, rootCauseKnown: true } } },
|
|
15
|
+
{ skill: 'showdar-debug', phases: ['diagnosis'], actions: ['investigate', 'reproduce', 'isolate', 'fix'], objects: ['runtime', 'build', 'network', 'state', 'auth', 'api'], risks: ['regression', 'performance', 'data-integrity'], mutations: readAndLocalWrite, evidence: { prefer: { failureObserved: true, rootCauseKnown: false }, deEmphasize: { rootCauseKnown: true } } },
|
|
16
|
+
{ skill: 'showdar-test', phases: ['verification'], actions: ['test', 'review'], objects: ['repository', 'backend', 'api', 'ui', 'runtime', 'data'], risks: ['regression', 'compatibility', 'performance'], mutations: readAndLocalWrite },
|
|
17
|
+
{ skill: 'showdar-quality', phases: ['verification'], actions: ['assess', 'review', 'test'], objects: ['repository', 'backend', 'api', 'ui', 'runtime'], risks: ['regression', 'compatibility'], mutations: readOnly },
|
|
18
|
+
{ skill: 'showdar-review', phases: ['discovery', 'verification'], actions: ['review', 'assess'], objects: ['repository', 'architecture', 'code', 'api', 'data'], risks: ['regression'], mutations: readOnly },
|
|
19
|
+
{ skill: 'showdar-security', phases: ['discovery', 'verification'], actions: ['assess', 'review'], objects: ['auth', 'secrets', 'trust-boundary', 'api', 'data'], risks: ['security'], mutations: readOnly },
|
|
20
|
+
{ skill: 'showdar-upgrade', phases: ['implementation'], actions: ['upgrade', 'modify', 'test'], objects: ['dependency', 'runtime', 'backend', 'api', 'repository'], risks: ['compatibility', 'regression'], mutations: localWrite },
|
|
21
|
+
{ skill: 'showdar-ship', phases: ['delivery', 'verification'], actions: ['release', 'review', 'assess'], objects: ['release', 'package', 'repository', 'deployment', 'runtime'], risks: ['production', 'compatibility'], mutations: readOnly },
|
|
22
|
+
{ skill: 'showdar-ops', phases: ['operations'], actions: ['deploy', 'modify', 'assess', 'review'], objects: ['deployment', 'container', 'ci', 'runtime', 'network'], risks: ['operations', 'production'], mutations: ['local-write', 'remote-write', 'production-impacting'] },
|
|
23
|
+
{ skill: 'showdar-recover', phases: ['recovery'], actions: ['recover', 'investigate'], objects: ['repository', 'implementation', 'runtime', 'state'], risks: ['data-integrity', 'regression'], mutations: readAndLocalWrite },
|
|
24
|
+
{ skill: 'showdar-git', phases: ['repository'], actions: ['git', 'modify', 'review'], objects: ['repository', 'branch', 'commit'], risks: ['data-integrity', 'operations'], mutations: ['read-only', 'local-write', 'remote-write'] },
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const listFields = ['phases', 'actions', 'objects', 'risks', 'mutations'];
|
|
28
|
+
const capabilityFields = new Set(['skill', ...listFields, 'evidence']);
|
|
29
|
+
const evidenceSections = ['prefer', 'deEmphasize'];
|
|
30
|
+
|
|
31
|
+
function isRecord(value) {
|
|
32
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function validateCapabilities(definitions = CAPABILITIES, skills = SKILLS) {
|
|
36
|
+
const errors = [];
|
|
37
|
+
if (!Array.isArray(definitions)) return { ok: false, errors: ['capabilities must be an array'] };
|
|
38
|
+
const expectedSkills = new Set(skills.map(({ id }) => id));
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
for (const definition of definitions) {
|
|
41
|
+
if (!definition || typeof definition !== 'object' || Array.isArray(definition)) {
|
|
42
|
+
errors.push('capability definition must be an object');
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const skill = definition.skill;
|
|
46
|
+
if (typeof skill !== 'string' || !skill.trim()) {
|
|
47
|
+
errors.push('capability skill must be a non-empty string');
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
for (const field of Object.keys(definition)) if (!capabilityFields.has(field)) errors.push(`capability ${skill} contains unknown field: ${field}`);
|
|
51
|
+
if (!expectedSkills.has(skill)) errors.push(`capability entry for nonexistent skill: ${skill}`);
|
|
52
|
+
if (seen.has(skill)) errors.push(`capability entries contain duplicates: ${skill}`);
|
|
53
|
+
seen.add(skill);
|
|
54
|
+
for (const field of listFields) {
|
|
55
|
+
const values = definition[field];
|
|
56
|
+
if (values === undefined) continue;
|
|
57
|
+
if (!Array.isArray(values)) {
|
|
58
|
+
errors.push(`capability ${skill} ${field} must be an array`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (values.some((value) => typeof value !== 'string' || !value.trim())) errors.push(`capability ${skill} ${field} must contain non-empty strings`);
|
|
62
|
+
if (new Set(values).size !== values.length) errors.push(`capability ${skill} ${field} contains duplicates`);
|
|
63
|
+
const known = field === 'phases' ? new Set(INTENT_PHASES) : field === 'risks' ? new Set(RISK_CAPABILITIES) : field === 'mutations' ? new Set(MUTATION_CLASSES) : null;
|
|
64
|
+
if (known) for (const value of values) if (typeof value === 'string' && !known.has(value)) errors.push(`capability ${skill} has invalid ${field.slice(0, -1)}: ${value}`);
|
|
65
|
+
}
|
|
66
|
+
if (definition.evidence !== undefined) {
|
|
67
|
+
if (!isRecord(definition.evidence)) errors.push(`capability ${skill} evidence must be an object`);
|
|
68
|
+
else {
|
|
69
|
+
for (const section of Object.keys(definition.evidence)) {
|
|
70
|
+
if (!evidenceSections.includes(section)) {
|
|
71
|
+
errors.push(`capability ${skill} evidence has unknown section: ${section}`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const preferences = definition.evidence[section];
|
|
75
|
+
if (!isRecord(preferences)) {
|
|
76
|
+
errors.push(`capability ${skill} evidence.${section} must be an object`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
for (const key of Object.keys(preferences)) {
|
|
80
|
+
if (!EVIDENCE_KEYS.includes(key)) errors.push(`capability ${skill} evidence.${section} has unknown key: ${key}`);
|
|
81
|
+
else if (typeof preferences[key] !== 'boolean') errors.push(`capability ${skill} evidence.${section}.${key} must be boolean`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
for (const skill of expectedSkills) if (!seen.has(skill)) errors.push(`missing capability entry for catalog skill: ${skill}`);
|
|
88
|
+
return { ok: errors.length === 0, errors };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getCapability(skill) {
|
|
92
|
+
return CAPABILITIES.find((definition) => definition.skill === skill) ?? null;
|
|
93
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { CAPABILITIES, getCapability } from './capabilities.js';
|
|
2
|
+
import { EVIDENCE_KEYS, normalizeIntent } from './intent.js';
|
|
3
|
+
|
|
4
|
+
export const CAPABILITY_SCORE_WEIGHTS = Object.freeze({
|
|
5
|
+
phase: 4,
|
|
6
|
+
action: 3,
|
|
7
|
+
object: 2,
|
|
8
|
+
risk: 2,
|
|
9
|
+
mutation: 2,
|
|
10
|
+
evidence: 2,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
function addUnique(list, value) {
|
|
14
|
+
if (!list.includes(value)) list.push(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function addMatch(matched, reasons, dimension, value, weight) {
|
|
18
|
+
addUnique(matched, `${dimension}:${value}`);
|
|
19
|
+
reasons.push(`matched ${dimension} ${value} (+${weight})`);
|
|
20
|
+
return weight;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function addMiss(unmatched, reasons, dimension, value, supported) {
|
|
24
|
+
addUnique(unmatched, `${dimension}:${value}`);
|
|
25
|
+
reasons.push(`unmatched ${dimension} ${value} (supported: ${supported.join(', ') || 'none'})`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function scoreIntent(intentInput, capability) {
|
|
29
|
+
const intent = normalizeIntent(intentInput);
|
|
30
|
+
const definition = typeof capability === 'string' ? getCapability(capability) : capability;
|
|
31
|
+
if (!definition?.skill) throw new Error('Capability with a skill id is required');
|
|
32
|
+
|
|
33
|
+
const matched = [];
|
|
34
|
+
const unmatched = [];
|
|
35
|
+
const reasons = [];
|
|
36
|
+
let score = 0;
|
|
37
|
+
// ponytail: exact labels only; add aliasing when a future router needs it.
|
|
38
|
+
const dimensions = [
|
|
39
|
+
['phase', intent.phase, definition.phases ?? [], CAPABILITY_SCORE_WEIGHTS.phase],
|
|
40
|
+
['action', intent.action, definition.actions ?? [], CAPABILITY_SCORE_WEIGHTS.action],
|
|
41
|
+
['object', intent.object, definition.objects ?? [], CAPABILITY_SCORE_WEIGHTS.object],
|
|
42
|
+
];
|
|
43
|
+
for (const [dimension, value, supported, weight] of dimensions) {
|
|
44
|
+
if (supported.includes(value)) score += addMatch(matched, reasons, dimension, value, weight);
|
|
45
|
+
else addMiss(unmatched, reasons, dimension, value, supported);
|
|
46
|
+
}
|
|
47
|
+
for (const risk of intent.risks) {
|
|
48
|
+
if ((definition.risks ?? []).includes(risk)) score += addMatch(matched, reasons, 'risk', risk, CAPABILITY_SCORE_WEIGHTS.risk);
|
|
49
|
+
else addMiss(unmatched, reasons, 'risk', risk, definition.risks ?? []);
|
|
50
|
+
}
|
|
51
|
+
if ((definition.mutations ?? []).includes(intent.mutation)) score += addMatch(matched, reasons, 'mutation', intent.mutation, CAPABILITY_SCORE_WEIGHTS.mutation);
|
|
52
|
+
else addMiss(unmatched, reasons, 'mutation', intent.mutation, definition.mutations ?? []);
|
|
53
|
+
const evidence = definition.evidence ?? {};
|
|
54
|
+
for (const section of ['prefer', 'deEmphasize']) {
|
|
55
|
+
for (const key of EVIDENCE_KEYS) {
|
|
56
|
+
const expected = evidence[section]?.[key];
|
|
57
|
+
if (expected === undefined) continue;
|
|
58
|
+
const actual = intent.evidence[key];
|
|
59
|
+
if (actual === null) {
|
|
60
|
+
reasons.push(`evidence ${key} unspecified; ${section} preference not applied`);
|
|
61
|
+
} else if (section === 'prefer' && actual === expected) {
|
|
62
|
+
score += addMatch(matched, reasons, 'evidence', `${key}=${actual}`, CAPABILITY_SCORE_WEIGHTS.evidence);
|
|
63
|
+
} else if (section === 'prefer') {
|
|
64
|
+
addMiss(unmatched, reasons, 'evidence', `${key}=${actual}`, [`${key}=${expected}`]);
|
|
65
|
+
reasons.push(`evidence mismatch ${key}: expected ${expected}, got ${actual}`);
|
|
66
|
+
} else if (actual === expected) {
|
|
67
|
+
addMiss(unmatched, reasons, 'evidence', `${key}=${actual}`, []);
|
|
68
|
+
score -= CAPABILITY_SCORE_WEIGHTS.evidence;
|
|
69
|
+
reasons.push(`evidence de-emphasized ${key}=${actual} (-${CAPABILITY_SCORE_WEIGHTS.evidence})`);
|
|
70
|
+
} else {
|
|
71
|
+
reasons.push(`evidence ${key}=${actual} did not trigger de-emphasis for ${expected}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { skill: definition.skill, score, matched, unmatched, reasons };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function rankCapabilities(intentInput, capabilities = CAPABILITIES) {
|
|
79
|
+
const intent = normalizeIntent(intentInput);
|
|
80
|
+
return capabilities.map((capability) => scoreIntent(intent, capability))
|
|
81
|
+
.sort((left, right) => right.score - left.score || (left.skill < right.skill ? -1 : left.skill > right.skill ? 1 : 0));
|
|
82
|
+
}
|
package/src/catalog.js
CHANGED
|
@@ -16,7 +16,7 @@ export const SKILLS = [
|
|
|
16
16
|
{ id: 'showdar-ops', domain: 'ops', description: 'Use when inspecting or changing CI/CD, containers, environments, deployment, observability, rollback, or runtime operations.' },
|
|
17
17
|
];
|
|
18
18
|
|
|
19
|
-
export const AI_TARGETS = ['codex', 'opencode', 'claude', 'universal', 'all'];
|
|
19
|
+
export const AI_TARGETS = ['codex', 'opencode', 'cursor', 'claude', 'universal', 'all'];
|
|
20
20
|
|
|
21
21
|
const ids = (...values) => values;
|
|
22
22
|
|
|
@@ -55,3 +55,16 @@ export function resolveProfile(profile) {
|
|
|
55
55
|
export function getSkill(id) {
|
|
56
56
|
return SKILLS.find((skill) => skill.id === id) ?? null;
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
export function normalizeSkillName(name) {
|
|
60
|
+
if (typeof name !== 'string' || !name.trim()) throw new Error('Skill name is required.');
|
|
61
|
+
const trimmed = name.trim();
|
|
62
|
+
const canonical = trimmed.startsWith('showdar-') ? trimmed : `showdar-${trimmed}`;
|
|
63
|
+
const skill = getSkill(canonical);
|
|
64
|
+
if (!skill) {
|
|
65
|
+
const known = SKILLS.map((s) => s.id.replace(/^showdar-/, '')).join(', ');
|
|
66
|
+
throw new Error(`Unknown skill "${name}". Available skills: ${known}`);
|
|
67
|
+
}
|
|
68
|
+
return skill.id;
|
|
69
|
+
}
|
|
70
|
+
|