thachvd-kit 1.0.37 → 1.0.39
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 -21
- package/README.md +303 -11
- package/THIRD_PARTY_NOTICES.md +49 -49
- package/bin/cli.js +1791 -1785
- package/bin/config.js +164 -0
- package/bin/entry.js +11 -1
- package/bin/native-skills.js +183 -149
- package/bin/spec-doctor.js +252 -0
- package/bin/spec-link.js +97 -0
- package/bin/spec-recipe.js +74 -0
- package/bin/spec-site.js +639 -0
- package/bin/spec-state.js +415 -0
- package/bin/spec.js +901 -0
- package/bin/upgrade.js +303 -303
- package/package.json +3 -3
- package/skills/finishing-a-development-branch/SKILL.md +240 -240
- package/skills/requesting-code-review/code-reviewer.md +198 -198
- package/skills/subagent-driven-development/SKILL.md +574 -574
- package/skills/subagent-driven-development/implementer-prompt.md +154 -154
- package/skills/subagent-driven-development/re-review-prompt.md +115 -115
- package/skills/subagent-driven-development/scripts/review-package +53 -53
- package/skills/subagent-driven-development/scripts/review-package.js +52 -52
- package/skills/subagent-driven-development/scripts/sdd-workspace +82 -82
- package/skills/subagent-driven-development/scripts/sdd-workspace-lib.js +62 -62
- package/skills/subagent-driven-development/scripts/sdd-workspace.js +15 -15
- package/skills/subagent-driven-development/scripts/task-brief +43 -43
- package/skills/subagent-driven-development/scripts/task-brief.js +46 -46
- package/skills/subagent-driven-development/task-reviewer-prompt.md +207 -207
- package/skills/system-discovery/SKILL.md +140 -0
- package/skills/system-reverse-engineer/SKILL.md +208 -0
- package/skills/system-spec-review/SKILL.md +177 -0
- package/skills/upstream.json +30 -30
- package/skills/using-git-worktrees/SKILL.md +175 -175
package/bin/config.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const CONFIG_RELATIVE_PATH = path.join('.thachvd', 'config.json');
|
|
5
|
+
const SUPPORTED_SPEC_LANGUAGES = new Set(['en', 'vi']);
|
|
6
|
+
const SUPPORTED_SPEC_PROFILES = new Set(['auto', 'backend', 'frontend', 'fullstack', 'mobile', 'infra']);
|
|
7
|
+
|
|
8
|
+
function resolveHome(env = process.env) {
|
|
9
|
+
return env.USERPROFILE || env.HOME || null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function configPath(homeDir) {
|
|
13
|
+
return path.join(homeDir, CONFIG_RELATIVE_PATH);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readConfig(homeDir) {
|
|
17
|
+
if (!homeDir) return {};
|
|
18
|
+
const filePath = configPath(homeDir);
|
|
19
|
+
if (!fs.existsSync(filePath)) return {};
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
22
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw new Error(`Cannot parse ${filePath}: ${error.message}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function writeConfig(homeDir, config) {
|
|
29
|
+
if (!homeDir) throw new Error('Cannot determine home directory from USERPROFILE or HOME.');
|
|
30
|
+
const filePath = configPath(homeDir);
|
|
31
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
32
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
33
|
+
return filePath;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeLanguage(value) {
|
|
37
|
+
const language = String(value || '').trim().toLowerCase();
|
|
38
|
+
if (!SUPPORTED_SPEC_LANGUAGES.has(language)) {
|
|
39
|
+
throw new Error(`Unsupported spec language: ${value}. Supported values: en, vi`);
|
|
40
|
+
}
|
|
41
|
+
return language;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeProfile(value) {
|
|
45
|
+
const profile = String(value || '').trim().toLowerCase();
|
|
46
|
+
if (!SUPPORTED_SPEC_PROFILES.has(profile)) {
|
|
47
|
+
throw new Error(`Unsupported spec profile: ${value}. Supported values: auto, backend, frontend, fullstack, mobile, infra`);
|
|
48
|
+
}
|
|
49
|
+
return profile;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getDefaultSpecProfile(env = process.env) {
|
|
53
|
+
const homeDir = resolveHome(env);
|
|
54
|
+
if (!homeDir) return null;
|
|
55
|
+
const config = readConfig(homeDir);
|
|
56
|
+
return config.spec_profile ? normalizeProfile(config.spec_profile) : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getDefaultSpecLanguage(env = process.env) {
|
|
60
|
+
const homeDir = resolveHome(env);
|
|
61
|
+
if (!homeDir) return null;
|
|
62
|
+
const config = readConfig(homeDir);
|
|
63
|
+
return config.spec_language ? normalizeLanguage(config.spec_language) : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function printHelp() {
|
|
67
|
+
console.log(`thachvd-kit config
|
|
68
|
+
|
|
69
|
+
Usage:
|
|
70
|
+
thachvd-kit config show
|
|
71
|
+
thachvd-kit config set spec-language en|vi
|
|
72
|
+
thachvd-kit config unset spec-language
|
|
73
|
+
thachvd-kit config set spec-profile auto|backend|frontend|fullstack|mobile|infra
|
|
74
|
+
thachvd-kit config unset spec-profile
|
|
75
|
+
|
|
76
|
+
The user-level config is stored at ~/.thachvd/config.json (or the platform-equivalent home directory).
|
|
77
|
+
Project-level --language and --profile always override their user defaults.
|
|
78
|
+
`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function runConfigCommand(args, options = {}) {
|
|
82
|
+
const env = options.env || process.env;
|
|
83
|
+
const homeDir = options.homeDir || resolveHome(env);
|
|
84
|
+
const command = args[0] || 'show';
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
if (command === '--help' || command === '-h' || command === 'help') {
|
|
88
|
+
printHelp();
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (command === 'show') {
|
|
93
|
+
const config = readConfig(homeDir);
|
|
94
|
+
console.log(`Config: ${homeDir ? configPath(homeDir) : '(home unavailable)'}`);
|
|
95
|
+
console.log(`spec-language: ${config.spec_language || '(default: en)'}`);
|
|
96
|
+
console.log(`spec-profile: ${config.spec_profile || '(default: auto)'}`);
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (command === 'set') {
|
|
101
|
+
if (!args[1] || !args[2]) {
|
|
102
|
+
throw new Error('Usage: thachvd-kit config set spec-language en|vi OR spec-profile auto|backend|frontend|fullstack|mobile|infra');
|
|
103
|
+
}
|
|
104
|
+
const config = readConfig(homeDir);
|
|
105
|
+
if (args[1] === 'spec-language') {
|
|
106
|
+
const language = normalizeLanguage(args[2]);
|
|
107
|
+
config.spec_language = language;
|
|
108
|
+
const filePath = writeConfig(homeDir, config);
|
|
109
|
+
console.log(`Updated ${filePath}`);
|
|
110
|
+
console.log(`spec-language: ${language}`);
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
if (args[1] === 'spec-profile') {
|
|
114
|
+
const profile = normalizeProfile(args[2]);
|
|
115
|
+
config.spec_profile = profile;
|
|
116
|
+
const filePath = writeConfig(homeDir, config);
|
|
117
|
+
console.log(`Updated ${filePath}`);
|
|
118
|
+
console.log(`spec-profile: ${profile}`);
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`Unknown config key: ${args[1]}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (command === 'unset') {
|
|
125
|
+
const config = readConfig(homeDir);
|
|
126
|
+
if (args[1] === 'spec-language') {
|
|
127
|
+
delete config.spec_language;
|
|
128
|
+
const filePath = writeConfig(homeDir, config);
|
|
129
|
+
console.log(`Updated ${filePath}`);
|
|
130
|
+
console.log('spec-language: (default: en)');
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
if (args[1] === 'spec-profile') {
|
|
134
|
+
delete config.spec_profile;
|
|
135
|
+
const filePath = writeConfig(homeDir, config);
|
|
136
|
+
console.log(`Updated ${filePath}`);
|
|
137
|
+
console.log('spec-profile: (default: auto)');
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
throw new Error('Usage: thachvd-kit config unset spec-language|spec-profile');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
throw new Error(`Unknown config command: ${command}`);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(`config ${command} failed: ${error.message}`);
|
|
146
|
+
process.exitCode = 1;
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
CONFIG_RELATIVE_PATH,
|
|
153
|
+
SUPPORTED_SPEC_LANGUAGES,
|
|
154
|
+
SUPPORTED_SPEC_PROFILES,
|
|
155
|
+
resolveHome,
|
|
156
|
+
configPath,
|
|
157
|
+
readConfig,
|
|
158
|
+
writeConfig,
|
|
159
|
+
normalizeLanguage,
|
|
160
|
+
normalizeProfile,
|
|
161
|
+
getDefaultSpecLanguage,
|
|
162
|
+
getDefaultSpecProfile,
|
|
163
|
+
runConfigCommand
|
|
164
|
+
};
|
package/bin/entry.js
CHANGED
|
@@ -11,7 +11,7 @@ function isInitInvocation(args) {
|
|
|
11
11
|
if (args.includes('--help') || args.includes('-h') || args.includes('--version') || args.includes('-v')) {
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
|
-
// Note: 'upgrade' and '
|
|
14
|
+
// Note: 'upgrade', 'global', 'config', and 'spec' are handled and returned above before this
|
|
15
15
|
// is ever called, so they never reach here.
|
|
16
16
|
const first = args[0];
|
|
17
17
|
return !first || first === 'init' || first.startsWith('-');
|
|
@@ -74,6 +74,16 @@ if (command === 'global') {
|
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
if (command === 'config') {
|
|
78
|
+
require('./config').runConfigCommand(args.slice(1));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (command === 'spec') {
|
|
83
|
+
require('./spec').runSpecCommand(args.slice(1));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
77
87
|
const shouldUpgradeGeneratedPolicy = isInitInvocation(args);
|
|
78
88
|
const beforeSnapshot = shouldUpgradeGeneratedPolicy ? snapshotPolicyFiles(process.cwd()) : null;
|
|
79
89
|
const force = args.includes('--yes') || args.includes('-y');
|
package/bin/native-skills.js
CHANGED
|
@@ -1,149 +1,183 @@
|
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
...
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
115
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
+
'system-discovery',
|
|
9
|
+
'system-reverse-engineer',
|
|
10
|
+
'system-spec-review'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const SYSTEM_SPEC_SKILLS = [
|
|
14
|
+
'system-discovery',
|
|
15
|
+
'system-reverse-engineer',
|
|
16
|
+
'system-spec-review'
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const NATIVE_SUPPORT_FILES = [
|
|
20
|
+
path.join('requesting-code-review', 'code-reviewer.md')
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const NATIVE_SKILL_SURFACES = [
|
|
24
|
+
{ id: 'agents', relativeDir: path.join('.agents', 'skills') },
|
|
25
|
+
{ id: 'claude', relativeDir: path.join('.claude', 'skills') }
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const nativeSkillsSourceDir = path.resolve(__dirname, '..', 'skills');
|
|
29
|
+
|
|
30
|
+
function sourcePath(relativePath) {
|
|
31
|
+
return path.join(nativeSkillsSourceDir, relativePath);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function skillSourceDir(name) {
|
|
35
|
+
return sourcePath(name);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function collectFiles(directory, relative = '') {
|
|
39
|
+
const files = [];
|
|
40
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
41
|
+
const entryRelative = path.join(relative, entry.name);
|
|
42
|
+
const entryPath = path.join(directory, entry.name);
|
|
43
|
+
if (entry.isDirectory()) files.push(...collectFiles(entryPath, entryRelative));
|
|
44
|
+
else if (entry.isFile()) files.push(entryRelative);
|
|
45
|
+
}
|
|
46
|
+
return files;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ownedFiles() {
|
|
50
|
+
return [
|
|
51
|
+
...NATIVE_SKILLS.flatMap(name => collectFiles(skillSourceDir(name), name)),
|
|
52
|
+
...NATIVE_SUPPORT_FILES
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ownedSkillFiles(name) {
|
|
57
|
+
return collectFiles(skillSourceDir(name), name);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function targetPath(targetDir, surface, relativePath) {
|
|
61
|
+
return path.join(targetDir, surface.relativeDir, relativePath);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function checkNativeSkills(targetDir) {
|
|
65
|
+
const surfaces = {};
|
|
66
|
+
const skills = {};
|
|
67
|
+
|
|
68
|
+
for (const surface of NATIVE_SKILL_SURFACES) {
|
|
69
|
+
const missingFiles = ownedFiles().filter(relativePath => !fs.existsSync(targetPath(targetDir, surface, relativePath)));
|
|
70
|
+
const installed = [];
|
|
71
|
+
const missing = [];
|
|
72
|
+
for (const name of NATIVE_SKILLS) {
|
|
73
|
+
(ownedSkillFiles(name).every(relativePath => fs.existsSync(targetPath(targetDir, surface, relativePath)))
|
|
74
|
+
? installed
|
|
75
|
+
: missing).push(name);
|
|
76
|
+
}
|
|
77
|
+
const supportInstalled = NATIVE_SUPPORT_FILES.filter(relativePath => !missingFiles.includes(relativePath));
|
|
78
|
+
const supportMissing = NATIVE_SUPPORT_FILES.filter(relativePath => missingFiles.includes(relativePath));
|
|
79
|
+
surfaces[surface.id] = {
|
|
80
|
+
installed,
|
|
81
|
+
missing,
|
|
82
|
+
total: NATIVE_SKILLS.length,
|
|
83
|
+
missingFiles,
|
|
84
|
+
support: {
|
|
85
|
+
installed: supportInstalled,
|
|
86
|
+
missing: supportMissing,
|
|
87
|
+
total: NATIVE_SUPPORT_FILES.length
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const name of NATIVE_SKILLS) {
|
|
93
|
+
skills[name] = {};
|
|
94
|
+
for (const surface of NATIVE_SKILL_SURFACES) {
|
|
95
|
+
skills[name][surface.id] = ownedSkillFiles(name).every(relativePath => fs.existsSync(targetPath(targetDir, surface, relativePath)));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const installed = NATIVE_SKILLS.filter(name => NATIVE_SKILL_SURFACES.every(surface => skills[name][surface.id]));
|
|
100
|
+
const missing = NATIVE_SKILLS.filter(name => !installed.includes(name));
|
|
101
|
+
const missingFiles = Object.fromEntries(NATIVE_SKILL_SURFACES.map(surface => [surface.id, surfaces[surface.id].missingFiles]));
|
|
102
|
+
return {
|
|
103
|
+
installed,
|
|
104
|
+
missing,
|
|
105
|
+
total: NATIVE_SKILLS.length,
|
|
106
|
+
allInstalled: missingFiles.agents.length === 0 && missingFiles.claude.length === 0,
|
|
107
|
+
surfaces,
|
|
108
|
+
skills,
|
|
109
|
+
missingFiles
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function copyFile(source, destination) {
|
|
114
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
115
|
+
fs.copyFileSync(source, destination);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function installNativeSkills(targetDir, options = {}) {
|
|
119
|
+
const dryRun = options.dryRun === true;
|
|
120
|
+
const operations = [];
|
|
121
|
+
for (const surface of NATIVE_SKILL_SURFACES) {
|
|
122
|
+
for (const relativePath of ownedFiles()) {
|
|
123
|
+
const source = sourcePath(relativePath);
|
|
124
|
+
const destination = targetPath(targetDir, surface, relativePath);
|
|
125
|
+
operations.push({ surface: surface.id, relativePath, source, destination });
|
|
126
|
+
if (!dryRun) copyFile(source, destination);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
ok: true,
|
|
131
|
+
dryRun,
|
|
132
|
+
operations,
|
|
133
|
+
results: NATIVE_SKILLS.map(name => ({ name, ok: true, dryRun })),
|
|
134
|
+
message: dryRun
|
|
135
|
+
? `Dry run: would copy ${NATIVE_SKILLS.length} native skills to .agents/skills and .claude/skills`
|
|
136
|
+
: `installed ${NATIVE_SKILLS.length} thachvd-kit native skills to .agents/skills and .claude/skills`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function installSystemSpecSkills(targetDir, options = {}) {
|
|
141
|
+
const dryRun = options.dryRun === true;
|
|
142
|
+
const operations = [];
|
|
143
|
+
for (const surface of NATIVE_SKILL_SURFACES) {
|
|
144
|
+
for (const name of SYSTEM_SPEC_SKILLS) {
|
|
145
|
+
for (const relativePath of ownedSkillFiles(name)) {
|
|
146
|
+
const source = sourcePath(relativePath);
|
|
147
|
+
const destination = targetPath(targetDir, surface, relativePath);
|
|
148
|
+
operations.push({ surface: surface.id, relativePath, source, destination });
|
|
149
|
+
if (!dryRun) copyFile(source, destination);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
ok: true,
|
|
155
|
+
dryRun,
|
|
156
|
+
operations,
|
|
157
|
+
message: dryRun
|
|
158
|
+
? `Dry run: would copy ${SYSTEM_SPEC_SKILLS.length} system-spec skills to workspace agent surfaces`
|
|
159
|
+
: `installed ${SYSTEM_SPEC_SKILLS.length} system-spec skills to workspace agent surfaces`
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function updateNativeSkills(targetDir, options = {}) {
|
|
164
|
+
const result = installNativeSkills(targetDir, options);
|
|
165
|
+
return {
|
|
166
|
+
...result,
|
|
167
|
+
message: result.dryRun
|
|
168
|
+
? `Dry run: would refresh ${NATIVE_SKILLS.length} native skills from bundled copies`
|
|
169
|
+
: `updated ${NATIVE_SKILLS.length} thachvd-kit native skills from bundled copies`
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
NATIVE_SKILLS,
|
|
175
|
+
SYSTEM_SPEC_SKILLS,
|
|
176
|
+
NATIVE_SUPPORT_FILES,
|
|
177
|
+
NATIVE_SKILL_SURFACES,
|
|
178
|
+
nativeSkillsSourceDir,
|
|
179
|
+
checkNativeSkills,
|
|
180
|
+
installNativeSkills,
|
|
181
|
+
installSystemSpecSkills,
|
|
182
|
+
updateNativeSkills
|
|
183
|
+
};
|