haki-skills 0.2.6 → 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.
|
@@ -85,6 +85,7 @@ const {
|
|
|
85
85
|
cmdRoadmapUpdateStatus,
|
|
86
86
|
} = require("./lib/roadmap.cjs");
|
|
87
87
|
const { cmdStateDetect, cmdStateJson } = require("./lib/state.cjs");
|
|
88
|
+
const { cmdSkill } = require("./lib/skill.cjs");
|
|
88
89
|
|
|
89
90
|
function main() {
|
|
90
91
|
const args = process.argv.slice(2);
|
|
@@ -153,6 +154,12 @@ function main() {
|
|
|
153
154
|
break;
|
|
154
155
|
}
|
|
155
156
|
|
|
157
|
+
// ─── Skill ───────────────────────────────────────────────────────
|
|
158
|
+
case "skill": {
|
|
159
|
+
cmdSkill(subCommand, restArgs[0]);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
156
163
|
// ─── State ────────────────────────────────────────────────────────
|
|
157
164
|
case "state": {
|
|
158
165
|
withRunEvents(cwd, `state.${subCommand || "unknown"}`, restArgs, () => {
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .agent/bin/lib/skill.cjs
|
|
3
|
+
*
|
|
4
|
+
* Skill-related commands for haki-tools.
|
|
5
|
+
* - list: List all skills with name and description
|
|
6
|
+
* - info: Show single skill details
|
|
7
|
+
* - invoke: Print skill SKILL.md content to stdout
|
|
8
|
+
* - registry: Generate .agent/registry.json
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
// __dirname = .agent/bin/lib/
|
|
17
|
+
// Resolve skills relative to this file: lib/ → bin/ → .agent/ → skills
|
|
18
|
+
const THIS_DIR = __dirname; // e.g. D:\...\haki-skills\.agent\bin\lib
|
|
19
|
+
const UP_TO_BIN = path.resolve(THIS_DIR, '..'); // .agent/bin
|
|
20
|
+
const UP_TO_AGENT = path.resolve(UP_TO_BIN, '..'); // .agent
|
|
21
|
+
const SKILLS_DIR = path.join(UP_TO_AGENT, 'skills');
|
|
22
|
+
const REGISTRY = path.join(UP_TO_AGENT, 'registry.json');
|
|
23
|
+
|
|
24
|
+
// ── Frontmatter parser ────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
function parseFrontmatter(content) {
|
|
27
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
28
|
+
if (!match) return null;
|
|
29
|
+
|
|
30
|
+
const raw = match[1];
|
|
31
|
+
const body = content.slice(match[0].length);
|
|
32
|
+
const attrs = {};
|
|
33
|
+
|
|
34
|
+
raw.split(/\r?\n/).forEach(function(line) {
|
|
35
|
+
const i = line.indexOf(':');
|
|
36
|
+
if (i === -1) return;
|
|
37
|
+
const k = line.slice(0, i).trim();
|
|
38
|
+
let v = line.slice(i + 1).trim();
|
|
39
|
+
if ((v.charAt(0) === '"' && v.charAt(v.length - 1) === '"') ||
|
|
40
|
+
(v.charAt(0) === "'" && v.charAt(v.length - 1) === "'")) {
|
|
41
|
+
v = v.slice(1, -1);
|
|
42
|
+
}
|
|
43
|
+
attrs[k] = v;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return { attrs: attrs, body: body };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Load all skills ────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
function loadAllSkills() {
|
|
52
|
+
const dirs = fs.readdirSync(SKILLS_DIR).filter(function(f) {
|
|
53
|
+
return fs.statSync(path.join(SKILLS_DIR, f)).isDirectory();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return dirs.map(function(dir) {
|
|
57
|
+
const skillPath = path.join(SKILLS_DIR, dir, 'SKILL.md');
|
|
58
|
+
const exists = fs.existsSync(skillPath);
|
|
59
|
+
|
|
60
|
+
// Collect templates and scripts
|
|
61
|
+
const skillDir = path.join(SKILLS_DIR, dir);
|
|
62
|
+
const templates = [];
|
|
63
|
+
const scripts = [];
|
|
64
|
+
try {
|
|
65
|
+
fs.readdirSync(path.join(skillDir, 'templates')).forEach(function(f) {
|
|
66
|
+
templates.push('templates/' + f);
|
|
67
|
+
});
|
|
68
|
+
} catch (e) { /* no templates dir */ }
|
|
69
|
+
try {
|
|
70
|
+
fs.readdirSync(path.join(skillDir, 'scripts')).forEach(function(f) {
|
|
71
|
+
scripts.push('scripts/' + f);
|
|
72
|
+
});
|
|
73
|
+
} catch (e) { /* no scripts dir */ }
|
|
74
|
+
|
|
75
|
+
if (!exists) {
|
|
76
|
+
return {
|
|
77
|
+
name: null,
|
|
78
|
+
description: null,
|
|
79
|
+
path: dir + '/SKILL.md',
|
|
80
|
+
templates: templates,
|
|
81
|
+
scripts: scripts,
|
|
82
|
+
exists: false,
|
|
83
|
+
raw: null,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const content = fs.readFileSync(skillPath, 'utf8');
|
|
88
|
+
const parsed = parseFrontmatter(content);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
name: parsed ? (parsed.attrs.name || dir) : dir,
|
|
92
|
+
description: parsed ? (parsed.attrs.description || '') : '',
|
|
93
|
+
path: dir + '/SKILL.md',
|
|
94
|
+
templates: templates,
|
|
95
|
+
scripts: scripts,
|
|
96
|
+
exists: true,
|
|
97
|
+
raw: content,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Commands ───────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
function cmdSkillList() {
|
|
105
|
+
const skills = loadAllSkills();
|
|
106
|
+
|
|
107
|
+
console.log('\nAvailable skills (' + skills.length + '):\n');
|
|
108
|
+
skills.forEach(function(s) {
|
|
109
|
+
const mark = s.exists ? '' : ' [MISSING SKILL.md]';
|
|
110
|
+
const desc = s.description
|
|
111
|
+
? ' — ' + s.description.slice(0, 70) + (s.description.length > 70 ? '...' : '')
|
|
112
|
+
: '';
|
|
113
|
+
console.log(' ' + s.name + mark + desc);
|
|
114
|
+
});
|
|
115
|
+
console.log('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function cmdSkillInfo(name) {
|
|
119
|
+
const skills = loadAllSkills();
|
|
120
|
+
const skill = skills.find(function(s) { return s.name === name; });
|
|
121
|
+
|
|
122
|
+
if (!skill) {
|
|
123
|
+
const names = skills.map(function(s) { return s.name; }).join(', ');
|
|
124
|
+
throw new Error('Unknown skill: ' + name + '\nAvailable: ' + names);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!skill.exists) {
|
|
128
|
+
throw new Error('SKILL.md missing for: ' + skill.path);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log('\n=== ' + skill.name + ' ===');
|
|
132
|
+
if (skill.description) console.log(skill.description + '\n');
|
|
133
|
+
console.log('Path: .agent/skills/' + skill.path);
|
|
134
|
+
if (skill.templates.length) console.log('Templates: ' + skill.templates.join(', '));
|
|
135
|
+
if (skill.scripts.length) console.log('Scripts: ' + skill.scripts.join(', '));
|
|
136
|
+
console.log('');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function cmdSkillInvoke(name) {
|
|
140
|
+
const skills = loadAllSkills();
|
|
141
|
+
const skill = skills.find(function(s) { return s.name === name; });
|
|
142
|
+
|
|
143
|
+
if (!skill) {
|
|
144
|
+
const names = skills.map(function(s) { return s.name; }).join(', ');
|
|
145
|
+
throw new Error('Unknown skill: ' + name + '\nAvailable: ' + names);
|
|
146
|
+
}
|
|
147
|
+
if (!skill.exists) {
|
|
148
|
+
throw new Error('SKILL.md missing for: ' + skill.path);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
process.stdout.write(skill.raw);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function cmdSkillRegistry() {
|
|
155
|
+
const skills = loadAllSkills();
|
|
156
|
+
const registry = {
|
|
157
|
+
skills: skills.map(function(s) {
|
|
158
|
+
return {
|
|
159
|
+
name: s.name || s.path.replace('/SKILL.md', ''),
|
|
160
|
+
description: s.description || '',
|
|
161
|
+
path: '.agent/skills/' + s.path,
|
|
162
|
+
templates: s.templates.map(function(t) { return '.agent/skills/' + s.path.replace('/SKILL.md', '/') + t; }),
|
|
163
|
+
scripts: s.scripts.map(function(t) { return '.agent/skills/' + s.path.replace('/SKILL.md', '/') + t; }),
|
|
164
|
+
};
|
|
165
|
+
}),
|
|
166
|
+
generated: new Date().toISOString(),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
fs.writeFileSync(REGISTRY, JSON.stringify(registry, null, 2), 'utf8');
|
|
170
|
+
console.log('Generated registry.json (' + registry.skills.length + ' skills)');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── Routing ────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
function cmdSkill(subCmd, name) {
|
|
176
|
+
switch (subCmd) {
|
|
177
|
+
case 'list':
|
|
178
|
+
case undefined:
|
|
179
|
+
cmdSkillList();
|
|
180
|
+
break;
|
|
181
|
+
case 'info':
|
|
182
|
+
if (!name) throw new Error('Usage: haki-tools skill info <name>');
|
|
183
|
+
cmdSkillInfo(name);
|
|
184
|
+
break;
|
|
185
|
+
case 'invoke':
|
|
186
|
+
if (!name) throw new Error('Usage: haki-tools skill invoke <name>');
|
|
187
|
+
cmdSkillInvoke(name);
|
|
188
|
+
break;
|
|
189
|
+
case 'registry':
|
|
190
|
+
cmdSkillRegistry();
|
|
191
|
+
break;
|
|
192
|
+
default:
|
|
193
|
+
throw new Error('Usage: haki-tools skill <list|info|invoke|registry> [name]');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = { cmdSkill, cmdSkillList, cmdSkillInfo, cmdSkillInvoke, cmdSkillRegistry };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: taste-research
|
|
3
|
+
description: "Reference research directory for design taste topics. Not a skill — see README.md for topic index."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Taste Research
|
|
7
|
+
|
|
8
|
+
This is a **reference directory**, not an executable skill.
|
|
9
|
+
|
|
10
|
+
Content: Research on design taste, AI laziness patterns, and remediation strategies.
|
|
11
|
+
|
|
12
|
+
See [README.md](./README.md) for available topic indexes.
|