@fro.bot/systematic 3.9.0 → 3.9.1
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/dist/cli.js +1 -1
- package/dist/{index-hjbt4p5s.js → index-y8nc60tz.js} +297 -299
- package/dist/index.js +121 -149
- package/dist/lib/skill-catalog.d.ts +0 -13
- package/dist/lib/skill-tool.d.ts +0 -6
- package/dist/pi.js +173 -202
- package/package.json +3 -3
package/dist/pi.js
CHANGED
|
@@ -3316,196 +3316,13 @@ function resolveToolAllowlist(toolsSource) {
|
|
|
3316
3316
|
}
|
|
3317
3317
|
|
|
3318
3318
|
// src/lib/bootstrap.ts
|
|
3319
|
-
import fs5 from "node:fs";
|
|
3320
|
-
import os from "node:os";
|
|
3321
|
-
import path4 from "node:path";
|
|
3322
|
-
|
|
3323
|
-
// src/lib/skill-catalog.ts
|
|
3324
|
-
import { pathToFileURL } from "node:url";
|
|
3325
|
-
|
|
3326
|
-
// src/lib/skill-loader.ts
|
|
3327
3319
|
import fs3 from "node:fs";
|
|
3320
|
+
import os from "node:os";
|
|
3328
3321
|
import path2 from "node:path";
|
|
3329
|
-
var SKILL_PREFIX = "systematic:";
|
|
3330
|
-
var SKILL_DESCRIPTION_PREFIX = "(Systematic) ";
|
|
3331
|
-
function formatSkillCommandName(name) {
|
|
3332
|
-
if (name.includes(":")) {
|
|
3333
|
-
return name;
|
|
3334
|
-
}
|
|
3335
|
-
return `${SKILL_PREFIX}${name}`;
|
|
3336
|
-
}
|
|
3337
|
-
function formatSkillDescription(description, fallbackName) {
|
|
3338
|
-
const desc = description || `${fallbackName} skill`;
|
|
3339
|
-
if (desc.startsWith(SKILL_DESCRIPTION_PREFIX)) {
|
|
3340
|
-
return desc;
|
|
3341
|
-
}
|
|
3342
|
-
return `${SKILL_DESCRIPTION_PREFIX}${desc}`;
|
|
3343
|
-
}
|
|
3344
|
-
function wrapSkillTemplate(skillPath, body) {
|
|
3345
|
-
const skillDir = path2.dirname(skillPath);
|
|
3346
|
-
return `<skill-instruction>
|
|
3347
|
-
Base directory for this skill: ${skillDir}/
|
|
3348
|
-
File references (@path) in this skill are relative to this directory.
|
|
3349
|
-
|
|
3350
|
-
${body.trim()}
|
|
3351
|
-
</skill-instruction>
|
|
3352
|
-
|
|
3353
|
-
<user-request>
|
|
3354
|
-
$ARGUMENTS
|
|
3355
|
-
</user-request>`;
|
|
3356
|
-
}
|
|
3357
|
-
function extractSkillBody(wrappedTemplate) {
|
|
3358
|
-
const match = wrappedTemplate.match(/<skill-instruction>([\s\S]*?)<\/skill-instruction>/);
|
|
3359
|
-
return match ? match[1].trim() : wrappedTemplate;
|
|
3360
|
-
}
|
|
3361
|
-
function loadSkill(skillInfo) {
|
|
3362
|
-
try {
|
|
3363
|
-
const content = fs3.readFileSync(skillInfo.skillFile, "utf8");
|
|
3364
|
-
const { body } = parseFrontmatter(content);
|
|
3365
|
-
const wrappedTemplate = wrapSkillTemplate(skillInfo.skillFile, body);
|
|
3366
|
-
return {
|
|
3367
|
-
name: skillInfo.name,
|
|
3368
|
-
prefixedName: formatSkillCommandName(skillInfo.name),
|
|
3369
|
-
description: formatSkillDescription(skillInfo.description, skillInfo.name),
|
|
3370
|
-
path: skillInfo.path,
|
|
3371
|
-
skillFile: skillInfo.skillFile,
|
|
3372
|
-
wrappedTemplate,
|
|
3373
|
-
disableModelInvocation: skillInfo.disableModelInvocation,
|
|
3374
|
-
userInvocable: skillInfo.userInvocable,
|
|
3375
|
-
subtask: skillInfo.subtask,
|
|
3376
|
-
agent: skillInfo.agent,
|
|
3377
|
-
model: skillInfo.model,
|
|
3378
|
-
argumentHint: skillInfo.argumentHint
|
|
3379
|
-
};
|
|
3380
|
-
} catch {
|
|
3381
|
-
return null;
|
|
3382
|
-
}
|
|
3383
|
-
}
|
|
3384
|
-
|
|
3385
|
-
// src/lib/skills.ts
|
|
3386
|
-
import fs4 from "node:fs";
|
|
3387
|
-
import path3 from "node:path";
|
|
3388
|
-
function parseMetadata(data) {
|
|
3389
|
-
const metadataRaw = data.metadata;
|
|
3390
|
-
if (!isRecord(metadataRaw)) {
|
|
3391
|
-
return;
|
|
3392
|
-
}
|
|
3393
|
-
const entries = Object.entries(metadataRaw);
|
|
3394
|
-
if (!entries.every(([, v]) => typeof v === "string")) {
|
|
3395
|
-
return;
|
|
3396
|
-
}
|
|
3397
|
-
return Object.fromEntries(entries);
|
|
3398
|
-
}
|
|
3399
|
-
function extractFrontmatterFromContent(content) {
|
|
3400
|
-
const { data, parseError } = parseFrontmatter(content);
|
|
3401
|
-
if (parseError) {
|
|
3402
|
-
return { name: "", description: "" };
|
|
3403
|
-
}
|
|
3404
|
-
const metadata = parseMetadata(data);
|
|
3405
|
-
const argumentHintRaw = extractNonEmptyString(data, "argument-hint");
|
|
3406
|
-
const argumentHint = argumentHintRaw?.replace(/^["']|["']$/g, "") || undefined;
|
|
3407
|
-
return {
|
|
3408
|
-
name: extractString(data, "name"),
|
|
3409
|
-
description: extractString(data, "description"),
|
|
3410
|
-
license: extractNonEmptyString(data, "license"),
|
|
3411
|
-
compatibility: extractNonEmptyString(data, "compatibility"),
|
|
3412
|
-
metadata,
|
|
3413
|
-
disableModelInvocation: extractBoolean(data, "disable-model-invocation"),
|
|
3414
|
-
userInvocable: extractBoolean(data, "user-invocable"),
|
|
3415
|
-
subtask: data.context === "fork" ? true : extractBoolean(data, "subtask") ?? undefined,
|
|
3416
|
-
agent: extractNonEmptyString(data, "agent"),
|
|
3417
|
-
model: extractNonEmptyString(data, "model"),
|
|
3418
|
-
argumentHint: argumentHint !== "" ? argumentHint : undefined,
|
|
3419
|
-
allowedTools: extractNonEmptyString(data, "allowed-tools")
|
|
3420
|
-
};
|
|
3421
|
-
}
|
|
3422
|
-
function extractFrontmatter(filePath) {
|
|
3423
|
-
try {
|
|
3424
|
-
const content = fs4.readFileSync(filePath, "utf8");
|
|
3425
|
-
return extractFrontmatterFromContent(content);
|
|
3426
|
-
} catch {
|
|
3427
|
-
return { name: "", description: "" };
|
|
3428
|
-
}
|
|
3429
|
-
}
|
|
3430
|
-
function findSkillsInDir(dir, maxDepth = 3) {
|
|
3431
|
-
const skills = [];
|
|
3432
|
-
const entries = walkDir(dir, {
|
|
3433
|
-
maxDepth,
|
|
3434
|
-
filter: (e) => e.isDirectory
|
|
3435
|
-
});
|
|
3436
|
-
for (const entry of entries) {
|
|
3437
|
-
const skillFile = path3.join(entry.path, "SKILL.md");
|
|
3438
|
-
if (fs4.existsSync(skillFile)) {
|
|
3439
|
-
const frontmatter = extractFrontmatter(skillFile);
|
|
3440
|
-
skills.push({
|
|
3441
|
-
path: entry.path,
|
|
3442
|
-
skillFile,
|
|
3443
|
-
name: frontmatter.name || entry.name,
|
|
3444
|
-
description: frontmatter.description || "",
|
|
3445
|
-
license: frontmatter.license,
|
|
3446
|
-
compatibility: frontmatter.compatibility,
|
|
3447
|
-
metadata: frontmatter.metadata,
|
|
3448
|
-
disableModelInvocation: frontmatter.disableModelInvocation,
|
|
3449
|
-
userInvocable: frontmatter.userInvocable,
|
|
3450
|
-
subtask: frontmatter.subtask,
|
|
3451
|
-
agent: frontmatter.agent,
|
|
3452
|
-
model: frontmatter.model,
|
|
3453
|
-
argumentHint: frontmatter.argumentHint,
|
|
3454
|
-
allowedTools: frontmatter.allowedTools
|
|
3455
|
-
});
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
|
-
return skills;
|
|
3459
|
-
}
|
|
3460
|
-
|
|
3461
|
-
// src/lib/skill-catalog.ts
|
|
3462
|
-
function escapeXml(text) {
|
|
3463
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3464
|
-
}
|
|
3465
|
-
function buildCatalogEntries(options) {
|
|
3466
|
-
const { bundledSkillsDir, disabledSkills } = options;
|
|
3467
|
-
return findSkillsInDir(bundledSkillsDir).filter((s) => !disabledSkills.includes(s.name)).filter((s) => s.disableModelInvocation !== true).sort((a, b) => a.name.localeCompare(b.name)).map((s) => ({
|
|
3468
|
-
name: s.name,
|
|
3469
|
-
prefixedName: formatSkillCommandName(s.name),
|
|
3470
|
-
description: s.description,
|
|
3471
|
-
path: s.path,
|
|
3472
|
-
skillFile: s.skillFile
|
|
3473
|
-
}));
|
|
3474
|
-
}
|
|
3475
|
-
function renderCatalogVerbose(options) {
|
|
3476
|
-
const entries = buildCatalogEntries(options);
|
|
3477
|
-
if (entries.length === 0)
|
|
3478
|
-
return "";
|
|
3479
|
-
const skillLines = entries.flatMap((entry) => [
|
|
3480
|
-
" <skill>",
|
|
3481
|
-
` <name>${escapeXml(entry.prefixedName)}</name>`,
|
|
3482
|
-
` <description>${escapeXml(entry.description)}</description>`,
|
|
3483
|
-
` <location>${pathToFileURL(entry.path).href}</location>`,
|
|
3484
|
-
" </skill>"
|
|
3485
|
-
]);
|
|
3486
|
-
return ["<available_skills>", ...skillLines, "</available_skills>"].join(`
|
|
3487
|
-
`);
|
|
3488
|
-
}
|
|
3489
|
-
function renderCatalogCompact(options) {
|
|
3490
|
-
const entries = buildCatalogEntries(options);
|
|
3491
|
-
const heading = "## Available Systematic Skills";
|
|
3492
|
-
if (entries.length === 0) {
|
|
3493
|
-
return `${heading}
|
|
3494
|
-
|
|
3495
|
-
No Systematic skills are currently available.`;
|
|
3496
|
-
}
|
|
3497
|
-
const bullets = entries.map((entry) => `- ${entry.prefixedName}: ${entry.description}`).join(`
|
|
3498
|
-
`);
|
|
3499
|
-
return `${heading}
|
|
3500
|
-
|
|
3501
|
-
${bullets}`;
|
|
3502
|
-
}
|
|
3503
|
-
|
|
3504
|
-
// src/lib/bootstrap.ts
|
|
3505
3322
|
function readHarnessProfile(bundledSkillsDir, name) {
|
|
3506
|
-
const profilePath =
|
|
3323
|
+
const profilePath = path2.join(bundledSkillsDir, "using-systematic/references", `${name}-profile.md`);
|
|
3507
3324
|
try {
|
|
3508
|
-
return
|
|
3325
|
+
return fs3.readFileSync(profilePath, "utf8");
|
|
3509
3326
|
} catch (error) {
|
|
3510
3327
|
console.error(`Failed to read harness profile ${profilePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
3511
3328
|
return null;
|
|
@@ -3557,25 +3374,18 @@ function getBootstrapContent(config, deps) {
|
|
|
3557
3374
|
if (!config.bootstrap.enabled)
|
|
3558
3375
|
return null;
|
|
3559
3376
|
if (config.bootstrap.file) {
|
|
3560
|
-
const customPath = config.bootstrap.file.startsWith("~/") ?
|
|
3561
|
-
if (
|
|
3562
|
-
return
|
|
3377
|
+
const customPath = config.bootstrap.file.startsWith("~/") ? path2.join(os.homedir(), config.bootstrap.file.slice(2)) : config.bootstrap.file;
|
|
3378
|
+
if (fs3.existsSync(customPath)) {
|
|
3379
|
+
return fs3.readFileSync(customPath, "utf8");
|
|
3563
3380
|
}
|
|
3564
3381
|
}
|
|
3565
|
-
const usingSystematicPath =
|
|
3566
|
-
if (!
|
|
3382
|
+
const usingSystematicPath = path2.join(bundledSkillsDir, "using-systematic/SKILL.md");
|
|
3383
|
+
if (!fs3.existsSync(usingSystematicPath))
|
|
3567
3384
|
return null;
|
|
3568
|
-
const fullContent =
|
|
3385
|
+
const fullContent = fs3.readFileSync(usingSystematicPath, "utf8");
|
|
3569
3386
|
const { body } = parseFrontmatter(fullContent);
|
|
3570
3387
|
const content = body.trim();
|
|
3571
3388
|
const skillUsage = usageTemplate ?? getSkillUsageTemplate();
|
|
3572
|
-
const catalog = renderCatalogVerbose({
|
|
3573
|
-
bundledSkillsDir,
|
|
3574
|
-
disabledSkills: config.disabled_skills
|
|
3575
|
-
});
|
|
3576
|
-
const catalogSection = catalog.length > 0 ? `
|
|
3577
|
-
|
|
3578
|
-
${catalog}` : "";
|
|
3579
3389
|
const profileSection = profileBlock === undefined ? "" : `
|
|
3580
3390
|
|
|
3581
3391
|
${profileBlock}`;
|
|
@@ -3586,7 +3396,7 @@ You have access to structured engineering workflows via the Systematic plugin.
|
|
|
3586
3396
|
|
|
3587
3397
|
${content}
|
|
3588
3398
|
|
|
3589
|
-
${skillUsage}${profileSection}
|
|
3399
|
+
${skillUsage}${profileSection}
|
|
3590
3400
|
</SYSTEMATIC_WORKFLOWS>`;
|
|
3591
3401
|
}
|
|
3592
3402
|
|
|
@@ -3920,7 +3730,168 @@ var createRealPiDelegateSession = createDelegateSessionWith(REAL_PI_DELEGATE_SES
|
|
|
3920
3730
|
// src/lib/skill-resolver.ts
|
|
3921
3731
|
import fs6 from "node:fs";
|
|
3922
3732
|
import path5 from "node:path";
|
|
3923
|
-
import { pathToFileURL
|
|
3733
|
+
import { pathToFileURL } from "node:url";
|
|
3734
|
+
|
|
3735
|
+
// src/lib/skill-loader.ts
|
|
3736
|
+
import fs4 from "node:fs";
|
|
3737
|
+
import path3 from "node:path";
|
|
3738
|
+
var SKILL_PREFIX = "systematic:";
|
|
3739
|
+
var SKILL_DESCRIPTION_PREFIX = "(Systematic) ";
|
|
3740
|
+
function formatSkillCommandName(name) {
|
|
3741
|
+
if (name.includes(":")) {
|
|
3742
|
+
return name;
|
|
3743
|
+
}
|
|
3744
|
+
return `${SKILL_PREFIX}${name}`;
|
|
3745
|
+
}
|
|
3746
|
+
function formatSkillDescription(description, fallbackName) {
|
|
3747
|
+
const desc = description || `${fallbackName} skill`;
|
|
3748
|
+
if (desc.startsWith(SKILL_DESCRIPTION_PREFIX)) {
|
|
3749
|
+
return desc;
|
|
3750
|
+
}
|
|
3751
|
+
return `${SKILL_DESCRIPTION_PREFIX}${desc}`;
|
|
3752
|
+
}
|
|
3753
|
+
function wrapSkillTemplate(skillPath, body) {
|
|
3754
|
+
const skillDir = path3.dirname(skillPath);
|
|
3755
|
+
return `<skill-instruction>
|
|
3756
|
+
Base directory for this skill: ${skillDir}/
|
|
3757
|
+
File references (@path) in this skill are relative to this directory.
|
|
3758
|
+
|
|
3759
|
+
${body.trim()}
|
|
3760
|
+
</skill-instruction>
|
|
3761
|
+
|
|
3762
|
+
<user-request>
|
|
3763
|
+
$ARGUMENTS
|
|
3764
|
+
</user-request>`;
|
|
3765
|
+
}
|
|
3766
|
+
function extractSkillBody(wrappedTemplate) {
|
|
3767
|
+
const match = wrappedTemplate.match(/<skill-instruction>([\s\S]*?)<\/skill-instruction>/);
|
|
3768
|
+
return match ? match[1].trim() : wrappedTemplate;
|
|
3769
|
+
}
|
|
3770
|
+
function loadSkill(skillInfo) {
|
|
3771
|
+
try {
|
|
3772
|
+
const content = fs4.readFileSync(skillInfo.skillFile, "utf8");
|
|
3773
|
+
const { body } = parseFrontmatter(content);
|
|
3774
|
+
const wrappedTemplate = wrapSkillTemplate(skillInfo.skillFile, body);
|
|
3775
|
+
return {
|
|
3776
|
+
name: skillInfo.name,
|
|
3777
|
+
prefixedName: formatSkillCommandName(skillInfo.name),
|
|
3778
|
+
description: formatSkillDescription(skillInfo.description, skillInfo.name),
|
|
3779
|
+
path: skillInfo.path,
|
|
3780
|
+
skillFile: skillInfo.skillFile,
|
|
3781
|
+
wrappedTemplate,
|
|
3782
|
+
disableModelInvocation: skillInfo.disableModelInvocation,
|
|
3783
|
+
userInvocable: skillInfo.userInvocable,
|
|
3784
|
+
subtask: skillInfo.subtask,
|
|
3785
|
+
agent: skillInfo.agent,
|
|
3786
|
+
model: skillInfo.model,
|
|
3787
|
+
argumentHint: skillInfo.argumentHint
|
|
3788
|
+
};
|
|
3789
|
+
} catch {
|
|
3790
|
+
return null;
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3794
|
+
// src/lib/skills.ts
|
|
3795
|
+
import fs5 from "node:fs";
|
|
3796
|
+
import path4 from "node:path";
|
|
3797
|
+
function parseMetadata(data) {
|
|
3798
|
+
const metadataRaw = data.metadata;
|
|
3799
|
+
if (!isRecord(metadataRaw)) {
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
const entries = Object.entries(metadataRaw);
|
|
3803
|
+
if (!entries.every(([, v]) => typeof v === "string")) {
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
return Object.fromEntries(entries);
|
|
3807
|
+
}
|
|
3808
|
+
function extractFrontmatterFromContent(content) {
|
|
3809
|
+
const { data, parseError } = parseFrontmatter(content);
|
|
3810
|
+
if (parseError) {
|
|
3811
|
+
return { name: "", description: "" };
|
|
3812
|
+
}
|
|
3813
|
+
const metadata = parseMetadata(data);
|
|
3814
|
+
const argumentHintRaw = extractNonEmptyString(data, "argument-hint");
|
|
3815
|
+
const argumentHint = argumentHintRaw?.replace(/^["']|["']$/g, "") || undefined;
|
|
3816
|
+
return {
|
|
3817
|
+
name: extractString(data, "name"),
|
|
3818
|
+
description: extractString(data, "description"),
|
|
3819
|
+
license: extractNonEmptyString(data, "license"),
|
|
3820
|
+
compatibility: extractNonEmptyString(data, "compatibility"),
|
|
3821
|
+
metadata,
|
|
3822
|
+
disableModelInvocation: extractBoolean(data, "disable-model-invocation"),
|
|
3823
|
+
userInvocable: extractBoolean(data, "user-invocable"),
|
|
3824
|
+
subtask: data.context === "fork" ? true : extractBoolean(data, "subtask") ?? undefined,
|
|
3825
|
+
agent: extractNonEmptyString(data, "agent"),
|
|
3826
|
+
model: extractNonEmptyString(data, "model"),
|
|
3827
|
+
argumentHint: argumentHint !== "" ? argumentHint : undefined,
|
|
3828
|
+
allowedTools: extractNonEmptyString(data, "allowed-tools")
|
|
3829
|
+
};
|
|
3830
|
+
}
|
|
3831
|
+
function extractFrontmatter(filePath) {
|
|
3832
|
+
try {
|
|
3833
|
+
const content = fs5.readFileSync(filePath, "utf8");
|
|
3834
|
+
return extractFrontmatterFromContent(content);
|
|
3835
|
+
} catch {
|
|
3836
|
+
return { name: "", description: "" };
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
function findSkillsInDir(dir, maxDepth = 3) {
|
|
3840
|
+
const skills = [];
|
|
3841
|
+
const entries = walkDir(dir, {
|
|
3842
|
+
maxDepth,
|
|
3843
|
+
filter: (e) => e.isDirectory
|
|
3844
|
+
});
|
|
3845
|
+
for (const entry of entries) {
|
|
3846
|
+
const skillFile = path4.join(entry.path, "SKILL.md");
|
|
3847
|
+
if (fs5.existsSync(skillFile)) {
|
|
3848
|
+
const frontmatter = extractFrontmatter(skillFile);
|
|
3849
|
+
skills.push({
|
|
3850
|
+
path: entry.path,
|
|
3851
|
+
skillFile,
|
|
3852
|
+
name: frontmatter.name || entry.name,
|
|
3853
|
+
description: frontmatter.description || "",
|
|
3854
|
+
license: frontmatter.license,
|
|
3855
|
+
compatibility: frontmatter.compatibility,
|
|
3856
|
+
metadata: frontmatter.metadata,
|
|
3857
|
+
disableModelInvocation: frontmatter.disableModelInvocation,
|
|
3858
|
+
userInvocable: frontmatter.userInvocable,
|
|
3859
|
+
subtask: frontmatter.subtask,
|
|
3860
|
+
agent: frontmatter.agent,
|
|
3861
|
+
model: frontmatter.model,
|
|
3862
|
+
argumentHint: frontmatter.argumentHint,
|
|
3863
|
+
allowedTools: frontmatter.allowedTools
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
return skills;
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
// src/lib/skill-catalog.ts
|
|
3871
|
+
function buildCatalogEntries(options) {
|
|
3872
|
+
const { bundledSkillsDir, disabledSkills } = options;
|
|
3873
|
+
return findSkillsInDir(bundledSkillsDir).filter((s) => !disabledSkills.includes(s.name)).filter((s) => s.disableModelInvocation !== true).sort((a, b) => a.name.localeCompare(b.name)).map((s) => ({
|
|
3874
|
+
name: s.name,
|
|
3875
|
+
prefixedName: formatSkillCommandName(s.name),
|
|
3876
|
+
description: s.description
|
|
3877
|
+
}));
|
|
3878
|
+
}
|
|
3879
|
+
function renderCatalogCompact(options) {
|
|
3880
|
+
const entries = buildCatalogEntries(options);
|
|
3881
|
+
const heading = "## Available Systematic Skills";
|
|
3882
|
+
if (entries.length === 0) {
|
|
3883
|
+
return `${heading}
|
|
3884
|
+
|
|
3885
|
+
No Systematic skills are currently available.`;
|
|
3886
|
+
}
|
|
3887
|
+
const bullets = entries.map((entry) => `- ${entry.prefixedName}: ${entry.description}`).join(`
|
|
3888
|
+
`);
|
|
3889
|
+
return `${heading}
|
|
3890
|
+
|
|
3891
|
+
${bullets}`;
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
// src/lib/skill-resolver.ts
|
|
3924
3895
|
function getAllSkills(options) {
|
|
3925
3896
|
const { bundledSkillsDir, disabledSkills } = options;
|
|
3926
3897
|
return findSkillsInDir(bundledSkillsDir).filter((s) => !disabledSkills.includes(s.name)).map((skillInfo) => loadSkill(skillInfo)).filter((s) => s !== null).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -3956,7 +3927,7 @@ function buildSkillToolParameterHint(options) {
|
|
|
3956
3927
|
function buildSkillContentOutput(matchedSkill) {
|
|
3957
3928
|
const body = extractSkillBody(matchedSkill.wrappedTemplate);
|
|
3958
3929
|
const dir = path5.dirname(matchedSkill.skillFile);
|
|
3959
|
-
const base =
|
|
3930
|
+
const base = pathToFileURL(dir).href;
|
|
3960
3931
|
const files = discoverSkillFiles(dir);
|
|
3961
3932
|
const lines = [
|
|
3962
3933
|
`<skill_content name="${matchedSkill.prefixedName}">`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fro.bot/systematic",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"description": "Compound-engineering loops for OpenCode, Pi, and Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://fro.bot/systematic",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"devDependencies": {
|
|
94
94
|
"@biomejs/biome": "2.5.8",
|
|
95
95
|
"@earendil-works/pi-coding-agent": "0.83.0",
|
|
96
|
-
"@opencode-ai/plugin": "1.18.
|
|
97
|
-
"@opencode-ai/sdk": "1.18.
|
|
96
|
+
"@opencode-ai/plugin": "1.18.18",
|
|
97
|
+
"@opencode-ai/sdk": "1.18.18",
|
|
98
98
|
"@semantic-release/exec": "7.1.0",
|
|
99
99
|
"@tintinweb/pi-subagents": "0.14.3",
|
|
100
100
|
"@types/bun": "latest",
|