thoth-agents 0.1.2 → 0.1.5
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/dist/cli/index.js +513 -493
- package/dist/harness/adapters/codex.d.ts +1 -0
- package/dist/harness/types.d.ts +1 -0
- package/dist/harness/writers/skill-layout.d.ts +1 -0
- package/package.json +19 -16
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025
|
|
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.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
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/dist/cli/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { pathToFileURL } from "url";
|
|
|
6
6
|
// src/harness/adapters/codex.ts
|
|
7
7
|
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
8
8
|
import { dirname as dirname2, resolve } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
9
10
|
|
|
10
11
|
// src/agents/prompt-dialects.ts
|
|
11
12
|
var OPENCODE_CAPABILITIES = {
|
|
@@ -2461,7 +2462,8 @@ function renderCodexSkillLayout(input) {
|
|
|
2461
2462
|
for (const skill of [...input.skills].sort(
|
|
2462
2463
|
(left, right) => left.name.localeCompare(right.name)
|
|
2463
2464
|
)) {
|
|
2464
|
-
const
|
|
2465
|
+
const sourceBaseRoot = input.packageRoot ?? input.projectRoot;
|
|
2466
|
+
const sourceRoot = path2.join(sourceBaseRoot, skill.sourcePath);
|
|
2465
2467
|
const files = collectFiles(sourceRoot);
|
|
2466
2468
|
if (files.length === 0) {
|
|
2467
2469
|
diagnostics.push({
|
|
@@ -2477,7 +2479,7 @@ function renderCodexSkillLayout(input) {
|
|
|
2477
2479
|
for (const file of files) {
|
|
2478
2480
|
const relative4 = normalizePath2(path2.relative(sourceRoot, file));
|
|
2479
2481
|
const content = fs2.readFileSync(file, "utf8");
|
|
2480
|
-
const sourcePath = normalizePath2(path2.relative(
|
|
2482
|
+
const sourcePath = normalizePath2(path2.relative(sourceBaseRoot, file));
|
|
2481
2483
|
for (const mode of outputModes) {
|
|
2482
2484
|
const config = OUTPUT_MODE_CONFIG[mode];
|
|
2483
2485
|
const outputPath = `${config.basePath}/${skill.name}/${relative4}`;
|
|
@@ -2512,14 +2514,19 @@ function renderCodexSkillLayout(input) {
|
|
|
2512
2514
|
}
|
|
2513
2515
|
|
|
2514
2516
|
// src/harness/adapters/codex.ts
|
|
2515
|
-
function readRootPackageVersion(
|
|
2516
|
-
const packageJsonPath = findRootPackageJsonPath([
|
|
2517
|
+
function readRootPackageVersion(context) {
|
|
2518
|
+
const packageJsonPath = findRootPackageJsonPath([
|
|
2519
|
+
...hasCodexPackageRoot(context) ? [context.packageRoot] : [],
|
|
2520
|
+
context.projectRoot,
|
|
2521
|
+
process.cwd(),
|
|
2522
|
+
fileURLToPath(new URL(".", import.meta.url))
|
|
2523
|
+
]);
|
|
2517
2524
|
return readPackageJsonVersion(packageJsonPath);
|
|
2518
2525
|
}
|
|
2519
|
-
function createCodexPluginPackageManifest(
|
|
2526
|
+
function createCodexPluginPackageManifest(context) {
|
|
2520
2527
|
return {
|
|
2521
2528
|
name: "thoth-agents",
|
|
2522
|
-
version: readRootPackageVersion(
|
|
2529
|
+
version: readRootPackageVersion(context),
|
|
2523
2530
|
description: "Delegate-first OpenCode plugin with seven agents, thoth-mem persistence, and bundled SDD skills."
|
|
2524
2531
|
};
|
|
2525
2532
|
}
|
|
@@ -2726,6 +2733,9 @@ function codexSurfaceHasField(surfaceId, field) {
|
|
|
2726
2733
|
function hasCodexConfig(context) {
|
|
2727
2734
|
return "config" in context;
|
|
2728
2735
|
}
|
|
2736
|
+
function hasCodexPackageRoot(context) {
|
|
2737
|
+
return "packageRoot" in context && typeof context.packageRoot === "string" && context.packageRoot.length > 0;
|
|
2738
|
+
}
|
|
2729
2739
|
function renderAgentArtifacts({ config }) {
|
|
2730
2740
|
const artifacts = [];
|
|
2731
2741
|
const diagnostics = [];
|
|
@@ -2845,12 +2855,13 @@ var codexAdapter = {
|
|
|
2845
2855
|
const skillOutputModes = resolveSkillOutputModes(context);
|
|
2846
2856
|
const skillLayout = renderCodexSkillLayout({
|
|
2847
2857
|
projectRoot: context.projectRoot,
|
|
2858
|
+
...hasCodexPackageRoot(context) ? { packageRoot: context.packageRoot } : {},
|
|
2848
2859
|
skills: getSkillRegistry(),
|
|
2849
2860
|
surfaceId: "plugin-skills-directory",
|
|
2850
2861
|
outputModes: skillOutputModes
|
|
2851
2862
|
});
|
|
2852
2863
|
const pluginPackage = renderCodexPluginPackage({
|
|
2853
|
-
manifest: createCodexPluginPackageManifest(context
|
|
2864
|
+
manifest: createCodexPluginPackageManifest(context),
|
|
2854
2865
|
assets: [
|
|
2855
2866
|
{
|
|
2856
2867
|
surfaceId: "plugin-skills-directory",
|
|
@@ -2901,14 +2912,15 @@ import { cwd } from "process";
|
|
|
2901
2912
|
|
|
2902
2913
|
// src/cli/codex-install.ts
|
|
2903
2914
|
import {
|
|
2904
|
-
copyFileSync as
|
|
2905
|
-
existsSync as
|
|
2906
|
-
mkdirSync as
|
|
2907
|
-
readFileSync as
|
|
2908
|
-
rmSync,
|
|
2909
|
-
writeFileSync as
|
|
2915
|
+
copyFileSync as copyFileSync4,
|
|
2916
|
+
existsSync as existsSync9,
|
|
2917
|
+
mkdirSync as mkdirSync5,
|
|
2918
|
+
readFileSync as readFileSync7,
|
|
2919
|
+
rmSync as rmSync2,
|
|
2920
|
+
writeFileSync as writeFileSync4
|
|
2910
2921
|
} from "fs";
|
|
2911
|
-
import { basename, dirname as
|
|
2922
|
+
import { basename, dirname as dirname5, isAbsolute, join as join8, relative as relative3 } from "path";
|
|
2923
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2912
2924
|
|
|
2913
2925
|
// src/harness/codex-plugin-paths.ts
|
|
2914
2926
|
import { join as join4 } from "path";
|
|
@@ -3109,116 +3121,496 @@ function resolveCodexTargets(options) {
|
|
|
3109
3121
|
};
|
|
3110
3122
|
}
|
|
3111
3123
|
|
|
3112
|
-
// src/cli/
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3124
|
+
// src/cli/custom-skills.ts
|
|
3125
|
+
import {
|
|
3126
|
+
copyFileSync as copyFileSync3,
|
|
3127
|
+
existsSync as existsSync8,
|
|
3128
|
+
mkdirSync as mkdirSync4,
|
|
3129
|
+
readdirSync as readdirSync3,
|
|
3130
|
+
rmSync,
|
|
3131
|
+
statSync as statSync3
|
|
3132
|
+
} from "fs";
|
|
3133
|
+
import { dirname as dirname4, join as join7, parse } from "path";
|
|
3134
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3135
|
+
|
|
3136
|
+
// src/cli/skill-manifest.ts
|
|
3137
|
+
import { createHash as createHash3 } from "crypto";
|
|
3138
|
+
import {
|
|
3139
|
+
existsSync as existsSync7,
|
|
3140
|
+
mkdirSync as mkdirSync3,
|
|
3141
|
+
readdirSync as readdirSync2,
|
|
3142
|
+
readFileSync as readFileSync6,
|
|
3143
|
+
statSync as statSync2,
|
|
3144
|
+
writeFileSync as writeFileSync3
|
|
3145
|
+
} from "fs";
|
|
3146
|
+
import { join as join6, relative as relative2 } from "path";
|
|
3147
|
+
var SHARED_SKILL_DIRECTORY = "_shared";
|
|
3148
|
+
var SKILLS_SOURCE_ROOT = join6("src", "skills");
|
|
3149
|
+
var MANIFEST_FILE_NAME = ".skill-manifest.json";
|
|
3150
|
+
function getManifestPath() {
|
|
3151
|
+
return join6(getCustomSkillsDir(), MANIFEST_FILE_NAME);
|
|
3152
|
+
}
|
|
3153
|
+
function listFilesRecursive(dirPath) {
|
|
3154
|
+
if (!existsSync7(dirPath)) {
|
|
3155
|
+
return [];
|
|
3121
3156
|
}
|
|
3122
|
-
|
|
3123
|
-
|
|
3157
|
+
const files = [];
|
|
3158
|
+
const entries = readdirSync2(dirPath).sort(
|
|
3159
|
+
(left, right) => left.localeCompare(right)
|
|
3160
|
+
);
|
|
3161
|
+
for (const entry of entries) {
|
|
3162
|
+
const entryPath = join6(dirPath, entry);
|
|
3163
|
+
const stat = statSync2(entryPath);
|
|
3164
|
+
if (stat.isDirectory()) {
|
|
3165
|
+
files.push(...listFilesRecursive(entryPath));
|
|
3166
|
+
continue;
|
|
3167
|
+
}
|
|
3168
|
+
files.push(entryPath);
|
|
3169
|
+
}
|
|
3170
|
+
return files;
|
|
3124
3171
|
}
|
|
3125
|
-
function
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
if (
|
|
3129
|
-
|
|
3130
|
-
|
|
3172
|
+
function readPackageVersion(packageRoot) {
|
|
3173
|
+
const packageJsonPath = join6(packageRoot, "package.json");
|
|
3174
|
+
const packageJson = JSON.parse(readFileSync6(packageJsonPath, "utf-8"));
|
|
3175
|
+
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
3176
|
+
throw new Error(`Invalid package version in ${packageJsonPath}`);
|
|
3177
|
+
}
|
|
3178
|
+
return packageJson.version;
|
|
3131
3179
|
}
|
|
3132
|
-
function
|
|
3133
|
-
|
|
3180
|
+
function readManifest() {
|
|
3181
|
+
const manifestPath = getManifestPath();
|
|
3182
|
+
if (!existsSync7(manifestPath)) {
|
|
3183
|
+
return null;
|
|
3184
|
+
}
|
|
3185
|
+
try {
|
|
3186
|
+
return JSON.parse(readFileSync6(manifestPath, "utf-8"));
|
|
3187
|
+
} catch (error) {
|
|
3188
|
+
console.warn(`Failed to read skill manifest: ${manifestPath}`, error);
|
|
3189
|
+
return null;
|
|
3190
|
+
}
|
|
3134
3191
|
}
|
|
3135
|
-
function
|
|
3136
|
-
const
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3192
|
+
function writeManifest(manifest) {
|
|
3193
|
+
const manifestPath = getManifestPath();
|
|
3194
|
+
mkdirSync3(getCustomSkillsDir(), { recursive: true });
|
|
3195
|
+
writeFileSync3(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
3196
|
+
`);
|
|
3140
3197
|
}
|
|
3141
|
-
function
|
|
3142
|
-
|
|
3143
|
-
|
|
3198
|
+
function computeSkillHash(skillDirPath) {
|
|
3199
|
+
const hash = createHash3("sha256");
|
|
3200
|
+
for (const filePath of listFilesRecursive(skillDirPath)) {
|
|
3201
|
+
hash.update(`${relative2(skillDirPath, filePath)}
|
|
3202
|
+
`);
|
|
3203
|
+
hash.update(readFileSync6(filePath));
|
|
3204
|
+
hash.update("\n");
|
|
3205
|
+
}
|
|
3206
|
+
return hash.digest("hex");
|
|
3207
|
+
}
|
|
3208
|
+
function findRemovedSkills(manifest) {
|
|
3209
|
+
const currentSkillNames = new Set(CUSTOM_SKILLS.map((skill) => skill.name));
|
|
3210
|
+
return Object.keys(manifest.skills).filter(
|
|
3211
|
+
(skillName) => !currentSkillNames.has(skillName)
|
|
3144
3212
|
);
|
|
3145
3213
|
}
|
|
3146
|
-
function
|
|
3214
|
+
function checkSkillsNeedUpdate(packageRoot) {
|
|
3215
|
+
const manifest = readManifest();
|
|
3216
|
+
const pluginVersion = readPackageVersion(packageRoot);
|
|
3217
|
+
const sharedHash = computeSkillHash(
|
|
3218
|
+
join6(packageRoot, SKILLS_SOURCE_ROOT, SHARED_SKILL_DIRECTORY)
|
|
3219
|
+
);
|
|
3220
|
+
const versionChanged = manifest !== null && manifest.pluginVersion !== pluginVersion;
|
|
3221
|
+
const sharedChanged = manifest !== null && manifest.sharedHash !== sharedHash;
|
|
3222
|
+
const removedSkills = manifest ? findRemovedSkills(manifest) : [];
|
|
3223
|
+
const skillsNeedingUpdate = CUSTOM_SKILLS.flatMap((skill) => {
|
|
3224
|
+
const sourcePath = join6(packageRoot, skill.sourcePath);
|
|
3225
|
+
const targetPath = join6(getCustomSkillsDir(), skill.name);
|
|
3226
|
+
const sourceHash = computeSkillHash(sourcePath);
|
|
3227
|
+
const manifestEntry = manifest?.skills[skill.name];
|
|
3228
|
+
const reasons = [];
|
|
3229
|
+
if (!manifest) {
|
|
3230
|
+
reasons.push("manifest-missing");
|
|
3231
|
+
}
|
|
3232
|
+
if (versionChanged) {
|
|
3233
|
+
reasons.push("version-change");
|
|
3234
|
+
}
|
|
3235
|
+
if (sharedChanged) {
|
|
3236
|
+
reasons.push("shared-hash-mismatch");
|
|
3237
|
+
}
|
|
3238
|
+
if (!manifestEntry) {
|
|
3239
|
+
reasons.push("new-skill");
|
|
3240
|
+
} else if (manifestEntry.hash !== sourceHash) {
|
|
3241
|
+
reasons.push("hash-mismatch");
|
|
3242
|
+
}
|
|
3243
|
+
if (!existsSync7(targetPath)) {
|
|
3244
|
+
reasons.push("missing-install");
|
|
3245
|
+
}
|
|
3246
|
+
if (reasons.length === 0) {
|
|
3247
|
+
return [];
|
|
3248
|
+
}
|
|
3249
|
+
return [
|
|
3250
|
+
{
|
|
3251
|
+
skill,
|
|
3252
|
+
sourceHash,
|
|
3253
|
+
targetPath,
|
|
3254
|
+
reasons
|
|
3255
|
+
}
|
|
3256
|
+
];
|
|
3257
|
+
});
|
|
3147
3258
|
return {
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
},
|
|
3157
|
-
category: "Productivity"
|
|
3259
|
+
pluginVersion,
|
|
3260
|
+
sharedHash,
|
|
3261
|
+
manifest,
|
|
3262
|
+
versionChanged,
|
|
3263
|
+
sharedChanged,
|
|
3264
|
+
needsUpdate: skillsNeedingUpdate.length > 0 || removedSkills.length > 0,
|
|
3265
|
+
skillsNeedingUpdate,
|
|
3266
|
+
removedSkills
|
|
3158
3267
|
};
|
|
3159
3268
|
}
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
}
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3269
|
+
|
|
3270
|
+
// src/cli/custom-skills.ts
|
|
3271
|
+
var SHARED_SKILL_DIRECTORY2 = "_shared";
|
|
3272
|
+
var SHARED_SKILL_SOURCE_PATH = `src/skills/${SHARED_SKILL_DIRECTORY2}`;
|
|
3273
|
+
var CUSTOM_SKILLS = BUNDLED_SKILL_REGISTRY.map(
|
|
3274
|
+
(skill) => ({
|
|
3275
|
+
name: skill.name,
|
|
3276
|
+
description: skill.description,
|
|
3277
|
+
allowedAgents: [...skill.allowedRoles],
|
|
3278
|
+
sourcePath: skill.sourcePath
|
|
3279
|
+
})
|
|
3280
|
+
);
|
|
3281
|
+
function getCustomSkillsDir() {
|
|
3282
|
+
return join7(getConfigDir(), "skills");
|
|
3169
3283
|
}
|
|
3170
|
-
function
|
|
3171
|
-
if (!
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3284
|
+
function copyDirRecursive(src, dest) {
|
|
3285
|
+
if (!existsSync8(dest)) {
|
|
3286
|
+
mkdirSync4(dest, { recursive: true });
|
|
3287
|
+
}
|
|
3288
|
+
const entries = readdirSync3(src);
|
|
3289
|
+
for (const entry of entries) {
|
|
3290
|
+
const srcPath = join7(src, entry);
|
|
3291
|
+
const destPath = join7(dest, entry);
|
|
3292
|
+
const stat = statSync3(srcPath);
|
|
3293
|
+
if (stat.isDirectory()) {
|
|
3294
|
+
copyDirRecursive(srcPath, destPath);
|
|
3295
|
+
} else {
|
|
3296
|
+
const destDir = dirname4(destPath);
|
|
3297
|
+
if (!existsSync8(destDir)) {
|
|
3298
|
+
mkdirSync4(destDir, { recursive: true });
|
|
3299
|
+
}
|
|
3300
|
+
copyFileSync3(srcPath, destPath);
|
|
3176
3301
|
}
|
|
3177
|
-
return {
|
|
3178
|
-
version: MANAGED_MODEL_STATE_VERSION,
|
|
3179
|
-
models: Object.fromEntries(
|
|
3180
|
-
Object.entries(parsed.models).filter(
|
|
3181
|
-
(entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
|
|
3182
|
-
)
|
|
3183
|
-
)
|
|
3184
|
-
};
|
|
3185
|
-
} catch {
|
|
3186
|
-
return emptyManagedModelState();
|
|
3187
3302
|
}
|
|
3188
3303
|
}
|
|
3189
|
-
function
|
|
3190
|
-
const
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
}
|
|
3194
|
-
|
|
3195
|
-
|
|
3304
|
+
function installSharedSkillAssets(packageRoot) {
|
|
3305
|
+
const sharedSourcePath = join7(packageRoot, SHARED_SKILL_SOURCE_PATH);
|
|
3306
|
+
const sharedTargetPath = join7(getCustomSkillsDir(), SHARED_SKILL_DIRECTORY2);
|
|
3307
|
+
if (!existsSync8(sharedSourcePath)) {
|
|
3308
|
+
console.error(`Custom skill shared assets not found: ${sharedSourcePath}`);
|
|
3309
|
+
return false;
|
|
3310
|
+
}
|
|
3311
|
+
rmSync(sharedTargetPath, { recursive: true, force: true });
|
|
3312
|
+
copyDirRecursive(sharedSourcePath, sharedTargetPath);
|
|
3313
|
+
return true;
|
|
3196
3314
|
}
|
|
3197
|
-
function
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3315
|
+
function findPackageRoot(startDir) {
|
|
3316
|
+
let currentDir = startDir;
|
|
3317
|
+
const filesystemRoot = parse(startDir).root;
|
|
3318
|
+
while (true) {
|
|
3319
|
+
if (existsSync8(join7(currentDir, "package.json")) && existsSync8(join7(currentDir, "src", "skills"))) {
|
|
3320
|
+
return currentDir;
|
|
3321
|
+
}
|
|
3322
|
+
if (currentDir === filesystemRoot) {
|
|
3323
|
+
return null;
|
|
3324
|
+
}
|
|
3325
|
+
const parentDir = dirname4(currentDir);
|
|
3326
|
+
if (parentDir === currentDir) {
|
|
3327
|
+
return null;
|
|
3328
|
+
}
|
|
3329
|
+
currentDir = parentDir;
|
|
3201
3330
|
}
|
|
3202
|
-
return `${rendered}
|
|
3203
|
-
${content}`;
|
|
3204
3331
|
}
|
|
3205
|
-
function
|
|
3206
|
-
|
|
3332
|
+
function resolvePackageRoot(packageRoot) {
|
|
3333
|
+
if (packageRoot) {
|
|
3334
|
+
return packageRoot;
|
|
3335
|
+
}
|
|
3336
|
+
const moduleDir = fileURLToPath2(new URL(".", import.meta.url));
|
|
3337
|
+
return findPackageRoot(moduleDir) ?? fileURLToPath2(new URL("../..", import.meta.url));
|
|
3207
3338
|
}
|
|
3208
|
-
function
|
|
3209
|
-
const
|
|
3210
|
-
const
|
|
3211
|
-
if (!
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
return options.renderedContent;
|
|
3339
|
+
function installCustomSkillFiles(skill, packageRoot) {
|
|
3340
|
+
const sourcePath = join7(packageRoot, skill.sourcePath);
|
|
3341
|
+
const targetPath = join7(getCustomSkillsDir(), skill.name);
|
|
3342
|
+
if (!existsSync8(sourcePath)) {
|
|
3343
|
+
console.error(`Custom skill source not found: ${sourcePath}`);
|
|
3344
|
+
return false;
|
|
3215
3345
|
}
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3346
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
3347
|
+
copyDirRecursive(sourcePath, targetPath);
|
|
3348
|
+
return true;
|
|
3349
|
+
}
|
|
3350
|
+
function removeObsoleteSkills(removedSkillNames) {
|
|
3351
|
+
const removedSkills = [];
|
|
3352
|
+
for (const skillName of removedSkillNames) {
|
|
3353
|
+
const targetPath = join7(getCustomSkillsDir(), skillName);
|
|
3354
|
+
try {
|
|
3355
|
+
console.log(`Removing obsolete bundled skill: ${skillName}`);
|
|
3356
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
3357
|
+
removedSkills.push(skillName);
|
|
3358
|
+
} catch (error) {
|
|
3359
|
+
console.warn(`Failed to remove obsolete bundled skill: ${skillName}`);
|
|
3360
|
+
console.warn(error);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
return removedSkills;
|
|
3364
|
+
}
|
|
3365
|
+
function buildManifest(packageRoot, updatedSkills, previousManifest, pluginVersion, sharedHash) {
|
|
3366
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3367
|
+
const updatedSkillNames = new Set(
|
|
3368
|
+
updatedSkills.map(({ skill }) => skill.name)
|
|
3369
|
+
);
|
|
3370
|
+
const skills = Object.fromEntries(
|
|
3371
|
+
CUSTOM_SKILLS.map((skill) => {
|
|
3372
|
+
const previousEntry = previousManifest?.skills[skill.name];
|
|
3373
|
+
const nextInstalledAt = updatedSkillNames.has(skill.name) ? installedAt : previousEntry?.installedAt ?? installedAt;
|
|
3374
|
+
return [
|
|
3375
|
+
skill.name,
|
|
3376
|
+
{
|
|
3377
|
+
hash: computeSkillHash(join7(packageRoot, skill.sourcePath)),
|
|
3378
|
+
installedAt: nextInstalledAt
|
|
3379
|
+
}
|
|
3380
|
+
];
|
|
3381
|
+
})
|
|
3382
|
+
);
|
|
3383
|
+
return {
|
|
3384
|
+
pluginVersion,
|
|
3385
|
+
sharedHash,
|
|
3386
|
+
skills
|
|
3387
|
+
};
|
|
3388
|
+
}
|
|
3389
|
+
function pruneRemovedSkillsFromManifest(manifest, removedSkills) {
|
|
3390
|
+
const removedSkillNames = new Set(removedSkills);
|
|
3391
|
+
return {
|
|
3392
|
+
...manifest,
|
|
3393
|
+
skills: Object.fromEntries(
|
|
3394
|
+
Object.entries(manifest.skills).filter(
|
|
3395
|
+
([skillName]) => !removedSkillNames.has(skillName)
|
|
3396
|
+
)
|
|
3397
|
+
)
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
function installCustomSkills(packageRoot = resolvePackageRoot()) {
|
|
3401
|
+
const updateCheck = checkSkillsNeedUpdate(packageRoot);
|
|
3402
|
+
if (!updateCheck.needsUpdate) {
|
|
3403
|
+
return {
|
|
3404
|
+
success: true,
|
|
3405
|
+
updatedSkills: [],
|
|
3406
|
+
skippedSkills: [...CUSTOM_SKILLS],
|
|
3407
|
+
failedSkills: [],
|
|
3408
|
+
removedSkills: []
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
const removedSkills = removeObsoleteSkills(updateCheck.removedSkills);
|
|
3412
|
+
if (removedSkills.length > 0 && updateCheck.manifest) {
|
|
3413
|
+
writeManifest(
|
|
3414
|
+
pruneRemovedSkillsFromManifest(updateCheck.manifest, removedSkills)
|
|
3415
|
+
);
|
|
3416
|
+
}
|
|
3417
|
+
if (updateCheck.skillsNeedingUpdate.length === 0) {
|
|
3418
|
+
writeManifest(
|
|
3419
|
+
buildManifest(
|
|
3420
|
+
packageRoot,
|
|
3421
|
+
[],
|
|
3422
|
+
updateCheck.manifest,
|
|
3423
|
+
updateCheck.pluginVersion,
|
|
3424
|
+
updateCheck.sharedHash
|
|
3425
|
+
)
|
|
3426
|
+
);
|
|
3427
|
+
return {
|
|
3428
|
+
success: true,
|
|
3429
|
+
updatedSkills: [],
|
|
3430
|
+
skippedSkills: [...CUSTOM_SKILLS],
|
|
3431
|
+
failedSkills: [],
|
|
3432
|
+
removedSkills
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
if (!installSharedSkillAssets(packageRoot)) {
|
|
3436
|
+
return {
|
|
3437
|
+
success: false,
|
|
3438
|
+
updatedSkills: [],
|
|
3439
|
+
skippedSkills: [],
|
|
3440
|
+
failedSkills: updateCheck.skillsNeedingUpdate.map(
|
|
3441
|
+
({ skill, reasons }) => ({
|
|
3442
|
+
skill,
|
|
3443
|
+
reasons
|
|
3444
|
+
})
|
|
3445
|
+
),
|
|
3446
|
+
removedSkills
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
const updatesBySkillName = new Map(
|
|
3450
|
+
updateCheck.skillsNeedingUpdate.map((entry) => [entry.skill.name, entry])
|
|
3451
|
+
);
|
|
3452
|
+
const updatedSkills = [];
|
|
3453
|
+
const skippedSkills = [];
|
|
3454
|
+
const failedSkills = [];
|
|
3455
|
+
for (const skill of CUSTOM_SKILLS) {
|
|
3456
|
+
const pendingUpdate = updatesBySkillName.get(skill.name);
|
|
3457
|
+
if (!pendingUpdate) {
|
|
3458
|
+
skippedSkills.push(skill);
|
|
3459
|
+
continue;
|
|
3460
|
+
}
|
|
3461
|
+
if (installCustomSkillFiles(skill, packageRoot)) {
|
|
3462
|
+
updatedSkills.push({
|
|
3463
|
+
skill,
|
|
3464
|
+
reasons: pendingUpdate.reasons
|
|
3465
|
+
});
|
|
3466
|
+
continue;
|
|
3467
|
+
}
|
|
3468
|
+
failedSkills.push({
|
|
3469
|
+
skill,
|
|
3470
|
+
reasons: pendingUpdate.reasons
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
if (failedSkills.length > 0) {
|
|
3474
|
+
return {
|
|
3475
|
+
success: false,
|
|
3476
|
+
updatedSkills,
|
|
3477
|
+
skippedSkills,
|
|
3478
|
+
failedSkills,
|
|
3479
|
+
removedSkills
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
writeManifest(
|
|
3483
|
+
buildManifest(
|
|
3484
|
+
packageRoot,
|
|
3485
|
+
updatedSkills,
|
|
3486
|
+
updateCheck.manifest,
|
|
3487
|
+
updateCheck.pluginVersion,
|
|
3488
|
+
updateCheck.sharedHash
|
|
3489
|
+
)
|
|
3490
|
+
);
|
|
3491
|
+
return {
|
|
3492
|
+
success: true,
|
|
3493
|
+
updatedSkills,
|
|
3494
|
+
skippedSkills,
|
|
3495
|
+
failedSkills,
|
|
3496
|
+
removedSkills
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
// src/cli/codex-install.ts
|
|
3501
|
+
var ROOT_START = "<!-- thoth-agents:codex-root:start -->";
|
|
3502
|
+
var ROOT_END = "<!-- thoth-agents:codex-root:end -->";
|
|
3503
|
+
var MANAGED_MODEL_STATE_VERSION = 1;
|
|
3504
|
+
function mergeManagedBlock(existing, managedBlock) {
|
|
3505
|
+
const start = existing.indexOf(ROOT_START);
|
|
3506
|
+
const end = existing.indexOf(ROOT_END);
|
|
3507
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
3508
|
+
return `${existing.slice(0, start)}${managedBlock}${existing.slice(end + ROOT_END.length).replace(/^\s*\n/, "")}`;
|
|
3509
|
+
}
|
|
3510
|
+
return `${existing}${existing.endsWith("\n") || existing.length === 0 ? "" : "\n"}
|
|
3511
|
+
${managedBlock}`;
|
|
3512
|
+
}
|
|
3513
|
+
function writeTextWithBackup(path3, content) {
|
|
3514
|
+
mkdirSync5(dirname5(path3), { recursive: true });
|
|
3515
|
+
if (existsSync9(path3) && readFileSync7(path3, "utf8") === content) return false;
|
|
3516
|
+
if (existsSync9(path3)) copyFileSync4(path3, `${path3}.bak`);
|
|
3517
|
+
writeFileSync4(path3, content);
|
|
3518
|
+
return true;
|
|
3519
|
+
}
|
|
3520
|
+
function packageArtifactTarget(packageRoot, artifact) {
|
|
3521
|
+
return join8(packageRoot, codexPluginRootArtifactPath(artifact.path));
|
|
3522
|
+
}
|
|
3523
|
+
function resolvePackageRoot2(packageRoot) {
|
|
3524
|
+
if (packageRoot) return packageRoot;
|
|
3525
|
+
return findPackageRoot(fileURLToPath3(new URL(".", import.meta.url))) ?? void 0;
|
|
3526
|
+
}
|
|
3527
|
+
function normalizeRelativeMarketplacePath(path3) {
|
|
3528
|
+
const normalized = path3.replaceAll("\\", "/");
|
|
3529
|
+
if (isAbsolute(path3) || /^[A-Za-z]:\//.test(normalized)) return normalized;
|
|
3530
|
+
if (normalized.startsWith("./")) return normalized;
|
|
3531
|
+
return `./${normalized}`;
|
|
3532
|
+
}
|
|
3533
|
+
function marketplaceSourcePath(homeDir, personalPluginRoot) {
|
|
3534
|
+
return normalizeRelativeMarketplacePath(
|
|
3535
|
+
relative3(homeDir, personalPluginRoot)
|
|
3536
|
+
);
|
|
3537
|
+
}
|
|
3538
|
+
function managedMarketplaceEntry(homeDir, personalPluginRoot) {
|
|
3539
|
+
return {
|
|
3540
|
+
name: "thoth-agents",
|
|
3541
|
+
source: {
|
|
3542
|
+
source: "local",
|
|
3543
|
+
path: marketplaceSourcePath(homeDir, personalPluginRoot)
|
|
3544
|
+
},
|
|
3545
|
+
policy: {
|
|
3546
|
+
installation: "AVAILABLE",
|
|
3547
|
+
authentication: "ON_INSTALL"
|
|
3548
|
+
},
|
|
3549
|
+
category: "Productivity"
|
|
3550
|
+
};
|
|
3551
|
+
}
|
|
3552
|
+
function stableJson3(value) {
|
|
3553
|
+
return `${JSON.stringify(value, null, 2)}
|
|
3554
|
+
`;
|
|
3555
|
+
}
|
|
3556
|
+
function emptyManagedModelState() {
|
|
3557
|
+
return {
|
|
3558
|
+
version: MANAGED_MODEL_STATE_VERSION,
|
|
3559
|
+
models: {}
|
|
3560
|
+
};
|
|
3561
|
+
}
|
|
3562
|
+
function readManagedModelState(path3) {
|
|
3563
|
+
if (!existsSync9(path3)) return emptyManagedModelState();
|
|
3564
|
+
try {
|
|
3565
|
+
const parsed = JSON.parse(readFileSync7(path3, "utf8"));
|
|
3566
|
+
if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
|
|
3567
|
+
return emptyManagedModelState();
|
|
3568
|
+
}
|
|
3569
|
+
return {
|
|
3570
|
+
version: MANAGED_MODEL_STATE_VERSION,
|
|
3571
|
+
models: Object.fromEntries(
|
|
3572
|
+
Object.entries(parsed.models).filter(
|
|
3573
|
+
(entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
|
|
3574
|
+
)
|
|
3575
|
+
)
|
|
3576
|
+
};
|
|
3577
|
+
} catch {
|
|
3578
|
+
return emptyManagedModelState();
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
function parseRoleTomlModel(content) {
|
|
3582
|
+
const match = /^model\s*=\s*"((?:\\.|[^"\\])*)"\s*$/m.exec(content);
|
|
3583
|
+
if (!match) return void 0;
|
|
3584
|
+
return match[1].replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
3585
|
+
}
|
|
3586
|
+
function escapeTomlString2(value) {
|
|
3587
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\f/g, "\\f").replace(/\r/g, "\\r");
|
|
3588
|
+
}
|
|
3589
|
+
function replaceRoleTomlModel(content, model) {
|
|
3590
|
+
const rendered = `model = "${escapeTomlString2(model)}"`;
|
|
3591
|
+
if (/^model\s*=\s*"(?:\\.|[^"\\])*"\s*$/m.test(content)) {
|
|
3592
|
+
return content.replace(/^model\s*=\s*"(?:\\.|[^"\\])*"\s*$/m, rendered);
|
|
3593
|
+
}
|
|
3594
|
+
return `${rendered}
|
|
3595
|
+
${content}`;
|
|
3596
|
+
}
|
|
3597
|
+
function roleManagedModelStateKey(path3) {
|
|
3598
|
+
return basename(path3);
|
|
3599
|
+
}
|
|
3600
|
+
function resolveRoleTomlContent(options) {
|
|
3601
|
+
const renderedModel = parseRoleTomlModel(options.renderedContent);
|
|
3602
|
+
const key = roleManagedModelStateKey(options.targetPath);
|
|
3603
|
+
if (!renderedModel) return options.renderedContent;
|
|
3604
|
+
if (options.reset || !existsSync9(options.targetPath)) {
|
|
3605
|
+
options.nextState.models[key] = renderedModel;
|
|
3606
|
+
return options.renderedContent;
|
|
3607
|
+
}
|
|
3608
|
+
const currentModel = parseRoleTomlModel(
|
|
3609
|
+
readFileSync7(options.targetPath, "utf8")
|
|
3610
|
+
);
|
|
3611
|
+
const trackedModel = options.state.models[key];
|
|
3612
|
+
const isUserOwned = currentModel !== void 0 && (trackedModel === void 0 ? currentModel !== renderedModel : currentModel !== trackedModel);
|
|
3613
|
+
if (isUserOwned) {
|
|
3222
3614
|
if (trackedModel !== void 0)
|
|
3223
3615
|
options.nextState.models[key] = trackedModel;
|
|
3224
3616
|
return replaceRoleTomlModel(options.renderedContent, currentModel);
|
|
@@ -3254,7 +3646,11 @@ function buildCodexSetupPlan(config) {
|
|
|
3254
3646
|
homeDir: config.homeDir,
|
|
3255
3647
|
codexHome: config.codexHome
|
|
3256
3648
|
});
|
|
3257
|
-
const
|
|
3649
|
+
const packageRoot = resolvePackageRoot2(config.packageRoot);
|
|
3650
|
+
const render = codexAdapter.render({
|
|
3651
|
+
projectRoot: config.projectRoot,
|
|
3652
|
+
...packageRoot ? { packageRoot } : {}
|
|
3653
|
+
});
|
|
3258
3654
|
const packageArtifacts = render.artifacts.filter(
|
|
3259
3655
|
(artifact) => artifact.path.startsWith(".codex-plugin/")
|
|
3260
3656
|
);
|
|
@@ -3276,7 +3672,7 @@ function buildCodexSetupPlan(config) {
|
|
|
3276
3672
|
action: "write-role-toml",
|
|
3277
3673
|
targetPath: target.path,
|
|
3278
3674
|
description: `Materialize Codex role subagent ${target.role}.`,
|
|
3279
|
-
requiresBackup:
|
|
3675
|
+
requiresBackup: existsSync9(target.path),
|
|
3280
3676
|
role: target.role,
|
|
3281
3677
|
content: resolveRoleTomlContent({
|
|
3282
3678
|
renderedContent: roleArtifactContent(target.role, render.artifacts),
|
|
@@ -3292,7 +3688,7 @@ function buildCodexSetupPlan(config) {
|
|
|
3292
3688
|
action: "write-managed-model-state",
|
|
3293
3689
|
targetPath: targets.managedModelsPath,
|
|
3294
3690
|
description: "Record thoth-agents-managed Codex role model ownership state.",
|
|
3295
|
-
requiresBackup:
|
|
3691
|
+
requiresBackup: existsSync9(targets.managedModelsPath),
|
|
3296
3692
|
content: stableJson3(nextManagedModelState)
|
|
3297
3693
|
},
|
|
3298
3694
|
...packageArtifacts.map(
|
|
@@ -3310,7 +3706,7 @@ function buildCodexSetupPlan(config) {
|
|
|
3310
3706
|
action: "merge-marketplace",
|
|
3311
3707
|
targetPath: targets.personalMarketplacePath,
|
|
3312
3708
|
description: "Register Personal Codex marketplace entry for the local thoth-agents plugin source.",
|
|
3313
|
-
requiresBackup:
|
|
3709
|
+
requiresBackup: existsSync9(targets.personalMarketplacePath),
|
|
3314
3710
|
content: targets.personalPluginRoot
|
|
3315
3711
|
},
|
|
3316
3712
|
{
|
|
@@ -3374,10 +3770,10 @@ function formatRefreshPackageGroup(kind, groups) {
|
|
|
3374
3770
|
}
|
|
3375
3771
|
function commonTargetDirectory(items) {
|
|
3376
3772
|
if (items.length === 0) return "";
|
|
3377
|
-
let common =
|
|
3773
|
+
let common = dirname5(items[0]?.targetPath ?? "");
|
|
3378
3774
|
for (const item of items.slice(1)) {
|
|
3379
3775
|
while (!isSameOrChildPath(item.targetPath, common)) {
|
|
3380
|
-
const parent =
|
|
3776
|
+
const parent = dirname5(common);
|
|
3381
3777
|
if (parent === common) return common;
|
|
3382
3778
|
common = parent;
|
|
3383
3779
|
}
|
|
@@ -3399,7 +3795,7 @@ function applyCodexSetup(plan) {
|
|
|
3399
3795
|
if (plan.dryRun) return { success: true, changed, diagnostics };
|
|
3400
3796
|
try {
|
|
3401
3797
|
for (const targetPath of managedRefreshRoots(plan)) {
|
|
3402
|
-
|
|
3798
|
+
rmSync2(targetPath, { recursive: true, force: true });
|
|
3403
3799
|
}
|
|
3404
3800
|
for (const item of plan.items) {
|
|
3405
3801
|
if (item.action === "diagnose-only") continue;
|
|
@@ -3417,8 +3813,8 @@ function applyCodexSetup(plan) {
|
|
|
3417
3813
|
if (item.action === "merge-marketplace") {
|
|
3418
3814
|
if (item.content === void 0) continue;
|
|
3419
3815
|
const content2 = mergePersonalMarketplace(
|
|
3420
|
-
|
|
3421
|
-
|
|
3816
|
+
existsSync9(item.targetPath) ? readFileSync7(item.targetPath, "utf8") : "",
|
|
3817
|
+
dirname5(dirname5(dirname5(item.targetPath))),
|
|
3422
3818
|
item.content
|
|
3423
3819
|
);
|
|
3424
3820
|
if (writeTextWithBackup(item.targetPath, content2))
|
|
@@ -3427,7 +3823,7 @@ function applyCodexSetup(plan) {
|
|
|
3427
3823
|
}
|
|
3428
3824
|
if (item.content === void 0) continue;
|
|
3429
3825
|
const content = item.action === "merge-managed-block" ? mergeManagedBlock(
|
|
3430
|
-
|
|
3826
|
+
existsSync9(item.targetPath) ? readFileSync7(item.targetPath, "utf8") : "",
|
|
3431
3827
|
item.content
|
|
3432
3828
|
) : item.content;
|
|
3433
3829
|
if (writeTextWithBackup(item.targetPath, content))
|
|
@@ -3455,7 +3851,7 @@ function managedRefreshRoots(plan) {
|
|
|
3455
3851
|
}
|
|
3456
3852
|
|
|
3457
3853
|
// src/cli/system.ts
|
|
3458
|
-
import { statSync as
|
|
3854
|
+
import { statSync as statSync4 } from "fs";
|
|
3459
3855
|
|
|
3460
3856
|
// src/utils/subprocess.ts
|
|
3461
3857
|
import {
|
|
@@ -3570,7 +3966,7 @@ function resolveOpenCodePath() {
|
|
|
3570
3966
|
for (const opencodePath of paths) {
|
|
3571
3967
|
if (opencodePath === "opencode") continue;
|
|
3572
3968
|
try {
|
|
3573
|
-
const stat =
|
|
3969
|
+
const stat = statSync4(opencodePath);
|
|
3574
3970
|
if (stat.isFile()) {
|
|
3575
3971
|
cachedOpenCodePath = opencodePath;
|
|
3576
3972
|
return opencodePath;
|
|
@@ -3619,382 +4015,6 @@ function getOpenCodePath() {
|
|
|
3619
4015
|
return path3 === "opencode" ? null : path3;
|
|
3620
4016
|
}
|
|
3621
4017
|
|
|
3622
|
-
// src/cli/custom-skills.ts
|
|
3623
|
-
import {
|
|
3624
|
-
copyFileSync as copyFileSync4,
|
|
3625
|
-
existsSync as existsSync9,
|
|
3626
|
-
mkdirSync as mkdirSync5,
|
|
3627
|
-
readdirSync as readdirSync3,
|
|
3628
|
-
rmSync as rmSync2,
|
|
3629
|
-
statSync as statSync4
|
|
3630
|
-
} from "fs";
|
|
3631
|
-
import { dirname as dirname5, join as join8, parse } from "path";
|
|
3632
|
-
import { fileURLToPath } from "url";
|
|
3633
|
-
|
|
3634
|
-
// src/cli/skill-manifest.ts
|
|
3635
|
-
import { createHash as createHash3 } from "crypto";
|
|
3636
|
-
import {
|
|
3637
|
-
existsSync as existsSync8,
|
|
3638
|
-
mkdirSync as mkdirSync4,
|
|
3639
|
-
readdirSync as readdirSync2,
|
|
3640
|
-
readFileSync as readFileSync7,
|
|
3641
|
-
statSync as statSync3,
|
|
3642
|
-
writeFileSync as writeFileSync4
|
|
3643
|
-
} from "fs";
|
|
3644
|
-
import { join as join7, relative as relative3 } from "path";
|
|
3645
|
-
var SHARED_SKILL_DIRECTORY = "_shared";
|
|
3646
|
-
var SKILLS_SOURCE_ROOT = join7("src", "skills");
|
|
3647
|
-
var MANIFEST_FILE_NAME = ".skill-manifest.json";
|
|
3648
|
-
function getManifestPath() {
|
|
3649
|
-
return join7(getCustomSkillsDir(), MANIFEST_FILE_NAME);
|
|
3650
|
-
}
|
|
3651
|
-
function listFilesRecursive(dirPath) {
|
|
3652
|
-
if (!existsSync8(dirPath)) {
|
|
3653
|
-
return [];
|
|
3654
|
-
}
|
|
3655
|
-
const files = [];
|
|
3656
|
-
const entries = readdirSync2(dirPath).sort(
|
|
3657
|
-
(left, right) => left.localeCompare(right)
|
|
3658
|
-
);
|
|
3659
|
-
for (const entry of entries) {
|
|
3660
|
-
const entryPath = join7(dirPath, entry);
|
|
3661
|
-
const stat = statSync3(entryPath);
|
|
3662
|
-
if (stat.isDirectory()) {
|
|
3663
|
-
files.push(...listFilesRecursive(entryPath));
|
|
3664
|
-
continue;
|
|
3665
|
-
}
|
|
3666
|
-
files.push(entryPath);
|
|
3667
|
-
}
|
|
3668
|
-
return files;
|
|
3669
|
-
}
|
|
3670
|
-
function readPackageVersion(packageRoot) {
|
|
3671
|
-
const packageJsonPath = join7(packageRoot, "package.json");
|
|
3672
|
-
const packageJson = JSON.parse(readFileSync7(packageJsonPath, "utf-8"));
|
|
3673
|
-
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
3674
|
-
throw new Error(`Invalid package version in ${packageJsonPath}`);
|
|
3675
|
-
}
|
|
3676
|
-
return packageJson.version;
|
|
3677
|
-
}
|
|
3678
|
-
function readManifest() {
|
|
3679
|
-
const manifestPath = getManifestPath();
|
|
3680
|
-
if (!existsSync8(manifestPath)) {
|
|
3681
|
-
return null;
|
|
3682
|
-
}
|
|
3683
|
-
try {
|
|
3684
|
-
return JSON.parse(readFileSync7(manifestPath, "utf-8"));
|
|
3685
|
-
} catch (error) {
|
|
3686
|
-
console.warn(`Failed to read skill manifest: ${manifestPath}`, error);
|
|
3687
|
-
return null;
|
|
3688
|
-
}
|
|
3689
|
-
}
|
|
3690
|
-
function writeManifest(manifest) {
|
|
3691
|
-
const manifestPath = getManifestPath();
|
|
3692
|
-
mkdirSync4(getCustomSkillsDir(), { recursive: true });
|
|
3693
|
-
writeFileSync4(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
3694
|
-
`);
|
|
3695
|
-
}
|
|
3696
|
-
function computeSkillHash(skillDirPath) {
|
|
3697
|
-
const hash = createHash3("sha256");
|
|
3698
|
-
for (const filePath of listFilesRecursive(skillDirPath)) {
|
|
3699
|
-
hash.update(`${relative3(skillDirPath, filePath)}
|
|
3700
|
-
`);
|
|
3701
|
-
hash.update(readFileSync7(filePath));
|
|
3702
|
-
hash.update("\n");
|
|
3703
|
-
}
|
|
3704
|
-
return hash.digest("hex");
|
|
3705
|
-
}
|
|
3706
|
-
function findRemovedSkills(manifest) {
|
|
3707
|
-
const currentSkillNames = new Set(CUSTOM_SKILLS.map((skill) => skill.name));
|
|
3708
|
-
return Object.keys(manifest.skills).filter(
|
|
3709
|
-
(skillName) => !currentSkillNames.has(skillName)
|
|
3710
|
-
);
|
|
3711
|
-
}
|
|
3712
|
-
function checkSkillsNeedUpdate(packageRoot) {
|
|
3713
|
-
const manifest = readManifest();
|
|
3714
|
-
const pluginVersion = readPackageVersion(packageRoot);
|
|
3715
|
-
const sharedHash = computeSkillHash(
|
|
3716
|
-
join7(packageRoot, SKILLS_SOURCE_ROOT, SHARED_SKILL_DIRECTORY)
|
|
3717
|
-
);
|
|
3718
|
-
const versionChanged = manifest !== null && manifest.pluginVersion !== pluginVersion;
|
|
3719
|
-
const sharedChanged = manifest !== null && manifest.sharedHash !== sharedHash;
|
|
3720
|
-
const removedSkills = manifest ? findRemovedSkills(manifest) : [];
|
|
3721
|
-
const skillsNeedingUpdate = CUSTOM_SKILLS.flatMap((skill) => {
|
|
3722
|
-
const sourcePath = join7(packageRoot, skill.sourcePath);
|
|
3723
|
-
const targetPath = join7(getCustomSkillsDir(), skill.name);
|
|
3724
|
-
const sourceHash = computeSkillHash(sourcePath);
|
|
3725
|
-
const manifestEntry = manifest?.skills[skill.name];
|
|
3726
|
-
const reasons = [];
|
|
3727
|
-
if (!manifest) {
|
|
3728
|
-
reasons.push("manifest-missing");
|
|
3729
|
-
}
|
|
3730
|
-
if (versionChanged) {
|
|
3731
|
-
reasons.push("version-change");
|
|
3732
|
-
}
|
|
3733
|
-
if (sharedChanged) {
|
|
3734
|
-
reasons.push("shared-hash-mismatch");
|
|
3735
|
-
}
|
|
3736
|
-
if (!manifestEntry) {
|
|
3737
|
-
reasons.push("new-skill");
|
|
3738
|
-
} else if (manifestEntry.hash !== sourceHash) {
|
|
3739
|
-
reasons.push("hash-mismatch");
|
|
3740
|
-
}
|
|
3741
|
-
if (!existsSync8(targetPath)) {
|
|
3742
|
-
reasons.push("missing-install");
|
|
3743
|
-
}
|
|
3744
|
-
if (reasons.length === 0) {
|
|
3745
|
-
return [];
|
|
3746
|
-
}
|
|
3747
|
-
return [
|
|
3748
|
-
{
|
|
3749
|
-
skill,
|
|
3750
|
-
sourceHash,
|
|
3751
|
-
targetPath,
|
|
3752
|
-
reasons
|
|
3753
|
-
}
|
|
3754
|
-
];
|
|
3755
|
-
});
|
|
3756
|
-
return {
|
|
3757
|
-
pluginVersion,
|
|
3758
|
-
sharedHash,
|
|
3759
|
-
manifest,
|
|
3760
|
-
versionChanged,
|
|
3761
|
-
sharedChanged,
|
|
3762
|
-
needsUpdate: skillsNeedingUpdate.length > 0 || removedSkills.length > 0,
|
|
3763
|
-
skillsNeedingUpdate,
|
|
3764
|
-
removedSkills
|
|
3765
|
-
};
|
|
3766
|
-
}
|
|
3767
|
-
|
|
3768
|
-
// src/cli/custom-skills.ts
|
|
3769
|
-
var SHARED_SKILL_DIRECTORY2 = "_shared";
|
|
3770
|
-
var SHARED_SKILL_SOURCE_PATH = `src/skills/${SHARED_SKILL_DIRECTORY2}`;
|
|
3771
|
-
var CUSTOM_SKILLS = BUNDLED_SKILL_REGISTRY.map(
|
|
3772
|
-
(skill) => ({
|
|
3773
|
-
name: skill.name,
|
|
3774
|
-
description: skill.description,
|
|
3775
|
-
allowedAgents: [...skill.allowedRoles],
|
|
3776
|
-
sourcePath: skill.sourcePath
|
|
3777
|
-
})
|
|
3778
|
-
);
|
|
3779
|
-
function getCustomSkillsDir() {
|
|
3780
|
-
return join8(getConfigDir(), "skills");
|
|
3781
|
-
}
|
|
3782
|
-
function copyDirRecursive(src, dest) {
|
|
3783
|
-
if (!existsSync9(dest)) {
|
|
3784
|
-
mkdirSync5(dest, { recursive: true });
|
|
3785
|
-
}
|
|
3786
|
-
const entries = readdirSync3(src);
|
|
3787
|
-
for (const entry of entries) {
|
|
3788
|
-
const srcPath = join8(src, entry);
|
|
3789
|
-
const destPath = join8(dest, entry);
|
|
3790
|
-
const stat = statSync4(srcPath);
|
|
3791
|
-
if (stat.isDirectory()) {
|
|
3792
|
-
copyDirRecursive(srcPath, destPath);
|
|
3793
|
-
} else {
|
|
3794
|
-
const destDir = dirname5(destPath);
|
|
3795
|
-
if (!existsSync9(destDir)) {
|
|
3796
|
-
mkdirSync5(destDir, { recursive: true });
|
|
3797
|
-
}
|
|
3798
|
-
copyFileSync4(srcPath, destPath);
|
|
3799
|
-
}
|
|
3800
|
-
}
|
|
3801
|
-
}
|
|
3802
|
-
function installSharedSkillAssets(packageRoot) {
|
|
3803
|
-
const sharedSourcePath = join8(packageRoot, SHARED_SKILL_SOURCE_PATH);
|
|
3804
|
-
const sharedTargetPath = join8(getCustomSkillsDir(), SHARED_SKILL_DIRECTORY2);
|
|
3805
|
-
if (!existsSync9(sharedSourcePath)) {
|
|
3806
|
-
console.error(`Custom skill shared assets not found: ${sharedSourcePath}`);
|
|
3807
|
-
return false;
|
|
3808
|
-
}
|
|
3809
|
-
rmSync2(sharedTargetPath, { recursive: true, force: true });
|
|
3810
|
-
copyDirRecursive(sharedSourcePath, sharedTargetPath);
|
|
3811
|
-
return true;
|
|
3812
|
-
}
|
|
3813
|
-
function findPackageRoot(startDir) {
|
|
3814
|
-
let currentDir = startDir;
|
|
3815
|
-
const filesystemRoot = parse(startDir).root;
|
|
3816
|
-
while (true) {
|
|
3817
|
-
if (existsSync9(join8(currentDir, "package.json")) && existsSync9(join8(currentDir, "src", "skills"))) {
|
|
3818
|
-
return currentDir;
|
|
3819
|
-
}
|
|
3820
|
-
if (currentDir === filesystemRoot) {
|
|
3821
|
-
return null;
|
|
3822
|
-
}
|
|
3823
|
-
const parentDir = dirname5(currentDir);
|
|
3824
|
-
if (parentDir === currentDir) {
|
|
3825
|
-
return null;
|
|
3826
|
-
}
|
|
3827
|
-
currentDir = parentDir;
|
|
3828
|
-
}
|
|
3829
|
-
}
|
|
3830
|
-
function resolvePackageRoot(packageRoot) {
|
|
3831
|
-
if (packageRoot) {
|
|
3832
|
-
return packageRoot;
|
|
3833
|
-
}
|
|
3834
|
-
const moduleDir = fileURLToPath(new URL(".", import.meta.url));
|
|
3835
|
-
return findPackageRoot(moduleDir) ?? fileURLToPath(new URL("../..", import.meta.url));
|
|
3836
|
-
}
|
|
3837
|
-
function installCustomSkillFiles(skill, packageRoot) {
|
|
3838
|
-
const sourcePath = join8(packageRoot, skill.sourcePath);
|
|
3839
|
-
const targetPath = join8(getCustomSkillsDir(), skill.name);
|
|
3840
|
-
if (!existsSync9(sourcePath)) {
|
|
3841
|
-
console.error(`Custom skill source not found: ${sourcePath}`);
|
|
3842
|
-
return false;
|
|
3843
|
-
}
|
|
3844
|
-
rmSync2(targetPath, { recursive: true, force: true });
|
|
3845
|
-
copyDirRecursive(sourcePath, targetPath);
|
|
3846
|
-
return true;
|
|
3847
|
-
}
|
|
3848
|
-
function removeObsoleteSkills(removedSkillNames) {
|
|
3849
|
-
const removedSkills = [];
|
|
3850
|
-
for (const skillName of removedSkillNames) {
|
|
3851
|
-
const targetPath = join8(getCustomSkillsDir(), skillName);
|
|
3852
|
-
try {
|
|
3853
|
-
console.log(`Removing obsolete bundled skill: ${skillName}`);
|
|
3854
|
-
rmSync2(targetPath, { recursive: true, force: true });
|
|
3855
|
-
removedSkills.push(skillName);
|
|
3856
|
-
} catch (error) {
|
|
3857
|
-
console.warn(`Failed to remove obsolete bundled skill: ${skillName}`);
|
|
3858
|
-
console.warn(error);
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
return removedSkills;
|
|
3862
|
-
}
|
|
3863
|
-
function buildManifest(packageRoot, updatedSkills, previousManifest, pluginVersion, sharedHash) {
|
|
3864
|
-
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3865
|
-
const updatedSkillNames = new Set(
|
|
3866
|
-
updatedSkills.map(({ skill }) => skill.name)
|
|
3867
|
-
);
|
|
3868
|
-
const skills = Object.fromEntries(
|
|
3869
|
-
CUSTOM_SKILLS.map((skill) => {
|
|
3870
|
-
const previousEntry = previousManifest?.skills[skill.name];
|
|
3871
|
-
const nextInstalledAt = updatedSkillNames.has(skill.name) ? installedAt : previousEntry?.installedAt ?? installedAt;
|
|
3872
|
-
return [
|
|
3873
|
-
skill.name,
|
|
3874
|
-
{
|
|
3875
|
-
hash: computeSkillHash(join8(packageRoot, skill.sourcePath)),
|
|
3876
|
-
installedAt: nextInstalledAt
|
|
3877
|
-
}
|
|
3878
|
-
];
|
|
3879
|
-
})
|
|
3880
|
-
);
|
|
3881
|
-
return {
|
|
3882
|
-
pluginVersion,
|
|
3883
|
-
sharedHash,
|
|
3884
|
-
skills
|
|
3885
|
-
};
|
|
3886
|
-
}
|
|
3887
|
-
function pruneRemovedSkillsFromManifest(manifest, removedSkills) {
|
|
3888
|
-
const removedSkillNames = new Set(removedSkills);
|
|
3889
|
-
return {
|
|
3890
|
-
...manifest,
|
|
3891
|
-
skills: Object.fromEntries(
|
|
3892
|
-
Object.entries(manifest.skills).filter(
|
|
3893
|
-
([skillName]) => !removedSkillNames.has(skillName)
|
|
3894
|
-
)
|
|
3895
|
-
)
|
|
3896
|
-
};
|
|
3897
|
-
}
|
|
3898
|
-
function installCustomSkills(packageRoot = resolvePackageRoot()) {
|
|
3899
|
-
const updateCheck = checkSkillsNeedUpdate(packageRoot);
|
|
3900
|
-
if (!updateCheck.needsUpdate) {
|
|
3901
|
-
return {
|
|
3902
|
-
success: true,
|
|
3903
|
-
updatedSkills: [],
|
|
3904
|
-
skippedSkills: [...CUSTOM_SKILLS],
|
|
3905
|
-
failedSkills: [],
|
|
3906
|
-
removedSkills: []
|
|
3907
|
-
};
|
|
3908
|
-
}
|
|
3909
|
-
const removedSkills = removeObsoleteSkills(updateCheck.removedSkills);
|
|
3910
|
-
if (removedSkills.length > 0 && updateCheck.manifest) {
|
|
3911
|
-
writeManifest(
|
|
3912
|
-
pruneRemovedSkillsFromManifest(updateCheck.manifest, removedSkills)
|
|
3913
|
-
);
|
|
3914
|
-
}
|
|
3915
|
-
if (updateCheck.skillsNeedingUpdate.length === 0) {
|
|
3916
|
-
writeManifest(
|
|
3917
|
-
buildManifest(
|
|
3918
|
-
packageRoot,
|
|
3919
|
-
[],
|
|
3920
|
-
updateCheck.manifest,
|
|
3921
|
-
updateCheck.pluginVersion,
|
|
3922
|
-
updateCheck.sharedHash
|
|
3923
|
-
)
|
|
3924
|
-
);
|
|
3925
|
-
return {
|
|
3926
|
-
success: true,
|
|
3927
|
-
updatedSkills: [],
|
|
3928
|
-
skippedSkills: [...CUSTOM_SKILLS],
|
|
3929
|
-
failedSkills: [],
|
|
3930
|
-
removedSkills
|
|
3931
|
-
};
|
|
3932
|
-
}
|
|
3933
|
-
if (!installSharedSkillAssets(packageRoot)) {
|
|
3934
|
-
return {
|
|
3935
|
-
success: false,
|
|
3936
|
-
updatedSkills: [],
|
|
3937
|
-
skippedSkills: [],
|
|
3938
|
-
failedSkills: updateCheck.skillsNeedingUpdate.map(
|
|
3939
|
-
({ skill, reasons }) => ({
|
|
3940
|
-
skill,
|
|
3941
|
-
reasons
|
|
3942
|
-
})
|
|
3943
|
-
),
|
|
3944
|
-
removedSkills
|
|
3945
|
-
};
|
|
3946
|
-
}
|
|
3947
|
-
const updatesBySkillName = new Map(
|
|
3948
|
-
updateCheck.skillsNeedingUpdate.map((entry) => [entry.skill.name, entry])
|
|
3949
|
-
);
|
|
3950
|
-
const updatedSkills = [];
|
|
3951
|
-
const skippedSkills = [];
|
|
3952
|
-
const failedSkills = [];
|
|
3953
|
-
for (const skill of CUSTOM_SKILLS) {
|
|
3954
|
-
const pendingUpdate = updatesBySkillName.get(skill.name);
|
|
3955
|
-
if (!pendingUpdate) {
|
|
3956
|
-
skippedSkills.push(skill);
|
|
3957
|
-
continue;
|
|
3958
|
-
}
|
|
3959
|
-
if (installCustomSkillFiles(skill, packageRoot)) {
|
|
3960
|
-
updatedSkills.push({
|
|
3961
|
-
skill,
|
|
3962
|
-
reasons: pendingUpdate.reasons
|
|
3963
|
-
});
|
|
3964
|
-
continue;
|
|
3965
|
-
}
|
|
3966
|
-
failedSkills.push({
|
|
3967
|
-
skill,
|
|
3968
|
-
reasons: pendingUpdate.reasons
|
|
3969
|
-
});
|
|
3970
|
-
}
|
|
3971
|
-
if (failedSkills.length > 0) {
|
|
3972
|
-
return {
|
|
3973
|
-
success: false,
|
|
3974
|
-
updatedSkills,
|
|
3975
|
-
skippedSkills,
|
|
3976
|
-
failedSkills,
|
|
3977
|
-
removedSkills
|
|
3978
|
-
};
|
|
3979
|
-
}
|
|
3980
|
-
writeManifest(
|
|
3981
|
-
buildManifest(
|
|
3982
|
-
packageRoot,
|
|
3983
|
-
updatedSkills,
|
|
3984
|
-
updateCheck.manifest,
|
|
3985
|
-
updateCheck.pluginVersion,
|
|
3986
|
-
updateCheck.sharedHash
|
|
3987
|
-
)
|
|
3988
|
-
);
|
|
3989
|
-
return {
|
|
3990
|
-
success: true,
|
|
3991
|
-
updatedSkills,
|
|
3992
|
-
skippedSkills,
|
|
3993
|
-
failedSkills,
|
|
3994
|
-
removedSkills
|
|
3995
|
-
};
|
|
3996
|
-
}
|
|
3997
|
-
|
|
3998
4018
|
// src/cli/skills.ts
|
|
3999
4019
|
import { spawnSync } from "child_process";
|
|
4000
4020
|
var RECOMMENDED_SKILLS = [
|
|
@@ -2,6 +2,7 @@ import { type PluginConfig } from '../../config';
|
|
|
2
2
|
import type { HarnessAdapter, HarnessCapabilities, HarnessRenderContext } from '../types';
|
|
3
3
|
export interface CodexRenderContext extends HarnessRenderContext {
|
|
4
4
|
config?: PluginConfig;
|
|
5
|
+
packageRoot?: string;
|
|
5
6
|
}
|
|
6
7
|
export declare const CODEX_CAPABILITIES: HarnessCapabilities;
|
|
7
8
|
export declare function renderCodexRootInstructions(config?: PluginConfig): string;
|
package/dist/harness/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { SkillRegistryEntry } from '../core/skills';
|
|
|
2
2
|
import type { HarnessArtifact, HarnessDiagnostic } from '../types';
|
|
3
3
|
export interface CodexSkillLayoutInput {
|
|
4
4
|
projectRoot: string;
|
|
5
|
+
packageRoot?: string;
|
|
5
6
|
skills: SkillRegistryEntry[];
|
|
6
7
|
surfaceId: string;
|
|
7
8
|
outputMode?: CodexSkillOutputMode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thoth-agents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Delegate-first OpenCode plugin with seven agents, thoth-mem persistence, and bundled SDD skills.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|
|
11
11
|
"license": "MIT",
|
|
12
|
+
"packageManager": "pnpm@11.2.2",
|
|
12
13
|
"engines": {
|
|
13
14
|
"node": ">=22.13"
|
|
14
15
|
},
|
|
@@ -39,6 +40,22 @@
|
|
|
39
40
|
"README.md",
|
|
40
41
|
"LICENSE"
|
|
41
42
|
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup && tsc --emitDeclarationOnly && pnpm run generate-schema",
|
|
45
|
+
"generate-schema": "tsx scripts/generate-schema.ts",
|
|
46
|
+
"release:notes": "tsx scripts/generate-release-notes.ts",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"lint": "biome lint .",
|
|
50
|
+
"format": "biome format . --write",
|
|
51
|
+
"check": "biome check --write .",
|
|
52
|
+
"check:ci": "biome check .",
|
|
53
|
+
"dev": "pnpm run build && opencode",
|
|
54
|
+
"prepublishOnly": "pnpm run build",
|
|
55
|
+
"release:patch": "npm version patch && git push --follow-tags",
|
|
56
|
+
"release:minor": "npm version minor && git push --follow-tags",
|
|
57
|
+
"release:major": "npm version major && git push --follow-tags"
|
|
58
|
+
},
|
|
42
59
|
"dependencies": {
|
|
43
60
|
"@ast-grep/cli": "^0.40.0",
|
|
44
61
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
@@ -63,19 +80,5 @@
|
|
|
63
80
|
"hono": "4.12.19",
|
|
64
81
|
"postcss": "8.5.14",
|
|
65
82
|
"tsx": "4.22.2"
|
|
66
|
-
},
|
|
67
|
-
"scripts": {
|
|
68
|
-
"build": "tsup && tsc --emitDeclarationOnly && pnpm run generate-schema",
|
|
69
|
-
"generate-schema": "tsx scripts/generate-schema.ts",
|
|
70
|
-
"typecheck": "tsc --noEmit",
|
|
71
|
-
"test": "vitest run",
|
|
72
|
-
"lint": "biome lint .",
|
|
73
|
-
"format": "biome format . --write",
|
|
74
|
-
"check": "biome check --write .",
|
|
75
|
-
"check:ci": "biome check .",
|
|
76
|
-
"dev": "pnpm run build && opencode",
|
|
77
|
-
"release:patch": "npm version patch && git push --follow-tags && npm publish",
|
|
78
|
-
"release:minor": "npm version minor && git push --follow-tags && npm publish",
|
|
79
|
-
"release:major": "npm version major && git push --follow-tags && npm publish"
|
|
80
83
|
}
|
|
81
|
-
}
|
|
84
|
+
}
|