@dzhechkov/skills-presentation-storyteller 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/bin/cli.js +5 -0
- package/package.json +47 -0
- package/sources.json +19 -0
- package/src/cli.js +107 -0
- package/src/commands/doctor.js +340 -0
- package/src/commands/init.js +168 -0
- package/src/commands/list.js +146 -0
- package/src/commands/remove.js +182 -0
- package/src/commands/update.js +170 -0
- package/src/utils.js +149 -0
- package/templates/.claude/commands/presentation-storyteller.md +23 -0
- package/templates/.claude/skills/explore/SKILL.md +218 -0
- package/templates/.claude/skills/explore/references/questioning-techniques.md +151 -0
- package/templates/.claude/skills/explore/references/task-brief-templates.md +355 -0
- package/templates/.claude/skills/goap-research-ed25519/SKILL.md +418 -0
- package/templates/.claude/skills/goap-research-ed25519/references/ed25519-verification.md +658 -0
- package/templates/.claude/skills/goap-research-ed25519/references/research-actions.md +544 -0
- package/templates/.claude/skills/goap-research-ed25519/references/source-evaluation.md +560 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/ed25519_verifier.py +662 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/goap_planner.py +720 -0
- package/templates/.claude/skills/presentation-storyteller/SKILL.md +374 -0
- package/templates/.claude/skills/presentation-storyteller/references/example-presentation.md +273 -0
- package/templates/.claude/skills/presentation-storyteller/references/slide-types.md +426 -0
- package/templates/.claude/skills/presentation-storyteller/references/sources-index-template.md +213 -0
- package/templates/.claude/skills/presentation-storyteller/references/speaker-script-patterns.md +324 -0
- package/templates/.claude/skills/presentation-storyteller/references/storytelling-frameworks.md +270 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dzhechko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @dzhechkov/skills-presentation-storyteller
|
|
2
|
+
|
|
3
|
+
Presentation Storyteller — composite skill for [Claude Code](https://claude.com/claude-code) that builds a **selling presentation with verified sources** plus a slide-by-slide **storytelling speaker script**.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
From a topic / product / pitch, it produces three artifacts:
|
|
8
|
+
|
|
9
|
+
1. `presentation-outline.md` — outline with the key thesis per point
|
|
10
|
+
2. `presentation-full.md` — the full deck in Markdown with inline source links
|
|
11
|
+
3. `sources-index.md` — index of verified sources
|
|
12
|
+
|
|
13
|
+
…and a **"how to tell it"** speaker script for every slide, using storytelling frameworks (AIDA, Hero's Journey, Problem-Solution). Sources are gathered through verified research, so claims trace back to a citation.
|
|
14
|
+
|
|
15
|
+
Triggers on `"сделай презентацию"`, `"presentation with storytelling"`, `"sales deck"`, `"pitch deck"`, `"продающая презентация"`.
|
|
16
|
+
|
|
17
|
+
## Why
|
|
18
|
+
|
|
19
|
+
A standalone, canonical home (per [ADR-0001](https://github.com/djd1m/dz-harness-hub/blob/main/docs/adr/0001-skill-canonicalization-and-dependency-model.md)) so other skills — e.g. `reverse-engineering-unicorn`'s Post-M6 pitch-deck step — can **reference it by id** instead of vendoring a copy.
|
|
20
|
+
|
|
21
|
+
## How to Use
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @dzhechkov/skills-presentation-storyteller init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then in Claude Code: `/presentation-storyteller pitch for an AI onboarding assistant`.
|
|
28
|
+
|
|
29
|
+
## What's Bundled
|
|
30
|
+
|
|
31
|
+
| Component | Description |
|
|
32
|
+
|-----------|-------------|
|
|
33
|
+
| `presentation-storyteller` | Orchestrator skill (outline + full deck + speaker script) |
|
|
34
|
+
| `explore` | Task clarification *(tracked vendor)* |
|
|
35
|
+
| `goap-research-ed25519` | Verified-source research *(tracked vendor)* |
|
|
36
|
+
| `/presentation-storyteller` | Slash command |
|
|
37
|
+
|
|
38
|
+
> **Canonicalization (ADR-0001):** `presentation-storyteller` is the canonical artifact of this pack. It had a single copy in the repo (keysarium), so there was no drift to resolve. The `explore`/`goap` pair is a **sources.json-tracked** vendored copy (canonical: [`@dzhechkov/skills-analyst-manual`](https://www.npmjs.com/package/@dzhechkov/skills-analyst-manual)). Re-sync drift with `dz sync-upstream`.
|
|
39
|
+
|
|
40
|
+
## CLI Commands
|
|
41
|
+
|
|
42
|
+
`init` *(default)* · `update` · `remove` · `list` · `doctor` — options `--force`, `--dry-run`, `--help`, `--version`.
|
|
43
|
+
|
|
44
|
+
## Also Available Via dz
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
dz init --select presentation-storyteller
|
|
48
|
+
dz info presentation-storyteller
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
MIT
|
package/bin/cli.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dzhechkov/skills-presentation-storyteller",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Presentation Storyteller — composite skill for Claude Code: build a selling presentation with verified sources + a slide-by-slide storytelling speaker script (AIDA / Hero's Journey / Problem-Solution)",
|
|
5
|
+
"main": "src/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"skills-presentation-storyteller": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"templates/",
|
|
13
|
+
"sources.json",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"claude",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"ai",
|
|
20
|
+
"presentation",
|
|
21
|
+
"storytelling",
|
|
22
|
+
"pitch-deck",
|
|
23
|
+
"sales-deck",
|
|
24
|
+
"speaker-notes",
|
|
25
|
+
"verified-sources",
|
|
26
|
+
"slides",
|
|
27
|
+
"skills",
|
|
28
|
+
"skill-pack"
|
|
29
|
+
],
|
|
30
|
+
"author": "dzhechko",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=16.0.0"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/djd1m/dz-harness-hub.git",
|
|
38
|
+
"directory": "packages/@dzhechkov/skills-presentation-storyteller"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/djd1m/dz-harness-hub/tree/main/packages/@dzhechkov/skills-presentation-storyteller#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/djd1m/dz-harness-hub/issues"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/sources.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_note": "Per ADR-0001: presentation-storyteller is the canonical artifact of THIS pack (single source of truth; it had only one copy in the repo — keysarium — so there was no drift to resolve). The explore/goap pair is a TRACKED vendored copy. Re-sync with `dz sync-upstream`.",
|
|
3
|
+
"skills": {
|
|
4
|
+
"presentation-storyteller": {
|
|
5
|
+
"origin": "dz-original",
|
|
6
|
+
"canonical_pack": "@dzhechkov/skills-presentation-storyteller"
|
|
7
|
+
},
|
|
8
|
+
"explore": {
|
|
9
|
+
"origin": "vendored",
|
|
10
|
+
"canonical_pack": "@dzhechkov/skills-analyst-manual",
|
|
11
|
+
"upstream_path": "templates/.claude/skills/explore"
|
|
12
|
+
},
|
|
13
|
+
"goap-research-ed25519": {
|
|
14
|
+
"origin": "vendored",
|
|
15
|
+
"canonical_pack": "@dzhechkov/skills-analyst-manual",
|
|
16
|
+
"upstream_path": "templates/.claude/skills/goap-research-ed25519"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { bold, cyan, dim, green, yellow, red, info, error } = require('./utils');
|
|
5
|
+
|
|
6
|
+
function showBanner() {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log(cyan('╔══════════════════════════════════════════════════════════╗'));
|
|
9
|
+
console.log(cyan('║') + bold(' PRESENTATION STORYTELLER — selling deck + speaker script') + cyan('║'));
|
|
10
|
+
console.log(cyan('║') + ' outline + full deck + "how to tell it" · verified srcs ' + cyan('║'));
|
|
11
|
+
console.log(cyan('╚══════════════════════════════════════════════════════════╝'));
|
|
12
|
+
console.log('');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getVersion() {
|
|
16
|
+
const pkg = require('../package.json');
|
|
17
|
+
return pkg.version;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function showHelp() {
|
|
21
|
+
console.log(bold('Usage:') + ' npx @dzhechkov/skills-presentation-storyteller ' + cyan('<command>') + dim(' [options]'));
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(bold('Commands:'));
|
|
24
|
+
console.log(' ' + green('init') + ' Install Presentation Storyteller skill pack into current project ' + dim('(default)'));
|
|
25
|
+
console.log(' ' + green('update') + ' Update Presentation Storyteller skill pack to latest version');
|
|
26
|
+
console.log(' ' + green('remove') + ' Remove Presentation Storyteller skill pack from project');
|
|
27
|
+
console.log(' ' + green('list') + ' List installed Presentation Storyteller components');
|
|
28
|
+
console.log(' ' + green('doctor') + ' Check Presentation Storyteller installation health');
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log(bold('Options:'));
|
|
31
|
+
console.log(' ' + yellow('--force') + ' Overwrite existing files without prompting');
|
|
32
|
+
console.log(' ' + yellow('--dry-run') + ' Show what would be done without making changes');
|
|
33
|
+
console.log(' ' + yellow('--help, -h') + ' Show this help message');
|
|
34
|
+
console.log(' ' + yellow('--version, -v') + ' Show version number');
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log(bold('Examples:'));
|
|
37
|
+
console.log(dim(' $ ') + 'npx @dzhechkov/skills-presentation-storyteller init');
|
|
38
|
+
console.log(dim(' $ ') + 'npx @dzhechkov/skills-presentation-storyteller init --force');
|
|
39
|
+
console.log(dim(' $ ') + 'npx @dzhechkov/skills-presentation-storyteller doctor');
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log(bold('What gets installed:'));
|
|
42
|
+
console.log(' Composite skill: presentation-storyteller (orchestrator)');
|
|
43
|
+
console.log(' Dependent skills: explore, goap-research-ed25519');
|
|
44
|
+
console.log(' Command: /presentation-storyteller');
|
|
45
|
+
console.log('');
|
|
46
|
+
console.log(bold('Self-contained:'));
|
|
47
|
+
console.log(' Bundles its dependent skills (tracked via sources.json).');
|
|
48
|
+
console.log('');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseArgs(argv) {
|
|
52
|
+
const args = argv.slice(2);
|
|
53
|
+
const flags = { force: false, dryRun: false, targetDir: process.cwd() };
|
|
54
|
+
let command = null;
|
|
55
|
+
|
|
56
|
+
for (const arg of args) {
|
|
57
|
+
switch (arg) {
|
|
58
|
+
case '--force': flags.force = true; break;
|
|
59
|
+
case '--dry-run': flags.dryRun = true; break;
|
|
60
|
+
case '--help':
|
|
61
|
+
case '-h': command = 'help'; break;
|
|
62
|
+
case '--version':
|
|
63
|
+
case '-v': command = 'version'; break;
|
|
64
|
+
default:
|
|
65
|
+
if (arg.startsWith('-')) {
|
|
66
|
+
error(`Unknown option: ${arg}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
} else if (command === null) {
|
|
69
|
+
command = arg;
|
|
70
|
+
} else {
|
|
71
|
+
error(`Unexpected argument: ${arg}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { command, flags };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function main() {
|
|
82
|
+
const { command, flags } = parseArgs(process.argv);
|
|
83
|
+
|
|
84
|
+
if (command === 'version') { console.log(getVersion()); return; }
|
|
85
|
+
|
|
86
|
+
showBanner();
|
|
87
|
+
|
|
88
|
+
if (command === 'help') { showHelp(); return; }
|
|
89
|
+
|
|
90
|
+
const resolvedCommand = command || 'init';
|
|
91
|
+
|
|
92
|
+
switch (resolvedCommand) {
|
|
93
|
+
case 'init': require('./commands/init')(flags); break;
|
|
94
|
+
case 'update': require('./commands/update')(flags); break;
|
|
95
|
+
case 'remove': require('./commands/remove')(flags); break;
|
|
96
|
+
case 'list': require('./commands/list')(flags); break;
|
|
97
|
+
case 'doctor': require('./commands/doctor')(flags); break;
|
|
98
|
+
default:
|
|
99
|
+
error(`Unknown command: "${resolvedCommand}"`);
|
|
100
|
+
console.log('');
|
|
101
|
+
showHelp();
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main();
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const {
|
|
6
|
+
green, red, yellow, cyan, bold, dim,
|
|
7
|
+
info, success, warn, error: logError,
|
|
8
|
+
fileExists, readJSON, readManifest,
|
|
9
|
+
MANIFEST_FILE, COMPONENTS,
|
|
10
|
+
} = require('../utils');
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Check definitions
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
function check(name, fn) {
|
|
17
|
+
try {
|
|
18
|
+
const result = fn();
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
passed: result.passed,
|
|
22
|
+
detail: result.detail || '',
|
|
23
|
+
fix: result.fix || '',
|
|
24
|
+
};
|
|
25
|
+
} catch (err) {
|
|
26
|
+
return {
|
|
27
|
+
name,
|
|
28
|
+
passed: false,
|
|
29
|
+
detail: `Exception: ${err.message}`,
|
|
30
|
+
fix: 'Investigate the error above.',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function checkFilesExist(manifest, targetDir) {
|
|
36
|
+
return check('Files exist', () => {
|
|
37
|
+
const files = manifest.files || [];
|
|
38
|
+
const missing = [];
|
|
39
|
+
|
|
40
|
+
for (const relPath of files) {
|
|
41
|
+
const absPath = path.join(targetDir, relPath);
|
|
42
|
+
if (!fileExists(absPath)) {
|
|
43
|
+
missing.push(relPath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (missing.length === 0) {
|
|
48
|
+
return {
|
|
49
|
+
passed: true,
|
|
50
|
+
detail: `All ${files.length} manifest files present`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
passed: false,
|
|
56
|
+
detail: `${missing.length} file(s) missing: ${missing.slice(0, 5).join(', ')}${missing.length > 5 ? '...' : ''}`,
|
|
57
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore missing files.`,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function checkSkillAnalyst(targetDir) {
|
|
63
|
+
return check('Presentation Storyteller Full skill', () => {
|
|
64
|
+
const skillDir = path.join(targetDir, '.claude', 'skills', 'presentation-storyteller');
|
|
65
|
+
|
|
66
|
+
if (!fileExists(skillDir)) {
|
|
67
|
+
return {
|
|
68
|
+
passed: false,
|
|
69
|
+
detail: '.claude/skills/presentation-storyteller/ directory not found',
|
|
70
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore skill.`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
75
|
+
if (!fileExists(skillMdPath)) {
|
|
76
|
+
return {
|
|
77
|
+
passed: false,
|
|
78
|
+
detail: '.claude/skills/presentation-storyteller/SKILL.md not found',
|
|
79
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore SKILL.md.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check for references directory
|
|
84
|
+
const refsDir = path.join(skillDir, 'references');
|
|
85
|
+
let refCount = 0;
|
|
86
|
+
if (fileExists(refsDir)) {
|
|
87
|
+
try {
|
|
88
|
+
const entries = fs.readdirSync(refsDir);
|
|
89
|
+
refCount = entries.filter((e) => e.endsWith('.md')).length;
|
|
90
|
+
} catch {
|
|
91
|
+
// ignore
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
passed: true,
|
|
97
|
+
detail: `SKILL.md present, ${refCount} references`,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function checkSkillExplore(targetDir) {
|
|
103
|
+
return check('Explore skill', () => {
|
|
104
|
+
const skillDir = path.join(targetDir, '.claude', 'skills', 'explore');
|
|
105
|
+
|
|
106
|
+
if (!fileExists(skillDir)) {
|
|
107
|
+
return {
|
|
108
|
+
passed: false,
|
|
109
|
+
detail: '.claude/skills/explore/ directory not found',
|
|
110
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore skill.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
115
|
+
if (!fileExists(skillMdPath)) {
|
|
116
|
+
return {
|
|
117
|
+
passed: false,
|
|
118
|
+
detail: '.claude/skills/explore/SKILL.md not found',
|
|
119
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore SKILL.md.`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
passed: true,
|
|
125
|
+
detail: 'SKILL.md present',
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function checkSkillGoap(targetDir) {
|
|
131
|
+
return check('GOAP Research skill', () => {
|
|
132
|
+
const skillDir = path.join(targetDir, '.claude', 'skills', 'goap-research-ed25519');
|
|
133
|
+
|
|
134
|
+
if (!fileExists(skillDir)) {
|
|
135
|
+
return {
|
|
136
|
+
passed: false,
|
|
137
|
+
detail: '.claude/skills/goap-research-ed25519/ directory not found',
|
|
138
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore skill.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
143
|
+
if (!fileExists(skillMdPath)) {
|
|
144
|
+
return {
|
|
145
|
+
passed: false,
|
|
146
|
+
detail: '.claude/skills/goap-research-ed25519/SKILL.md not found',
|
|
147
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore SKILL.md.`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
passed: true,
|
|
153
|
+
detail: 'SKILL.md present',
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function checkSkillSolver(targetDir) {
|
|
159
|
+
return check('Problem Solver Enhanced skill', () => {
|
|
160
|
+
const skillDir = path.join(targetDir, '.claude', 'skills', 'problem-solver-enhanced');
|
|
161
|
+
|
|
162
|
+
if (!fileExists(skillDir)) {
|
|
163
|
+
return {
|
|
164
|
+
passed: false,
|
|
165
|
+
detail: '.claude/skills/problem-solver-enhanced/ directory not found',
|
|
166
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore skill.`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
171
|
+
if (!fileExists(skillMdPath)) {
|
|
172
|
+
return {
|
|
173
|
+
passed: false,
|
|
174
|
+
detail: '.claude/skills/problem-solver-enhanced/SKILL.md not found',
|
|
175
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore SKILL.md.`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
passed: true,
|
|
181
|
+
detail: 'SKILL.md present',
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function checkCommandPresent(targetDir) {
|
|
187
|
+
return check('Presentation Storyteller command', () => {
|
|
188
|
+
const commandsDir = path.join(targetDir, '.claude', 'commands');
|
|
189
|
+
|
|
190
|
+
if (!fileExists(commandsDir)) {
|
|
191
|
+
return {
|
|
192
|
+
passed: false,
|
|
193
|
+
detail: '.claude/commands/ directory not found',
|
|
194
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore command.`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let entries;
|
|
199
|
+
try {
|
|
200
|
+
entries = fs.readdirSync(commandsDir);
|
|
201
|
+
} catch {
|
|
202
|
+
return {
|
|
203
|
+
passed: false,
|
|
204
|
+
detail: 'Cannot read .claude/commands/ directory',
|
|
205
|
+
fix: 'Check directory permissions.',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const analystCommands = entries.filter(
|
|
210
|
+
(e) => e.startsWith('presentation-storyteller') && e.endsWith('.md')
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
if (analystCommands.length === 0) {
|
|
214
|
+
return {
|
|
215
|
+
passed: false,
|
|
216
|
+
detail: 'No Presentation Storyteller command files found (expected presentation-storyteller*.md)',
|
|
217
|
+
fix: `Run ${cyan('@dzhechkov/skills-presentation-storyteller update')} to restore command.`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
passed: true,
|
|
223
|
+
detail: `${analystCommands.length} command(s): ${analystCommands.join(', ')}`,
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function checkKeysariumIntegration(targetDir) {
|
|
229
|
+
return check('Keysarium integration', () => {
|
|
230
|
+
const keysariumPath = path.join(targetDir, '.keysarium.json');
|
|
231
|
+
|
|
232
|
+
if (!fileExists(keysariumPath)) {
|
|
233
|
+
return {
|
|
234
|
+
passed: true,
|
|
235
|
+
detail: 'Not installed (standalone Presentation Storyteller mode)',
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const keysariumManifest = readJSON(keysariumPath);
|
|
240
|
+
if (!keysariumManifest) {
|
|
241
|
+
return {
|
|
242
|
+
passed: true,
|
|
243
|
+
detail: 'Detected but manifest unreadable',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
passed: true,
|
|
249
|
+
detail: `Active (Keysarium v${keysariumManifest.version})`,
|
|
250
|
+
};
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
// Main command
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
|
|
258
|
+
async function run(options) {
|
|
259
|
+
const { targetDir } = options;
|
|
260
|
+
const manifestPath = path.join(targetDir, MANIFEST_FILE);
|
|
261
|
+
|
|
262
|
+
// ── a) Check manifest exists ──────────────────────────────────────────
|
|
263
|
+
if (!fileExists(manifestPath)) {
|
|
264
|
+
logError('Presentation Storyteller skill pack is not installed in this directory.');
|
|
265
|
+
info(`Run ${cyan('@dzhechkov/skills-presentation-storyteller init')} to install.`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const manifest = readManifest(targetDir);
|
|
270
|
+
if (!manifest) {
|
|
271
|
+
logError(`Failed to read ${MANIFEST_FILE} \u2014 file may be corrupted.`);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log('');
|
|
276
|
+
info(bold(`Running health checks for @dzhechkov/skills-presentation-storyteller v${manifest.version}...`));
|
|
277
|
+
console.log('');
|
|
278
|
+
|
|
279
|
+
// ── b) Run all checks ─────────────────────────────────────────────────
|
|
280
|
+
const results = [
|
|
281
|
+
checkFilesExist(manifest, targetDir),
|
|
282
|
+
checkSkillAnalyst(targetDir),
|
|
283
|
+
checkSkillExplore(targetDir),
|
|
284
|
+
checkSkillGoap(targetDir),
|
|
285
|
+
checkSkillSolver(targetDir),
|
|
286
|
+
checkCommandPresent(targetDir),
|
|
287
|
+
checkKeysariumIntegration(targetDir),
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
// ── c) Display results ────────────────────────────────────────────────
|
|
291
|
+
let passedCount = 0;
|
|
292
|
+
const failures = [];
|
|
293
|
+
|
|
294
|
+
for (const result of results) {
|
|
295
|
+
const icon = result.passed ? green('\u2713') : red('\u2717');
|
|
296
|
+
const statusColor = result.passed ? green : red;
|
|
297
|
+
const detailStr = result.detail ? dim(` \u2014 ${result.detail}`) : '';
|
|
298
|
+
|
|
299
|
+
console.log(` ${icon} ${statusColor(result.name)}${detailStr}`);
|
|
300
|
+
|
|
301
|
+
if (result.passed) {
|
|
302
|
+
passedCount++;
|
|
303
|
+
} else {
|
|
304
|
+
failures.push(result);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── d) Summary ────────────────────────────────────────────────────────
|
|
309
|
+
console.log('');
|
|
310
|
+
const total = results.length;
|
|
311
|
+
|
|
312
|
+
if (passedCount === total) {
|
|
313
|
+
success(bold(`${passedCount}/${total} checks passed \u2014 Presentation Storyteller installation is healthy!`));
|
|
314
|
+
console.log('');
|
|
315
|
+
process.exit(0);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
warn(bold(`${passedCount}/${total} checks passed`));
|
|
319
|
+
console.log('');
|
|
320
|
+
|
|
321
|
+
// ── e) Show fix suggestions ───────────────────────────────────────────
|
|
322
|
+
console.log(bold('Fix suggestions:'));
|
|
323
|
+
console.log('');
|
|
324
|
+
|
|
325
|
+
for (const failure of failures) {
|
|
326
|
+
console.log(` ${red('\u2717')} ${bold(failure.name)}`);
|
|
327
|
+
if (failure.detail) {
|
|
328
|
+
console.log(` ${dim(failure.detail)}`);
|
|
329
|
+
}
|
|
330
|
+
if (failure.fix) {
|
|
331
|
+
console.log(` ${yellow('Fix:')} ${failure.fix}`);
|
|
332
|
+
}
|
|
333
|
+
console.log('');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
module.exports = run;
|
|
340
|
+
module.exports.run = run;
|