thachvd-kit 1.0.35 → 1.0.37
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/README.md +11 -1
- package/THIRD_PARTY_NOTICES.md +49 -0
- package/bin/cli.js +74 -24
- package/bin/matt-skills.js +192 -191
- package/bin/native-skills.js +149 -0
- package/bin/upgrade.js +22 -17
- package/package.json +5 -3
- package/skills/finishing-a-development-branch/SKILL.md +240 -0
- package/skills/requesting-code-review/code-reviewer.md +198 -0
- package/skills/subagent-driven-development/SKILL.md +574 -0
- package/skills/subagent-driven-development/implementer-prompt.md +154 -0
- package/skills/subagent-driven-development/re-review-prompt.md +115 -0
- package/skills/subagent-driven-development/scripts/review-package +53 -0
- package/skills/subagent-driven-development/scripts/review-package.js +52 -0
- package/skills/subagent-driven-development/scripts/sdd-workspace +82 -0
- package/skills/subagent-driven-development/scripts/sdd-workspace-lib.js +62 -0
- package/skills/subagent-driven-development/scripts/sdd-workspace.js +15 -0
- package/skills/subagent-driven-development/scripts/task-brief +43 -0
- package/skills/subagent-driven-development/scripts/task-brief.js +46 -0
- package/skills/subagent-driven-development/task-reviewer-prompt.md +207 -0
- package/skills/upstream.json +30 -0
- package/skills/using-git-worktrees/SKILL.md +175 -0
package/bin/matt-skills.js
CHANGED
|
@@ -1,191 +1,192 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { spawnSync } = require('child_process');
|
|
4
|
-
|
|
5
|
-
// Source repository for Matt Pocock's promoted skills, installed via the
|
|
6
|
-
// upstream `skills` CLI (npx skills@latest add <source>).
|
|
7
|
-
const MATT_SKILLS_SOURCE = 'mattpocock/skills';
|
|
8
|
-
|
|
9
|
-
// This list mirrors upstream's promoted daily-driver catalog:
|
|
10
|
-
// https://github.com/mattpocock/skills -> skills/engineering/ + skills/productivity/
|
|
11
|
-
// Do NOT add anything from skills/in-progress, skills/misc, or skills/deprecated —
|
|
12
|
-
// those are explicitly not part of the supported set. Update this list by hand
|
|
13
|
-
// when upstream promotes a new skill or retires one; there is no reliable
|
|
14
|
-
// upstream "promoted only" filter, so this manifest is the source of truth.
|
|
15
|
-
const PROMOTED_SKILLS = [
|
|
16
|
-
// skills/engineering
|
|
17
|
-
'ask-matt',
|
|
18
|
-
'code-review',
|
|
19
|
-
'codebase-design',
|
|
20
|
-
'diagnosing-bugs',
|
|
21
|
-
'domain-modeling',
|
|
22
|
-
'grill-with-docs',
|
|
23
|
-
'implement',
|
|
24
|
-
'improve-codebase-architecture',
|
|
25
|
-
'prototype',
|
|
26
|
-
'research',
|
|
27
|
-
'resolving-merge-conflicts',
|
|
28
|
-
'setup-matt-pocock-skills',
|
|
29
|
-
'tdd',
|
|
30
|
-
'to-spec',
|
|
31
|
-
'to-tickets',
|
|
32
|
-
'triage',
|
|
33
|
-
'wayfinder',
|
|
34
|
-
'wizard',
|
|
35
|
-
// skills/productivity
|
|
36
|
-
'grill-me',
|
|
37
|
-
'grilling',
|
|
38
|
-
'handoff',
|
|
39
|
-
'teach',
|
|
40
|
-
'to-questionnaire',
|
|
41
|
-
'wait-what',
|
|
42
|
-
'writing-for-agents'
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
// Agent identifiers as understood by the upstream `skills` CLI
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// `
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
'--
|
|
92
|
-
'
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
// Source repository for Matt Pocock's promoted skills, installed via the
|
|
6
|
+
// upstream `skills` CLI (npx skills@latest add <source>).
|
|
7
|
+
const MATT_SKILLS_SOURCE = 'mattpocock/skills';
|
|
8
|
+
|
|
9
|
+
// This list mirrors upstream's promoted daily-driver catalog:
|
|
10
|
+
// https://github.com/mattpocock/skills -> skills/engineering/ + skills/productivity/
|
|
11
|
+
// Do NOT add anything from skills/in-progress, skills/misc, or skills/deprecated —
|
|
12
|
+
// those are explicitly not part of the supported set. Update this list by hand
|
|
13
|
+
// when upstream promotes a new skill or retires one; there is no reliable
|
|
14
|
+
// upstream "promoted only" filter, so this manifest is the source of truth.
|
|
15
|
+
const PROMOTED_SKILLS = [
|
|
16
|
+
// skills/engineering
|
|
17
|
+
'ask-matt',
|
|
18
|
+
'code-review',
|
|
19
|
+
'codebase-design',
|
|
20
|
+
'diagnosing-bugs',
|
|
21
|
+
'domain-modeling',
|
|
22
|
+
'grill-with-docs',
|
|
23
|
+
'implement',
|
|
24
|
+
'improve-codebase-architecture',
|
|
25
|
+
'prototype',
|
|
26
|
+
'research',
|
|
27
|
+
'resolving-merge-conflicts',
|
|
28
|
+
'setup-matt-pocock-skills',
|
|
29
|
+
'tdd',
|
|
30
|
+
'to-spec',
|
|
31
|
+
'to-tickets',
|
|
32
|
+
'triage',
|
|
33
|
+
'wayfinder',
|
|
34
|
+
'wizard',
|
|
35
|
+
// skills/productivity
|
|
36
|
+
'grill-me',
|
|
37
|
+
'grilling',
|
|
38
|
+
'handoff',
|
|
39
|
+
'teach',
|
|
40
|
+
'to-questionnaire',
|
|
41
|
+
'wait-what',
|
|
42
|
+
'writing-for-agents'
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// Agent identifiers as understood by the upstream `skills` CLI. Codex and
|
|
46
|
+
// Antigravity share the project-local .agents/skills directory; Claude Code
|
|
47
|
+
// also receives its native .claude/skills integration so skills are exposed
|
|
48
|
+
// as slash commands such as /ask-matt and /implement.
|
|
49
|
+
const SUPPORTED_AGENTS = ['codex', 'antigravity', 'claude-code'];
|
|
50
|
+
|
|
51
|
+
// Project-local install target. Skills are intentionally not installed
|
|
52
|
+
// globally (see AGENTS.md / .agent/docs/tooling.md for the reasoning).
|
|
53
|
+
const SKILLS_RELATIVE_DIR = path.join('.agents', 'skills');
|
|
54
|
+
|
|
55
|
+
const SETUP_SKILL_NAME = 'setup-matt-pocock-skills';
|
|
56
|
+
|
|
57
|
+
function npxCommand() {
|
|
58
|
+
return process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function skillsDirFor(targetDir) {
|
|
62
|
+
return path.join(targetDir, SKILLS_RELATIVE_DIR);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isSkillInstalled(targetDir, name) {
|
|
66
|
+
return fs.existsSync(path.join(skillsDirFor(targetDir), name, 'SKILL.md'));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Read-only: never spawns a process. Safe to call from `doctor` and
|
|
70
|
+
// `skills check`.
|
|
71
|
+
function checkSkills(targetDir) {
|
|
72
|
+
const installed = [];
|
|
73
|
+
const missing = [];
|
|
74
|
+
for (const name of PROMOTED_SKILLS) {
|
|
75
|
+
(isSkillInstalled(targetDir, name) ? installed : missing).push(name);
|
|
76
|
+
}
|
|
77
|
+
return { installed, missing, total: PROMOTED_SKILLS.length };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Builds the argv for `npx skills@latest add <source> ...`. Always passes
|
|
81
|
+
// `--skill <name>` as separate argv entries (never `--skill=<name>`): current
|
|
82
|
+
// `skills` CLI versions have a parsing bug where the `--skill=<name>` form is
|
|
83
|
+
// silently ignored and installs every skill in the source repository,
|
|
84
|
+
// including skills/in-progress, skills/misc, and skills/deprecated.
|
|
85
|
+
function buildAddArgs(skillNames) {
|
|
86
|
+
return [
|
|
87
|
+
'-y', // npx: auto-confirm installing the `skills` package itself
|
|
88
|
+
'skills@latest',
|
|
89
|
+
'add',
|
|
90
|
+
MATT_SKILLS_SOURCE,
|
|
91
|
+
'--skill', ...skillNames,
|
|
92
|
+
'--agent', ...SUPPORTED_AGENTS,
|
|
93
|
+
'-y' // skills CLI: skip its own confirmation prompts
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function formatCommand(args) {
|
|
98
|
+
return `${npxCommand()} ${args.join(' ')}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function runNpx(args, options = {}) {
|
|
102
|
+
return spawnSync(npxCommand(), args, {
|
|
103
|
+
cwd: options.cwd,
|
|
104
|
+
encoding: 'utf8',
|
|
105
|
+
stdio: options.stdio || 'pipe',
|
|
106
|
+
shell: process.platform === 'win32'
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Installs the full promoted set in a single `add` call (one clone, one
|
|
111
|
+
// filtered install). Never uses `--all`, which would also install
|
|
112
|
+
// in-progress/misc/deprecated skills.
|
|
113
|
+
function installSkills(targetDir, options = {}) {
|
|
114
|
+
const dryRun = options.dryRun === true;
|
|
115
|
+
const args = buildAddArgs(PROMOTED_SKILLS);
|
|
116
|
+
const command = formatCommand(args);
|
|
117
|
+
|
|
118
|
+
if (dryRun) {
|
|
119
|
+
return { ok: true, dryRun: true, command, message: `Dry run: would run ${command}` };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const result = runNpx(args, { cwd: targetDir, stdio: 'inherit' });
|
|
123
|
+
if (result.error || result.status !== 0) {
|
|
124
|
+
return { ok: false, dryRun: false, command, message: `failed to install promoted skills; run manually: ${command}` };
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
ok: true,
|
|
128
|
+
dryRun: false,
|
|
129
|
+
command,
|
|
130
|
+
message: `installed ${PROMOTED_SKILLS.length} promoted Matt Pocock skills to ${SKILLS_RELATIVE_DIR}`
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Updates each promoted skill by re-adding it individually and idempotently.
|
|
135
|
+
// This is deliberately NOT `npx skills update`: upstream has a known issue
|
|
136
|
+
// where updating one project skill can reinstall every skill from the
|
|
137
|
+
// source. Re-adding by explicit single-skill name only ever touches that
|
|
138
|
+
// skill's directory (verified manually against the upstream CLI).
|
|
139
|
+
// Trade-off: this does one network clone of the source repo per promoted
|
|
140
|
+
// skill (~PROMOTED_SKILLS.length calls), so it is noticeably slower than
|
|
141
|
+
// `installSkills`'s single batched call. That cost is accepted deliberately
|
|
142
|
+
// for the safety guarantee above; revisit only if upstream ships a scoped,
|
|
143
|
+
// non-destructive `update`.
|
|
144
|
+
function updateSkills(targetDir, options = {}) {
|
|
145
|
+
const dryRun = options.dryRun === true;
|
|
146
|
+
const results = [];
|
|
147
|
+
|
|
148
|
+
for (const name of PROMOTED_SKILLS) {
|
|
149
|
+
const args = buildAddArgs([name]);
|
|
150
|
+
const command = formatCommand(args);
|
|
151
|
+
|
|
152
|
+
if (dryRun) {
|
|
153
|
+
results.push({ name, ok: true, dryRun: true, command });
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const result = runNpx(args, { cwd: targetDir, stdio: 'pipe' });
|
|
158
|
+
const ok = !result.error && result.status === 0;
|
|
159
|
+
results.push({
|
|
160
|
+
name,
|
|
161
|
+
ok,
|
|
162
|
+
dryRun: false,
|
|
163
|
+
command,
|
|
164
|
+
error: ok ? null : (result.stderr || result.stdout || `exit ${result.status}`)
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const failed = results.filter(r => !r.ok);
|
|
169
|
+
const message = dryRun
|
|
170
|
+
? `Dry run: would update ${PROMOTED_SKILLS.length} promoted skills one by one`
|
|
171
|
+
: failed.length === 0
|
|
172
|
+
? `updated ${PROMOTED_SKILLS.length} promoted Matt Pocock skills`
|
|
173
|
+
: `updated ${results.length - failed.length}/${PROMOTED_SKILLS.length} promoted skills; failed: ${failed.map(f => f.name).join(', ')}`;
|
|
174
|
+
|
|
175
|
+
return { ok: failed.length === 0, dryRun, results, message };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
MATT_SKILLS_SOURCE,
|
|
180
|
+
PROMOTED_SKILLS,
|
|
181
|
+
SUPPORTED_AGENTS,
|
|
182
|
+
SKILLS_RELATIVE_DIR,
|
|
183
|
+
SETUP_SKILL_NAME,
|
|
184
|
+
npxCommand,
|
|
185
|
+
skillsDirFor,
|
|
186
|
+
isSkillInstalled,
|
|
187
|
+
checkSkills,
|
|
188
|
+
buildAddArgs,
|
|
189
|
+
formatCommand,
|
|
190
|
+
installSkills,
|
|
191
|
+
updateSkills
|
|
192
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const NATIVE_SKILLS = [
|
|
5
|
+
'using-git-worktrees',
|
|
6
|
+
'subagent-driven-development',
|
|
7
|
+
'finishing-a-development-branch'
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const NATIVE_SUPPORT_FILES = [
|
|
11
|
+
path.join('requesting-code-review', 'code-reviewer.md')
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const NATIVE_SKILL_SURFACES = [
|
|
15
|
+
{ id: 'agents', relativeDir: path.join('.agents', 'skills') },
|
|
16
|
+
{ id: 'claude', relativeDir: path.join('.claude', 'skills') }
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const nativeSkillsSourceDir = path.resolve(__dirname, '..', 'skills');
|
|
20
|
+
|
|
21
|
+
function sourcePath(relativePath) {
|
|
22
|
+
return path.join(nativeSkillsSourceDir, relativePath);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function skillSourceDir(name) {
|
|
26
|
+
return sourcePath(name);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function collectFiles(directory, relative = '') {
|
|
30
|
+
const files = [];
|
|
31
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
32
|
+
const entryRelative = path.join(relative, entry.name);
|
|
33
|
+
const entryPath = path.join(directory, entry.name);
|
|
34
|
+
if (entry.isDirectory()) files.push(...collectFiles(entryPath, entryRelative));
|
|
35
|
+
else if (entry.isFile()) files.push(entryRelative);
|
|
36
|
+
}
|
|
37
|
+
return files;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function ownedFiles() {
|
|
41
|
+
return [
|
|
42
|
+
...NATIVE_SKILLS.flatMap(name => collectFiles(skillSourceDir(name), name)),
|
|
43
|
+
...NATIVE_SUPPORT_FILES
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ownedSkillFiles(name) {
|
|
48
|
+
return collectFiles(skillSourceDir(name), name);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function targetPath(targetDir, surface, relativePath) {
|
|
52
|
+
return path.join(targetDir, surface.relativeDir, relativePath);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function checkNativeSkills(targetDir) {
|
|
56
|
+
const surfaces = {};
|
|
57
|
+
const skills = {};
|
|
58
|
+
|
|
59
|
+
for (const surface of NATIVE_SKILL_SURFACES) {
|
|
60
|
+
const missingFiles = ownedFiles().filter(relativePath => !fs.existsSync(targetPath(targetDir, surface, relativePath)));
|
|
61
|
+
const installed = [];
|
|
62
|
+
const missing = [];
|
|
63
|
+
for (const name of NATIVE_SKILLS) {
|
|
64
|
+
(ownedSkillFiles(name).every(relativePath => fs.existsSync(targetPath(targetDir, surface, relativePath)))
|
|
65
|
+
? installed
|
|
66
|
+
: missing).push(name);
|
|
67
|
+
}
|
|
68
|
+
const supportInstalled = NATIVE_SUPPORT_FILES.filter(relativePath => !missingFiles.includes(relativePath));
|
|
69
|
+
const supportMissing = NATIVE_SUPPORT_FILES.filter(relativePath => missingFiles.includes(relativePath));
|
|
70
|
+
surfaces[surface.id] = {
|
|
71
|
+
installed,
|
|
72
|
+
missing,
|
|
73
|
+
total: NATIVE_SKILLS.length,
|
|
74
|
+
missingFiles,
|
|
75
|
+
support: {
|
|
76
|
+
installed: supportInstalled,
|
|
77
|
+
missing: supportMissing,
|
|
78
|
+
total: NATIVE_SUPPORT_FILES.length
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const name of NATIVE_SKILLS) {
|
|
84
|
+
skills[name] = {};
|
|
85
|
+
for (const surface of NATIVE_SKILL_SURFACES) {
|
|
86
|
+
skills[name][surface.id] = ownedSkillFiles(name).every(relativePath => fs.existsSync(targetPath(targetDir, surface, relativePath)));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const installed = NATIVE_SKILLS.filter(name => NATIVE_SKILL_SURFACES.every(surface => skills[name][surface.id]));
|
|
91
|
+
const missing = NATIVE_SKILLS.filter(name => !installed.includes(name));
|
|
92
|
+
const missingFiles = Object.fromEntries(NATIVE_SKILL_SURFACES.map(surface => [surface.id, surfaces[surface.id].missingFiles]));
|
|
93
|
+
return {
|
|
94
|
+
installed,
|
|
95
|
+
missing,
|
|
96
|
+
total: NATIVE_SKILLS.length,
|
|
97
|
+
allInstalled: missingFiles.agents.length === 0 && missingFiles.claude.length === 0,
|
|
98
|
+
surfaces,
|
|
99
|
+
skills,
|
|
100
|
+
missingFiles
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function copyFile(source, destination) {
|
|
105
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
106
|
+
fs.copyFileSync(source, destination);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function installNativeSkills(targetDir, options = {}) {
|
|
110
|
+
const dryRun = options.dryRun === true;
|
|
111
|
+
const operations = [];
|
|
112
|
+
for (const surface of NATIVE_SKILL_SURFACES) {
|
|
113
|
+
for (const relativePath of ownedFiles()) {
|
|
114
|
+
const source = sourcePath(relativePath);
|
|
115
|
+
const destination = targetPath(targetDir, surface, relativePath);
|
|
116
|
+
operations.push({ surface: surface.id, relativePath, source, destination });
|
|
117
|
+
if (!dryRun) copyFile(source, destination);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
ok: true,
|
|
122
|
+
dryRun,
|
|
123
|
+
operations,
|
|
124
|
+
results: NATIVE_SKILLS.map(name => ({ name, ok: true, dryRun })),
|
|
125
|
+
message: dryRun
|
|
126
|
+
? `Dry run: would copy ${NATIVE_SKILLS.length} native skills to .agents/skills and .claude/skills`
|
|
127
|
+
: `installed ${NATIVE_SKILLS.length} thachvd-kit native skills to .agents/skills and .claude/skills`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function updateNativeSkills(targetDir, options = {}) {
|
|
132
|
+
const result = installNativeSkills(targetDir, options);
|
|
133
|
+
return {
|
|
134
|
+
...result,
|
|
135
|
+
message: result.dryRun
|
|
136
|
+
? `Dry run: would refresh ${NATIVE_SKILLS.length} native skills from bundled copies`
|
|
137
|
+
: `updated ${NATIVE_SKILLS.length} thachvd-kit native skills from bundled copies`
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = {
|
|
142
|
+
NATIVE_SKILLS,
|
|
143
|
+
NATIVE_SUPPORT_FILES,
|
|
144
|
+
NATIVE_SKILL_SURFACES,
|
|
145
|
+
nativeSkillsSourceDir,
|
|
146
|
+
checkNativeSkills,
|
|
147
|
+
installNativeSkills,
|
|
148
|
+
updateNativeSkills
|
|
149
|
+
};
|