@zcy2nn/agent-forge 1.1.0 → 1.1.2
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/custom-skills.d.ts +1 -12
- package/dist/cli/index.js +139 -97
- package/dist/cli/skill-sync.d.ts +23 -0
- package/dist/cli/types.d.ts +0 -1
- package/dist/hooks/filter-available-skills/index.d.ts +13 -4
- package/dist/index.js +335 -157
- package/dist/tui.js +14 -0
- package/dist/utils/package-root.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A custom skill bundled in this repository.
|
|
3
|
-
*
|
|
3
|
+
* Built-in skills are read directly from the package at runtime (no file copy needed).
|
|
4
4
|
*/
|
|
5
5
|
export interface CustomSkill {
|
|
6
6
|
/** Skill name (folder name) */
|
|
@@ -16,14 +16,3 @@ export interface CustomSkill {
|
|
|
16
16
|
* Registry of custom skills bundled in this repository.
|
|
17
17
|
*/
|
|
18
18
|
export declare const CUSTOM_SKILLS: CustomSkill[];
|
|
19
|
-
/**
|
|
20
|
-
* Get the target directory for custom skills installation.
|
|
21
|
-
*/
|
|
22
|
-
export declare function getCustomSkillsDir(): string;
|
|
23
|
-
/**
|
|
24
|
-
* Install a custom skill by copying from src/skills/ to the OpenCode skills directory
|
|
25
|
-
* @param skill - The custom skill to install
|
|
26
|
-
* @param projectRoot - Root directory of agent-forge project
|
|
27
|
-
* @returns True if installation succeeded, false otherwise
|
|
28
|
-
*/
|
|
29
|
-
export declare function installCustomSkill(skill: CustomSkill): boolean;
|
package/dist/cli/index.js
CHANGED
|
@@ -13,14 +13,14 @@ import * as path from "node:path";
|
|
|
13
13
|
|
|
14
14
|
// src/cli/config-io.ts
|
|
15
15
|
import {
|
|
16
|
-
copyFileSync
|
|
17
|
-
existsSync as
|
|
16
|
+
copyFileSync,
|
|
17
|
+
existsSync as existsSync2,
|
|
18
18
|
readFileSync,
|
|
19
19
|
renameSync,
|
|
20
|
-
statSync
|
|
20
|
+
statSync,
|
|
21
21
|
writeFileSync
|
|
22
22
|
} from "node:fs";
|
|
23
|
-
import { dirname as
|
|
23
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
24
24
|
|
|
25
25
|
// src/cli/paths.ts
|
|
26
26
|
import { existsSync, mkdirSync } from "node:fs";
|
|
@@ -390,15 +390,6 @@ var DEFAULT_AGENT_MCPS = {
|
|
|
390
390
|
};
|
|
391
391
|
|
|
392
392
|
// src/cli/custom-skills.ts
|
|
393
|
-
import {
|
|
394
|
-
copyFileSync,
|
|
395
|
-
existsSync as existsSync2,
|
|
396
|
-
mkdirSync as mkdirSync2,
|
|
397
|
-
readdirSync,
|
|
398
|
-
statSync
|
|
399
|
-
} from "node:fs";
|
|
400
|
-
import { dirname as dirname2, join as join2 } from "node:path";
|
|
401
|
-
import { fileURLToPath } from "node:url";
|
|
402
393
|
var CUSTOM_SKILLS = [
|
|
403
394
|
{
|
|
404
395
|
name: "simplify",
|
|
@@ -491,45 +482,6 @@ var CUSTOM_SKILLS = [
|
|
|
491
482
|
sourcePath: "src/skills/writing-skills"
|
|
492
483
|
}
|
|
493
484
|
];
|
|
494
|
-
function getCustomSkillsDir() {
|
|
495
|
-
return join2(getConfigDir(), "skills");
|
|
496
|
-
}
|
|
497
|
-
function copyDirRecursive(src, dest) {
|
|
498
|
-
if (!existsSync2(dest)) {
|
|
499
|
-
mkdirSync2(dest, { recursive: true });
|
|
500
|
-
}
|
|
501
|
-
const entries = readdirSync(src);
|
|
502
|
-
for (const entry of entries) {
|
|
503
|
-
const srcPath = join2(src, entry);
|
|
504
|
-
const destPath = join2(dest, entry);
|
|
505
|
-
const stat = statSync(srcPath);
|
|
506
|
-
if (stat.isDirectory()) {
|
|
507
|
-
copyDirRecursive(srcPath, destPath);
|
|
508
|
-
} else {
|
|
509
|
-
const destDir = dirname2(destPath);
|
|
510
|
-
if (!existsSync2(destDir)) {
|
|
511
|
-
mkdirSync2(destDir, { recursive: true });
|
|
512
|
-
}
|
|
513
|
-
copyFileSync(srcPath, destPath);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
function installCustomSkill(skill) {
|
|
518
|
-
try {
|
|
519
|
-
const packageRoot = fileURLToPath(new URL("../..", import.meta.url));
|
|
520
|
-
const sourcePath = join2(packageRoot, skill.sourcePath);
|
|
521
|
-
const targetPath = join2(getCustomSkillsDir(), skill.name);
|
|
522
|
-
if (!existsSync2(sourcePath)) {
|
|
523
|
-
console.error(`Custom skill source not found: ${sourcePath}`);
|
|
524
|
-
return false;
|
|
525
|
-
}
|
|
526
|
-
copyDirRecursive(sourcePath, targetPath);
|
|
527
|
-
return true;
|
|
528
|
-
} catch (error) {
|
|
529
|
-
console.error(`Failed to install custom skill: ${skill.name}`, error);
|
|
530
|
-
return false;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
485
|
|
|
534
486
|
// src/cli/skills.ts
|
|
535
487
|
import { spawnSync } from "node:child_process";
|
|
@@ -642,7 +594,7 @@ function generateLiteConfig(installConfig) {
|
|
|
642
594
|
...RECOMMENDED_SKILLS.filter((s) => s.allowedAgents.includes("*") || s.allowedAgents.includes(agentName)).map((s) => s.skillName),
|
|
643
595
|
...CUSTOM_SKILLS.filter((s) => s.allowedAgents.includes("*") || s.allowedAgents.includes(agentName)).map((s) => s.name)
|
|
644
596
|
];
|
|
645
|
-
if (agentName === "implementer" && !skills.includes("agent-browser")) {
|
|
597
|
+
if (installConfig.installSkills && agentName === "implementer" && !skills.includes("agent-browser")) {
|
|
646
598
|
skills.push("agent-browser");
|
|
647
599
|
}
|
|
648
600
|
return {
|
|
@@ -696,10 +648,10 @@ function normalizePathForMatch(path) {
|
|
|
696
648
|
return path.replaceAll("\\", "/");
|
|
697
649
|
}
|
|
698
650
|
function findPackageRoot(startPath) {
|
|
699
|
-
let currentPath =
|
|
651
|
+
let currentPath = dirname2(startPath);
|
|
700
652
|
while (true) {
|
|
701
|
-
const packageJsonPath =
|
|
702
|
-
if (
|
|
653
|
+
const packageJsonPath = join2(currentPath, "package.json");
|
|
654
|
+
if (existsSync2(packageJsonPath)) {
|
|
703
655
|
try {
|
|
704
656
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
705
657
|
if (packageJson.name === PACKAGE_NAME) {
|
|
@@ -707,7 +659,7 @@ function findPackageRoot(startPath) {
|
|
|
707
659
|
}
|
|
708
660
|
} catch {}
|
|
709
661
|
}
|
|
710
|
-
const parentPath =
|
|
662
|
+
const parentPath = dirname2(currentPath);
|
|
711
663
|
if (parentPath === currentPath) {
|
|
712
664
|
return null;
|
|
713
665
|
}
|
|
@@ -722,8 +674,8 @@ function isLocalPackageRootEntry(entry) {
|
|
|
722
674
|
if (!entry || entry.startsWith("file://")) {
|
|
723
675
|
return false;
|
|
724
676
|
}
|
|
725
|
-
const packageJsonPath =
|
|
726
|
-
if (!
|
|
677
|
+
const packageJsonPath = join2(entry, "package.json");
|
|
678
|
+
if (!existsSync2(packageJsonPath)) {
|
|
727
679
|
return false;
|
|
728
680
|
}
|
|
729
681
|
try {
|
|
@@ -762,9 +714,9 @@ function stripJsonComments(json) {
|
|
|
762
714
|
}
|
|
763
715
|
function parseConfigFile(path) {
|
|
764
716
|
try {
|
|
765
|
-
if (!
|
|
717
|
+
if (!existsSync2(path))
|
|
766
718
|
return { config: null };
|
|
767
|
-
const stat =
|
|
719
|
+
const stat = statSync(path);
|
|
768
720
|
if (stat.size === 0)
|
|
769
721
|
return { config: null };
|
|
770
722
|
const content = readFileSync(path, "utf-8");
|
|
@@ -793,8 +745,8 @@ function writeConfig(configPath, config) {
|
|
|
793
745
|
const bakPath = `${configPath}.bak`;
|
|
794
746
|
const content = `${JSON.stringify(config, null, 2)}
|
|
795
747
|
`;
|
|
796
|
-
if (
|
|
797
|
-
|
|
748
|
+
if (existsSync2(configPath)) {
|
|
749
|
+
copyFileSync(configPath, bakPath);
|
|
798
750
|
}
|
|
799
751
|
writeFileSync(tmpPath, content);
|
|
800
752
|
renameSync(tmpPath, configPath);
|
|
@@ -880,8 +832,8 @@ function writeLiteConfig(installConfig, targetPath) {
|
|
|
880
832
|
const bakPath = `${configPath}.bak`;
|
|
881
833
|
const content = `${JSON.stringify(config, null, 2)}
|
|
882
834
|
`;
|
|
883
|
-
if (
|
|
884
|
-
|
|
835
|
+
if (existsSync2(configPath)) {
|
|
836
|
+
copyFileSync(configPath, bakPath);
|
|
885
837
|
}
|
|
886
838
|
writeFileSync(tmpPath, content);
|
|
887
839
|
renameSync(tmpPath, configPath);
|
|
@@ -1256,11 +1208,12 @@ Options:
|
|
|
1256
1208
|
}
|
|
1257
1209
|
|
|
1258
1210
|
// src/cli/install.ts
|
|
1259
|
-
import { existsSync as
|
|
1211
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
1212
|
+
import { join as join6 } from "node:path";
|
|
1260
1213
|
import { createInterface } from "node:readline/promises";
|
|
1261
1214
|
// src/cli/system.ts
|
|
1262
1215
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1263
|
-
import { statSync as
|
|
1216
|
+
import { statSync as statSync3 } from "node:fs";
|
|
1264
1217
|
|
|
1265
1218
|
// src/utils/compat.ts
|
|
1266
1219
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
@@ -1381,7 +1334,7 @@ function resolveOpenCodePath() {
|
|
|
1381
1334
|
if (opencodePath === "opencode")
|
|
1382
1335
|
continue;
|
|
1383
1336
|
try {
|
|
1384
|
-
const stat =
|
|
1337
|
+
const stat = statSync3(opencodePath);
|
|
1385
1338
|
if (stat.isFile()) {
|
|
1386
1339
|
cachedOpenCodePath = opencodePath;
|
|
1387
1340
|
return opencodePath;
|
|
@@ -1433,6 +1386,97 @@ function getOpenCodePath() {
|
|
|
1433
1386
|
const path2 = resolveOpenCodePath();
|
|
1434
1387
|
return path2 === "opencode" ? null : path2;
|
|
1435
1388
|
}
|
|
1389
|
+
// src/cli/skill-sync.ts
|
|
1390
|
+
import {
|
|
1391
|
+
cpSync,
|
|
1392
|
+
existsSync as existsSync5,
|
|
1393
|
+
lstatSync,
|
|
1394
|
+
mkdirSync as mkdirSync2,
|
|
1395
|
+
readlinkSync,
|
|
1396
|
+
rmSync,
|
|
1397
|
+
symlinkSync
|
|
1398
|
+
} from "node:fs";
|
|
1399
|
+
import { join as join5 } from "node:path";
|
|
1400
|
+
|
|
1401
|
+
// src/utils/package-root.ts
|
|
1402
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
1403
|
+
import { join as join4 } from "node:path";
|
|
1404
|
+
var _cachedPackageRoot;
|
|
1405
|
+
function resolvePackageRoot() {
|
|
1406
|
+
if (_cachedPackageRoot)
|
|
1407
|
+
return _cachedPackageRoot;
|
|
1408
|
+
const distDir = import.meta.dirname;
|
|
1409
|
+
if (!distDir) {
|
|
1410
|
+
throw new Error("import.meta.dirname is not available");
|
|
1411
|
+
}
|
|
1412
|
+
const npmRoot = join4(distDir, "..", "..");
|
|
1413
|
+
if (existsSync4(join4(npmRoot, "src", "skills"))) {
|
|
1414
|
+
_cachedPackageRoot = npmRoot;
|
|
1415
|
+
return npmRoot;
|
|
1416
|
+
}
|
|
1417
|
+
const localRoot = join4(distDir, "..");
|
|
1418
|
+
if (existsSync4(join4(localRoot, "src", "skills"))) {
|
|
1419
|
+
_cachedPackageRoot = localRoot;
|
|
1420
|
+
return localRoot;
|
|
1421
|
+
}
|
|
1422
|
+
_cachedPackageRoot = npmRoot;
|
|
1423
|
+
return npmRoot;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// src/cli/skill-sync.ts
|
|
1427
|
+
function syncBuiltinSkills(packageRoot, skillsDirOverride) {
|
|
1428
|
+
const resolvedPackageRoot = packageRoot ?? resolvePackageRoot();
|
|
1429
|
+
const skillsDir = skillsDirOverride ?? join5(getConfigDir(), "skills");
|
|
1430
|
+
mkdirSync2(skillsDir, { recursive: true });
|
|
1431
|
+
let installed = 0;
|
|
1432
|
+
let skipped = 0;
|
|
1433
|
+
let unchanged = 0;
|
|
1434
|
+
let copied = 0;
|
|
1435
|
+
for (const skill of CUSTOM_SKILLS) {
|
|
1436
|
+
const sourcePath = join5(resolvedPackageRoot, skill.sourcePath);
|
|
1437
|
+
const linkPath = join5(skillsDir, skill.name);
|
|
1438
|
+
if (!existsSync5(sourcePath)) {
|
|
1439
|
+
skipped++;
|
|
1440
|
+
continue;
|
|
1441
|
+
}
|
|
1442
|
+
try {
|
|
1443
|
+
if (lstatSync(linkPath).isSymbolicLink()) {
|
|
1444
|
+
const currentTarget = getSymlinkTarget(linkPath);
|
|
1445
|
+
if (currentTarget === sourcePath) {
|
|
1446
|
+
unchanged++;
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
rmSync(linkPath, { recursive: true, force: true });
|
|
1450
|
+
} else if (existsSync5(linkPath)) {
|
|
1451
|
+
rmSync(linkPath, { recursive: true, force: true });
|
|
1452
|
+
}
|
|
1453
|
+
} catch {
|
|
1454
|
+
try {
|
|
1455
|
+
rmSync(linkPath, { recursive: true, force: true });
|
|
1456
|
+
} catch {}
|
|
1457
|
+
}
|
|
1458
|
+
try {
|
|
1459
|
+
symlinkSync(sourcePath, linkPath, "junction");
|
|
1460
|
+
installed++;
|
|
1461
|
+
} catch {
|
|
1462
|
+
try {
|
|
1463
|
+
cpSync(sourcePath, linkPath, { recursive: true, force: true });
|
|
1464
|
+
copied++;
|
|
1465
|
+
} catch {
|
|
1466
|
+
skipped++;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return { installed, skipped, unchanged, copied };
|
|
1471
|
+
}
|
|
1472
|
+
function getSymlinkTarget(linkPath) {
|
|
1473
|
+
try {
|
|
1474
|
+
return readlinkSync(linkPath, "utf-8");
|
|
1475
|
+
} catch {
|
|
1476
|
+
return "";
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1436
1480
|
// src/cli/install.ts
|
|
1437
1481
|
var GREEN = "\x1B[32m";
|
|
1438
1482
|
var BLUE = "\x1B[34m";
|
|
@@ -1482,6 +1526,9 @@ async function confirm(message, defaultYes = true) {
|
|
|
1482
1526
|
rl.close();
|
|
1483
1527
|
}
|
|
1484
1528
|
}
|
|
1529
|
+
function installBuiltinSkills() {
|
|
1530
|
+
return syncBuiltinSkills();
|
|
1531
|
+
}
|
|
1485
1532
|
async function askToStarRepo(config) {
|
|
1486
1533
|
if (!config.promptForStar || config.dryRun || !process.stdin.isTTY)
|
|
1487
1534
|
return;
|
|
@@ -1529,11 +1576,9 @@ async function runInstall(config) {
|
|
|
1529
1576
|
const detected = detectCurrentConfig();
|
|
1530
1577
|
const isUpdate = detected.isInstalled;
|
|
1531
1578
|
printHeader(isUpdate);
|
|
1532
|
-
let totalSteps =
|
|
1579
|
+
let totalSteps = 7;
|
|
1533
1580
|
if (config.installSkills)
|
|
1534
1581
|
totalSteps += 1;
|
|
1535
|
-
if (config.installCustomSkills)
|
|
1536
|
-
totalSteps += 1;
|
|
1537
1582
|
let step = 1;
|
|
1538
1583
|
printStep(step++, totalSteps, "Checking OpenCode installation...");
|
|
1539
1584
|
if (config.dryRun) {
|
|
@@ -1587,7 +1632,7 @@ ${JSON.stringify(liteConfig, null, 2)}
|
|
|
1587
1632
|
`);
|
|
1588
1633
|
} else {
|
|
1589
1634
|
const configPath2 = getExistingLiteConfigPath();
|
|
1590
|
-
const configExists =
|
|
1635
|
+
const configExists = existsSync6(configPath2);
|
|
1591
1636
|
if (configExists && !config.reset) {
|
|
1592
1637
|
printInfo(`Configuration already exists at ${configPath2}. Use --reset to overwrite.`);
|
|
1593
1638
|
} else {
|
|
@@ -1596,6 +1641,24 @@ ${JSON.stringify(liteConfig, null, 2)}
|
|
|
1596
1641
|
return 1;
|
|
1597
1642
|
}
|
|
1598
1643
|
}
|
|
1644
|
+
printStep(step++, totalSteps, "Installing built-in skills...");
|
|
1645
|
+
if (config.dryRun) {
|
|
1646
|
+
printInfo("Dry run mode - would install built-in skills:");
|
|
1647
|
+
for (const skill of CUSTOM_SKILLS) {
|
|
1648
|
+
printInfo(` - ${skill.name}`);
|
|
1649
|
+
}
|
|
1650
|
+
} else {
|
|
1651
|
+
const result = installBuiltinSkills();
|
|
1652
|
+
if (result.installed > 0) {
|
|
1653
|
+
printSuccess(`${result.installed} built-in skill(s) symlinked to ${join6(getConfigDir(), "skills")}`);
|
|
1654
|
+
}
|
|
1655
|
+
if (result.copied > 0) {
|
|
1656
|
+
printInfo(`${result.copied} skill(s) copied (symlink unavailable — auto-update will NOT sync these)`);
|
|
1657
|
+
}
|
|
1658
|
+
if (result.skipped > 0) {
|
|
1659
|
+
printInfo(`${result.skipped} skill(s) skipped`);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1599
1662
|
if (config.installSkills) {
|
|
1600
1663
|
printStep(step++, totalSteps, "Installing recommended skills...");
|
|
1601
1664
|
if (config.dryRun) {
|
|
@@ -1616,28 +1679,8 @@ ${JSON.stringify(liteConfig, null, 2)}
|
|
|
1616
1679
|
}
|
|
1617
1680
|
printSuccess(`${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills processed`);
|
|
1618
1681
|
}
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
printStep(step++, totalSteps, "Installing custom skills...");
|
|
1622
|
-
if (config.dryRun) {
|
|
1623
|
-
printInfo("Dry run mode - would install custom skills:");
|
|
1624
|
-
for (const skill of CUSTOM_SKILLS) {
|
|
1625
|
-
printInfo(` - ${skill.name}`);
|
|
1626
|
-
}
|
|
1627
|
-
} else {
|
|
1628
|
-
let customSkillsInstalled = 0;
|
|
1629
|
-
for (const skill of CUSTOM_SKILLS) {
|
|
1630
|
-
printInfo(`Installing ${skill.name}...`);
|
|
1631
|
-
if (installCustomSkill(skill)) {
|
|
1632
|
-
printSuccess(`Installed: ${skill.name}`);
|
|
1633
|
-
customSkillsInstalled++;
|
|
1634
|
-
} else {
|
|
1635
|
-
printInfo(`Skipped: ${skill.name} (already installed)`);
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
const totalCustom = CUSTOM_SKILLS.length;
|
|
1639
|
-
printSuccess(`${customSkillsInstalled}/${totalCustom} custom skills processed`);
|
|
1640
|
-
}
|
|
1682
|
+
} else {
|
|
1683
|
+
printInfo("Recommended skills (e.g. agent-browser) not installed. Use --skills=yes to install them.");
|
|
1641
1684
|
}
|
|
1642
1685
|
const statusMsg = isUpdate ? "Configuration updated!" : "Installation complete!";
|
|
1643
1686
|
console.log(`${SYMBOLS.star} ${BOLD}${GREEN}${statusMsg}${RESET}`);
|
|
@@ -1674,7 +1717,6 @@ async function install(args) {
|
|
|
1674
1717
|
const config = {
|
|
1675
1718
|
hasTmux: false,
|
|
1676
1719
|
installSkills: args.skills === "yes",
|
|
1677
|
-
installCustomSkills: args.skills === "yes",
|
|
1678
1720
|
preset: args.preset,
|
|
1679
1721
|
promptForStar: args.tui,
|
|
1680
1722
|
dryRun: args.dryRun,
|
|
@@ -1696,7 +1738,7 @@ function getGeneratedPresetNames2() {
|
|
|
1696
1738
|
function parseArgs(args) {
|
|
1697
1739
|
const result = {
|
|
1698
1740
|
tui: true,
|
|
1699
|
-
skills: "
|
|
1741
|
+
skills: "no"
|
|
1700
1742
|
};
|
|
1701
1743
|
for (const arg of args) {
|
|
1702
1744
|
if (arg === "--no-tui") {
|
|
@@ -1730,7 +1772,7 @@ Usage:
|
|
|
1730
1772
|
bunx @zcy2nn/agent-forge doctor [OPTIONS]
|
|
1731
1773
|
|
|
1732
1774
|
Options:
|
|
1733
|
-
--skills=yes|no Install recommended
|
|
1775
|
+
--skills=yes|no Install recommended skills like agent-browser (default: no)
|
|
1734
1776
|
--preset=<name> Active generated config preset (default: openai)
|
|
1735
1777
|
--no-tui Non-interactive mode
|
|
1736
1778
|
--dry-run Simulate install without writing files
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SkillSyncResult {
|
|
2
|
+
installed: number;
|
|
3
|
+
skipped: number;
|
|
4
|
+
/** Skills that were already correctly symlinked (no change needed) */
|
|
5
|
+
unchanged: number;
|
|
6
|
+
/** Skills installed via file copy (symlink unavailable) */
|
|
7
|
+
copied: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Ensure all built-in skills are symlinked into OpenCode's skills directory.
|
|
11
|
+
*
|
|
12
|
+
* - If a symlink already points to the correct source → skip (unchanged).
|
|
13
|
+
* - If a directory or stale symlink exists → replace with fresh symlink.
|
|
14
|
+
* - If symlink creation fails (e.g. Windows without junction support) →
|
|
15
|
+
* fall back to file copy.
|
|
16
|
+
* - If the source path doesn't exist in the package → skip.
|
|
17
|
+
*
|
|
18
|
+
* @param packageRoot - Absolute path to the agent-forge package root
|
|
19
|
+
* (the directory containing `src/skills/`). Defaults to auto-detected via resolvePackageRoot().
|
|
20
|
+
* @param skillsDirOverride - Override for the target skills directory
|
|
21
|
+
* (useful in tests). Defaults to `<configDir>/skills`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function syncBuiltinSkills(packageRoot?: string, skillsDirOverride?: string): SkillSyncResult;
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Filter available_skills block based on the current agent's permission.skill rules.
|
|
3
|
-
* OpenCode core injects `<available_skills>`
|
|
4
|
-
* block before the prompt is sent.
|
|
2
|
+
* Filter and inject available_skills block based on the current agent's permission.skill rules.
|
|
3
|
+
* OpenCode core injects `<available_skills>` from installed skills, so this hook rewrites that
|
|
4
|
+
* block before the prompt is sent. Built-in skills from the package are also injected so they
|
|
5
|
+
* are always visible even without being copied to the OpenCode skills directory.
|
|
5
6
|
*/
|
|
6
7
|
import type { PluginInput } from '@opencode-ai/plugin';
|
|
7
8
|
import { type PluginConfig } from '../../config';
|
|
@@ -19,6 +20,14 @@ interface MessageWithParts {
|
|
|
19
20
|
parts: MessagePart[];
|
|
20
21
|
}
|
|
21
22
|
type SkillRule = 'allow' | 'ask' | 'deny';
|
|
23
|
+
interface SkillEntry {
|
|
24
|
+
name: string;
|
|
25
|
+
block: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Merge installed skill entries with built-in entries (dedup by name, installed takes precedence).
|
|
29
|
+
*/
|
|
30
|
+
declare function mergeWithBuiltinSkills(installedEntries: SkillEntry[]): SkillEntry[];
|
|
22
31
|
declare function filterAvailableSkillsText(text: string, permissionRules: Record<string, SkillRule>): string;
|
|
23
32
|
/**
|
|
24
33
|
* Creates the experimental.chat.messages.transform hook for filtering available skills.
|
|
@@ -29,4 +38,4 @@ export declare function createFilterAvailableSkillsHook(_ctx: PluginInput, confi
|
|
|
29
38
|
messages: MessageWithParts[];
|
|
30
39
|
}) => Promise<void>;
|
|
31
40
|
};
|
|
32
|
-
export { filterAvailableSkillsText };
|
|
41
|
+
export { filterAvailableSkillsText, mergeWithBuiltinSkills };
|
package/dist/index.js
CHANGED
|
@@ -30,8 +30,175 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
30
30
|
return to;
|
|
31
31
|
};
|
|
32
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
37
|
+
var __export = (target, all) => {
|
|
38
|
+
for (var name in all)
|
|
39
|
+
__defProp(target, name, {
|
|
40
|
+
get: all[name],
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
33
47
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
34
48
|
|
|
49
|
+
// src/cli/custom-skills.ts
|
|
50
|
+
var CUSTOM_SKILLS;
|
|
51
|
+
var init_custom_skills = __esm(() => {
|
|
52
|
+
CUSTOM_SKILLS = [
|
|
53
|
+
{
|
|
54
|
+
name: "simplify",
|
|
55
|
+
description: "Code simplification and readability-focused refactoring",
|
|
56
|
+
allowedAgents: ["reviewer"],
|
|
57
|
+
sourcePath: "src/skills/simplify"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "codemap",
|
|
61
|
+
description: "Repository understanding and hierarchical codemap generation",
|
|
62
|
+
allowedAgents: ["orchestrator"],
|
|
63
|
+
sourcePath: "src/skills/codemap"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "brainstorming",
|
|
67
|
+
description: "Turn ideas into designs through collaborative dialogue before implementation",
|
|
68
|
+
allowedAgents: ["orchestrator"],
|
|
69
|
+
sourcePath: "src/skills/brainstorming"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "writing-plans",
|
|
73
|
+
description: "Create comprehensive implementation plans from specs before touching code",
|
|
74
|
+
allowedAgents: ["orchestrator"],
|
|
75
|
+
sourcePath: "src/skills/writing-plans"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "executing-plans",
|
|
79
|
+
description: "Execute written implementation plans with review checkpoints",
|
|
80
|
+
allowedAgents: ["orchestrator"],
|
|
81
|
+
sourcePath: "src/skills/executing-plans"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "subagent-driven-development",
|
|
85
|
+
description: "Execute plans by dispatching fresh subagent per task with two-stage review",
|
|
86
|
+
allowedAgents: ["orchestrator"],
|
|
87
|
+
sourcePath: "src/skills/subagent-driven-development"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "dispatching-parallel-agents",
|
|
91
|
+
description: "Dispatch parallel agents for independent tasks without shared state",
|
|
92
|
+
allowedAgents: ["orchestrator"],
|
|
93
|
+
sourcePath: "src/skills/dispatching-parallel-agents"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "using-git-worktrees",
|
|
97
|
+
description: "Create isolated git worktrees before executing implementation plans",
|
|
98
|
+
allowedAgents: ["orchestrator"],
|
|
99
|
+
sourcePath: "src/skills/using-git-worktrees"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "finishing-a-development-branch",
|
|
103
|
+
description: "Guide completion of development work with structured merge/PR options",
|
|
104
|
+
allowedAgents: ["orchestrator"],
|
|
105
|
+
sourcePath: "src/skills/finishing-a-development-branch"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "requesting-code-review",
|
|
109
|
+
description: "Dispatch code reviewer subagent to catch issues before merging",
|
|
110
|
+
allowedAgents: ["orchestrator"],
|
|
111
|
+
sourcePath: "src/skills/requesting-code-review"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "test-driven-development",
|
|
115
|
+
description: "Write failing tests first, then minimal code to pass, before any implementation",
|
|
116
|
+
allowedAgents: ["implementer"],
|
|
117
|
+
sourcePath: "src/skills/test-driven-development"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "verification-before-completion",
|
|
121
|
+
description: "Run verification commands and confirm output before claiming work is complete",
|
|
122
|
+
allowedAgents: ["implementer"],
|
|
123
|
+
sourcePath: "src/skills/verification-before-completion"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "systematic-debugging",
|
|
127
|
+
description: "Find root cause before attempting fixes for any bug or test failure",
|
|
128
|
+
allowedAgents: ["reviewer"],
|
|
129
|
+
sourcePath: "src/skills/systematic-debugging"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "receiving-code-review",
|
|
133
|
+
description: "Evaluate code review feedback with technical rigor before implementing suggestions",
|
|
134
|
+
allowedAgents: ["reviewer"],
|
|
135
|
+
sourcePath: "src/skills/receiving-code-review"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "writing-skills",
|
|
139
|
+
description: "Create and edit skills using TDD methodology for process documentation",
|
|
140
|
+
allowedAgents: ["orchestrator"],
|
|
141
|
+
sourcePath: "src/skills/writing-skills"
|
|
142
|
+
}
|
|
143
|
+
];
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// src/cli/paths.ts
|
|
147
|
+
import { homedir } from "node:os";
|
|
148
|
+
import { dirname, join } from "node:path";
|
|
149
|
+
function getDefaultOpenCodeConfigDir() {
|
|
150
|
+
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
151
|
+
return join(userConfigDir, "opencode");
|
|
152
|
+
}
|
|
153
|
+
function getCustomOpenCodeConfigDir() {
|
|
154
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
155
|
+
return configDir || undefined;
|
|
156
|
+
}
|
|
157
|
+
function getConfigDir() {
|
|
158
|
+
const customConfigDir = getCustomOpenCodeConfigDir();
|
|
159
|
+
if (customConfigDir) {
|
|
160
|
+
return customConfigDir;
|
|
161
|
+
}
|
|
162
|
+
return getDefaultOpenCodeConfigDir();
|
|
163
|
+
}
|
|
164
|
+
function getConfigSearchDirs() {
|
|
165
|
+
const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
|
|
166
|
+
return dirs.filter((dir, index) => {
|
|
167
|
+
return Boolean(dir) && dirs.indexOf(dir) === index;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function getOpenCodeConfigPaths() {
|
|
171
|
+
const configDir = getDefaultOpenCodeConfigDir();
|
|
172
|
+
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
173
|
+
}
|
|
174
|
+
var init_paths = () => {};
|
|
175
|
+
|
|
176
|
+
// src/utils/package-root.ts
|
|
177
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
178
|
+
import { join as join13 } from "node:path";
|
|
179
|
+
function resolvePackageRoot() {
|
|
180
|
+
if (_cachedPackageRoot)
|
|
181
|
+
return _cachedPackageRoot;
|
|
182
|
+
const distDir = import.meta.dirname;
|
|
183
|
+
if (!distDir) {
|
|
184
|
+
throw new Error("import.meta.dirname is not available");
|
|
185
|
+
}
|
|
186
|
+
const npmRoot = join13(distDir, "..", "..");
|
|
187
|
+
if (existsSync10(join13(npmRoot, "src", "skills"))) {
|
|
188
|
+
_cachedPackageRoot = npmRoot;
|
|
189
|
+
return npmRoot;
|
|
190
|
+
}
|
|
191
|
+
const localRoot = join13(distDir, "..");
|
|
192
|
+
if (existsSync10(join13(localRoot, "src", "skills"))) {
|
|
193
|
+
_cachedPackageRoot = localRoot;
|
|
194
|
+
return localRoot;
|
|
195
|
+
}
|
|
196
|
+
_cachedPackageRoot = npmRoot;
|
|
197
|
+
return npmRoot;
|
|
198
|
+
}
|
|
199
|
+
var _cachedPackageRoot;
|
|
200
|
+
var init_package_root = () => {};
|
|
201
|
+
|
|
35
202
|
// node_modules/@mozilla/readability/Readability.js
|
|
36
203
|
var require_Readability = __commonJS((exports, module) => {
|
|
37
204
|
function Readability(doc, options) {
|
|
@@ -18185,136 +18352,81 @@ var require_turndown_cjs = __commonJS((exports, module) => {
|
|
|
18185
18352
|
module.exports = TurndownService;
|
|
18186
18353
|
});
|
|
18187
18354
|
|
|
18188
|
-
// src/cli/
|
|
18189
|
-
|
|
18190
|
-
|
|
18191
|
-
|
|
18192
|
-
|
|
18193
|
-
import {
|
|
18194
|
-
|
|
18195
|
-
|
|
18196
|
-
|
|
18197
|
-
|
|
18198
|
-
|
|
18199
|
-
|
|
18200
|
-
|
|
18201
|
-
}
|
|
18202
|
-
|
|
18203
|
-
|
|
18204
|
-
|
|
18205
|
-
|
|
18355
|
+
// src/cli/skill-sync.ts
|
|
18356
|
+
var exports_skill_sync = {};
|
|
18357
|
+
__export(exports_skill_sync, {
|
|
18358
|
+
syncBuiltinSkills: () => syncBuiltinSkills
|
|
18359
|
+
});
|
|
18360
|
+
import {
|
|
18361
|
+
cpSync,
|
|
18362
|
+
existsSync as existsSync13,
|
|
18363
|
+
lstatSync,
|
|
18364
|
+
mkdirSync as mkdirSync7,
|
|
18365
|
+
readlinkSync,
|
|
18366
|
+
rmSync as rmSync2,
|
|
18367
|
+
symlinkSync
|
|
18368
|
+
} from "node:fs";
|
|
18369
|
+
import { join as join15 } from "node:path";
|
|
18370
|
+
function syncBuiltinSkills(packageRoot, skillsDirOverride) {
|
|
18371
|
+
const resolvedPackageRoot = packageRoot ?? resolvePackageRoot();
|
|
18372
|
+
const skillsDir = skillsDirOverride ?? join15(getConfigDir(), "skills");
|
|
18373
|
+
mkdirSync7(skillsDir, { recursive: true });
|
|
18374
|
+
let installed = 0;
|
|
18375
|
+
let skipped = 0;
|
|
18376
|
+
let unchanged = 0;
|
|
18377
|
+
let copied = 0;
|
|
18378
|
+
for (const skill of CUSTOM_SKILLS) {
|
|
18379
|
+
const sourcePath = join15(resolvedPackageRoot, skill.sourcePath);
|
|
18380
|
+
const linkPath = join15(skillsDir, skill.name);
|
|
18381
|
+
if (!existsSync13(sourcePath)) {
|
|
18382
|
+
skipped++;
|
|
18383
|
+
continue;
|
|
18384
|
+
}
|
|
18385
|
+
try {
|
|
18386
|
+
if (lstatSync(linkPath).isSymbolicLink()) {
|
|
18387
|
+
const currentTarget = getSymlinkTarget(linkPath);
|
|
18388
|
+
if (currentTarget === sourcePath) {
|
|
18389
|
+
unchanged++;
|
|
18390
|
+
continue;
|
|
18391
|
+
}
|
|
18392
|
+
rmSync2(linkPath, { recursive: true, force: true });
|
|
18393
|
+
} else if (existsSync13(linkPath)) {
|
|
18394
|
+
rmSync2(linkPath, { recursive: true, force: true });
|
|
18395
|
+
}
|
|
18396
|
+
} catch {
|
|
18397
|
+
try {
|
|
18398
|
+
rmSync2(linkPath, { recursive: true, force: true });
|
|
18399
|
+
} catch {}
|
|
18400
|
+
}
|
|
18401
|
+
try {
|
|
18402
|
+
symlinkSync(sourcePath, linkPath, "junction");
|
|
18403
|
+
installed++;
|
|
18404
|
+
} catch {
|
|
18405
|
+
try {
|
|
18406
|
+
cpSync(sourcePath, linkPath, { recursive: true, force: true });
|
|
18407
|
+
copied++;
|
|
18408
|
+
} catch {
|
|
18409
|
+
skipped++;
|
|
18410
|
+
}
|
|
18411
|
+
}
|
|
18206
18412
|
}
|
|
18207
|
-
return
|
|
18208
|
-
}
|
|
18209
|
-
function getConfigSearchDirs() {
|
|
18210
|
-
const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
|
|
18211
|
-
return dirs.filter((dir, index) => {
|
|
18212
|
-
return Boolean(dir) && dirs.indexOf(dir) === index;
|
|
18213
|
-
});
|
|
18413
|
+
return { installed, skipped, unchanged, copied };
|
|
18214
18414
|
}
|
|
18215
|
-
function
|
|
18216
|
-
|
|
18217
|
-
|
|
18218
|
-
}
|
|
18219
|
-
|
|
18220
|
-
// src/cli/custom-skills.ts
|
|
18221
|
-
var CUSTOM_SKILLS = [
|
|
18222
|
-
{
|
|
18223
|
-
name: "simplify",
|
|
18224
|
-
description: "Code simplification and readability-focused refactoring",
|
|
18225
|
-
allowedAgents: ["reviewer"],
|
|
18226
|
-
sourcePath: "src/skills/simplify"
|
|
18227
|
-
},
|
|
18228
|
-
{
|
|
18229
|
-
name: "codemap",
|
|
18230
|
-
description: "Repository understanding and hierarchical codemap generation",
|
|
18231
|
-
allowedAgents: ["orchestrator"],
|
|
18232
|
-
sourcePath: "src/skills/codemap"
|
|
18233
|
-
},
|
|
18234
|
-
{
|
|
18235
|
-
name: "brainstorming",
|
|
18236
|
-
description: "Turn ideas into designs through collaborative dialogue before implementation",
|
|
18237
|
-
allowedAgents: ["orchestrator"],
|
|
18238
|
-
sourcePath: "src/skills/brainstorming"
|
|
18239
|
-
},
|
|
18240
|
-
{
|
|
18241
|
-
name: "writing-plans",
|
|
18242
|
-
description: "Create comprehensive implementation plans from specs before touching code",
|
|
18243
|
-
allowedAgents: ["orchestrator"],
|
|
18244
|
-
sourcePath: "src/skills/writing-plans"
|
|
18245
|
-
},
|
|
18246
|
-
{
|
|
18247
|
-
name: "executing-plans",
|
|
18248
|
-
description: "Execute written implementation plans with review checkpoints",
|
|
18249
|
-
allowedAgents: ["orchestrator"],
|
|
18250
|
-
sourcePath: "src/skills/executing-plans"
|
|
18251
|
-
},
|
|
18252
|
-
{
|
|
18253
|
-
name: "subagent-driven-development",
|
|
18254
|
-
description: "Execute plans by dispatching fresh subagent per task with two-stage review",
|
|
18255
|
-
allowedAgents: ["orchestrator"],
|
|
18256
|
-
sourcePath: "src/skills/subagent-driven-development"
|
|
18257
|
-
},
|
|
18258
|
-
{
|
|
18259
|
-
name: "dispatching-parallel-agents",
|
|
18260
|
-
description: "Dispatch parallel agents for independent tasks without shared state",
|
|
18261
|
-
allowedAgents: ["orchestrator"],
|
|
18262
|
-
sourcePath: "src/skills/dispatching-parallel-agents"
|
|
18263
|
-
},
|
|
18264
|
-
{
|
|
18265
|
-
name: "using-git-worktrees",
|
|
18266
|
-
description: "Create isolated git worktrees before executing implementation plans",
|
|
18267
|
-
allowedAgents: ["orchestrator"],
|
|
18268
|
-
sourcePath: "src/skills/using-git-worktrees"
|
|
18269
|
-
},
|
|
18270
|
-
{
|
|
18271
|
-
name: "finishing-a-development-branch",
|
|
18272
|
-
description: "Guide completion of development work with structured merge/PR options",
|
|
18273
|
-
allowedAgents: ["orchestrator"],
|
|
18274
|
-
sourcePath: "src/skills/finishing-a-development-branch"
|
|
18275
|
-
},
|
|
18276
|
-
{
|
|
18277
|
-
name: "requesting-code-review",
|
|
18278
|
-
description: "Dispatch code reviewer subagent to catch issues before merging",
|
|
18279
|
-
allowedAgents: ["orchestrator"],
|
|
18280
|
-
sourcePath: "src/skills/requesting-code-review"
|
|
18281
|
-
},
|
|
18282
|
-
{
|
|
18283
|
-
name: "test-driven-development",
|
|
18284
|
-
description: "Write failing tests first, then minimal code to pass, before any implementation",
|
|
18285
|
-
allowedAgents: ["implementer"],
|
|
18286
|
-
sourcePath: "src/skills/test-driven-development"
|
|
18287
|
-
},
|
|
18288
|
-
{
|
|
18289
|
-
name: "verification-before-completion",
|
|
18290
|
-
description: "Run verification commands and confirm output before claiming work is complete",
|
|
18291
|
-
allowedAgents: ["implementer"],
|
|
18292
|
-
sourcePath: "src/skills/verification-before-completion"
|
|
18293
|
-
},
|
|
18294
|
-
{
|
|
18295
|
-
name: "systematic-debugging",
|
|
18296
|
-
description: "Find root cause before attempting fixes for any bug or test failure",
|
|
18297
|
-
allowedAgents: ["reviewer"],
|
|
18298
|
-
sourcePath: "src/skills/systematic-debugging"
|
|
18299
|
-
},
|
|
18300
|
-
{
|
|
18301
|
-
name: "receiving-code-review",
|
|
18302
|
-
description: "Evaluate code review feedback with technical rigor before implementing suggestions",
|
|
18303
|
-
allowedAgents: ["reviewer"],
|
|
18304
|
-
sourcePath: "src/skills/receiving-code-review"
|
|
18305
|
-
},
|
|
18306
|
-
{
|
|
18307
|
-
name: "writing-skills",
|
|
18308
|
-
description: "Create and edit skills using TDD methodology for process documentation",
|
|
18309
|
-
allowedAgents: ["orchestrator"],
|
|
18310
|
-
sourcePath: "src/skills/writing-skills"
|
|
18415
|
+
function getSymlinkTarget(linkPath) {
|
|
18416
|
+
try {
|
|
18417
|
+
return readlinkSync(linkPath, "utf-8");
|
|
18418
|
+
} catch {
|
|
18419
|
+
return "";
|
|
18311
18420
|
}
|
|
18312
|
-
];
|
|
18313
|
-
function getCustomSkillsDir() {
|
|
18314
|
-
return join2(getConfigDir(), "skills");
|
|
18315
18421
|
}
|
|
18422
|
+
var init_skill_sync = __esm(() => {
|
|
18423
|
+
init_package_root();
|
|
18424
|
+
init_custom_skills();
|
|
18425
|
+
init_paths();
|
|
18426
|
+
});
|
|
18316
18427
|
|
|
18317
18428
|
// src/cli/skills.ts
|
|
18429
|
+
init_custom_skills();
|
|
18318
18430
|
var RECOMMENDED_SKILLS = [
|
|
18319
18431
|
{
|
|
18320
18432
|
name: "agent-browser",
|
|
@@ -18474,6 +18586,9 @@ var CouncilConfigSchema = z.object({
|
|
|
18474
18586
|
import * as fs from "node:fs";
|
|
18475
18587
|
import * as path from "node:path";
|
|
18476
18588
|
|
|
18589
|
+
// src/cli/config-io.ts
|
|
18590
|
+
init_paths();
|
|
18591
|
+
|
|
18477
18592
|
// src/config/agent-mcps.ts
|
|
18478
18593
|
var DEFAULT_AGENT_MCPS = {
|
|
18479
18594
|
orchestrator: ["*", "!context7"],
|
|
@@ -18506,6 +18621,9 @@ function getAgentMcpList(agentName, config) {
|
|
|
18506
18621
|
return defaultMcps ?? [];
|
|
18507
18622
|
}
|
|
18508
18623
|
|
|
18624
|
+
// src/cli/providers.ts
|
|
18625
|
+
init_custom_skills();
|
|
18626
|
+
|
|
18509
18627
|
// src/cli/config-io.ts
|
|
18510
18628
|
function stripJsonComments(json) {
|
|
18511
18629
|
const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
|
|
@@ -18513,6 +18631,9 @@ function stripJsonComments(json) {
|
|
|
18513
18631
|
return json.replace(commentPattern, (match, commentGroup) => commentGroup ? "" : match).replace(trailingCommaPattern, (match, comma, closing) => comma ? closing : match);
|
|
18514
18632
|
}
|
|
18515
18633
|
|
|
18634
|
+
// src/config/loader.ts
|
|
18635
|
+
init_paths();
|
|
18636
|
+
|
|
18516
18637
|
// src/config/schema.ts
|
|
18517
18638
|
import { z as z2 } from "zod";
|
|
18518
18639
|
var ProviderModelIdSchema = z2.string().regex(/^[^/\s]+\/[^\s]+$/, "Expected provider/model format (provider/.../model)");
|
|
@@ -21837,6 +21958,10 @@ async function crossWrite(path6, data) {
|
|
|
21837
21958
|
// src/hooks/auto-update-checker/cache.ts
|
|
21838
21959
|
import * as fs5 from "node:fs";
|
|
21839
21960
|
import * as path8 from "node:path";
|
|
21961
|
+
|
|
21962
|
+
// src/cli/config-manager.ts
|
|
21963
|
+
init_paths();
|
|
21964
|
+
|
|
21840
21965
|
// src/hooks/auto-update-checker/checker.ts
|
|
21841
21966
|
import * as fs4 from "node:fs";
|
|
21842
21967
|
import * as path7 from "node:path";
|
|
@@ -22808,8 +22933,22 @@ ${buildRetryGuidance(detected)}`;
|
|
|
22808
22933
|
};
|
|
22809
22934
|
}
|
|
22810
22935
|
// src/hooks/filter-available-skills/index.ts
|
|
22936
|
+
init_custom_skills();
|
|
22811
22937
|
var AVAILABLE_SKILLS_BLOCK_REGEX = /<available_skills>\s*([\s\S]*?)\s*<\/available_skills>/g;
|
|
22812
22938
|
var SKILL_NAME_REGEX = /<name>([^<]+)<\/name>/;
|
|
22939
|
+
function builtinSkillBlock(name, description) {
|
|
22940
|
+
return `<skill>
|
|
22941
|
+
<name>${name}</name>
|
|
22942
|
+
<description>${description}</description>
|
|
22943
|
+
<location>builtin</location>
|
|
22944
|
+
</skill>`;
|
|
22945
|
+
}
|
|
22946
|
+
function getBuiltinSkillEntries() {
|
|
22947
|
+
return CUSTOM_SKILLS.map((skill) => ({
|
|
22948
|
+
name: skill.name,
|
|
22949
|
+
block: builtinSkillBlock(skill.name, skill.description)
|
|
22950
|
+
}));
|
|
22951
|
+
}
|
|
22813
22952
|
function getCurrentAgent(messages) {
|
|
22814
22953
|
for (let index = messages.length - 1;index >= 0; index -= 1) {
|
|
22815
22954
|
const message = messages[index];
|
|
@@ -22842,9 +22981,21 @@ function isSkillAllowed(skillName, permissionRules) {
|
|
|
22842
22981
|
}
|
|
22843
22982
|
return permissionRules["*"] === "allow";
|
|
22844
22983
|
}
|
|
22984
|
+
function mergeWithBuiltinSkills(installedEntries) {
|
|
22985
|
+
const builtinEntries = getBuiltinSkillEntries();
|
|
22986
|
+
const installedNames = new Set(installedEntries.map((e) => e.name));
|
|
22987
|
+
for (const entry of builtinEntries) {
|
|
22988
|
+
if (!installedNames.has(entry.name)) {
|
|
22989
|
+
installedEntries.push(entry);
|
|
22990
|
+
}
|
|
22991
|
+
}
|
|
22992
|
+
return installedEntries;
|
|
22993
|
+
}
|
|
22845
22994
|
function filterAvailableSkillsText(text, permissionRules) {
|
|
22846
22995
|
return text.replace(AVAILABLE_SKILLS_BLOCK_REGEX, (_fullMatch, blockContent) => {
|
|
22847
|
-
|
|
22996
|
+
let allEntries = extractSkillEntries(blockContent);
|
|
22997
|
+
allEntries = mergeWithBuiltinSkills(allEntries);
|
|
22998
|
+
const allowedEntries = allEntries.filter((entry) => isSkillAllowed(entry.name, permissionRules));
|
|
22848
22999
|
if (allowedEntries.length === 0) {
|
|
22849
23000
|
return `<available_skills>
|
|
22850
23001
|
No skills available.
|
|
@@ -22856,6 +23007,22 @@ ${allowedEntries.map((entry) => entry.block).join(`
|
|
|
22856
23007
|
</available_skills>`;
|
|
22857
23008
|
});
|
|
22858
23009
|
}
|
|
23010
|
+
function injectBuiltinSkillsIfMissing(text, permissionRules) {
|
|
23011
|
+
if (text.includes("<available_skills>")) {
|
|
23012
|
+
return text;
|
|
23013
|
+
}
|
|
23014
|
+
const builtinEntries = getBuiltinSkillEntries();
|
|
23015
|
+
const allowedEntries = builtinEntries.filter((entry) => isSkillAllowed(entry.name, permissionRules));
|
|
23016
|
+
if (allowedEntries.length === 0) {
|
|
23017
|
+
return text;
|
|
23018
|
+
}
|
|
23019
|
+
const block = `<available_skills>
|
|
23020
|
+
${allowedEntries.map((entry) => entry.block).join(`
|
|
23021
|
+
`)}
|
|
23022
|
+
</available_skills>`;
|
|
23023
|
+
return `${text}
|
|
23024
|
+
${block}`;
|
|
23025
|
+
}
|
|
22859
23026
|
function createFilterAvailableSkillsHook(_ctx, config) {
|
|
22860
23027
|
const permissionRulesByAgent = new Map;
|
|
22861
23028
|
const getPermissionRules = (agentName) => {
|
|
@@ -22878,10 +23045,14 @@ function createFilterAvailableSkillsHook(_ctx, config) {
|
|
|
22878
23045
|
const permissionRules = getPermissionRules(agentName);
|
|
22879
23046
|
for (const message of messages) {
|
|
22880
23047
|
for (const part of message.parts) {
|
|
22881
|
-
if (part.type !== "text" || !part.text
|
|
23048
|
+
if (part.type !== "text" || !part.text) {
|
|
22882
23049
|
continue;
|
|
22883
23050
|
}
|
|
22884
|
-
|
|
23051
|
+
if (part.text.includes("<available_skills>")) {
|
|
23052
|
+
part.text = filterAvailableSkillsText(part.text, permissionRules);
|
|
23053
|
+
} else {
|
|
23054
|
+
part.text = injectBuiltinSkillsIfMissing(part.text, permissionRules);
|
|
23055
|
+
}
|
|
22885
23056
|
}
|
|
22886
23057
|
}
|
|
22887
23058
|
}
|
|
@@ -23123,7 +23294,7 @@ import {
|
|
|
23123
23294
|
unlinkSync as unlinkSync2,
|
|
23124
23295
|
writeFileSync as writeFileSync3
|
|
23125
23296
|
} from "node:fs";
|
|
23126
|
-
import { basename as basename2, extname, join as
|
|
23297
|
+
import { basename as basename2, extname, join as join7 } from "node:path";
|
|
23127
23298
|
var lastCleanupByDir = new Map;
|
|
23128
23299
|
var CLEANUP_INTERVAL = 10 * 60 * 1000;
|
|
23129
23300
|
function isImagePart(p) {
|
|
@@ -23171,7 +23342,7 @@ function cleanupAllSessions(saveDir) {
|
|
|
23171
23342
|
const dirsToScan = [];
|
|
23172
23343
|
try {
|
|
23173
23344
|
for (const entry of readdirSync2(saveDir, { withFileTypes: true })) {
|
|
23174
|
-
const fp =
|
|
23345
|
+
const fp = join7(saveDir, entry.name);
|
|
23175
23346
|
if (entry.isDirectory()) {
|
|
23176
23347
|
dirsToScan.push(fp);
|
|
23177
23348
|
} else {
|
|
@@ -23188,7 +23359,7 @@ function cleanupAllSessions(saveDir) {
|
|
|
23188
23359
|
let allRemoved = true;
|
|
23189
23360
|
for (const f of readdirSync2(dir)) {
|
|
23190
23361
|
isEmpty = false;
|
|
23191
|
-
const fp =
|
|
23362
|
+
const fp = join7(dir, f);
|
|
23192
23363
|
try {
|
|
23193
23364
|
if (now - statSync3(fp).mtimeMs > maxAge) {
|
|
23194
23365
|
unlinkSync2(fp);
|
|
@@ -23210,7 +23381,7 @@ function cleanupAllSessions(saveDir) {
|
|
|
23210
23381
|
function writeUniqueFile(dir, name, data, log2) {
|
|
23211
23382
|
const ext = extname(name);
|
|
23212
23383
|
const base = basename2(name, ext) || name;
|
|
23213
|
-
let candidate =
|
|
23384
|
+
let candidate = join7(dir, name);
|
|
23214
23385
|
if (existsSync5(candidate)) {
|
|
23215
23386
|
return candidate;
|
|
23216
23387
|
}
|
|
@@ -23223,7 +23394,7 @@ function writeUniqueFile(dir, name, data, log2) {
|
|
|
23223
23394
|
} catch (e) {
|
|
23224
23395
|
if (e instanceof Error && e.code === "EEXIST") {
|
|
23225
23396
|
counter += 1;
|
|
23226
|
-
candidate =
|
|
23397
|
+
candidate = join7(dir, `${base}-${counter}${ext}`);
|
|
23227
23398
|
continue;
|
|
23228
23399
|
}
|
|
23229
23400
|
log2(`[image-hook] failed to save image: ${e}`);
|
|
@@ -23247,13 +23418,13 @@ function processImageAttachments(args) {
|
|
|
23247
23418
|
messagesWithImages.push({ msg, imageParts });
|
|
23248
23419
|
}
|
|
23249
23420
|
}
|
|
23250
|
-
const saveDir =
|
|
23421
|
+
const saveDir = join7(workDir, ".opencode", "images");
|
|
23251
23422
|
if (messagesWithImages.length === 0) {
|
|
23252
23423
|
if (existsSync5(saveDir))
|
|
23253
23424
|
cleanupAllSessions(saveDir);
|
|
23254
23425
|
return;
|
|
23255
23426
|
}
|
|
23256
|
-
const gitignorePath =
|
|
23427
|
+
const gitignorePath = join7(workDir, ".opencode", ".gitignore");
|
|
23257
23428
|
try {
|
|
23258
23429
|
mkdirSync3(saveDir, { recursive: true });
|
|
23259
23430
|
if (!existsSync5(gitignorePath))
|
|
@@ -23265,7 +23436,7 @@ function processImageAttachments(args) {
|
|
|
23265
23436
|
cleanupAllSessions(saveDir);
|
|
23266
23437
|
for (const { msg, imageParts } of messagesWithImages) {
|
|
23267
23438
|
const sessionSubdir = msg.info.sessionID ? sanitizeFilename(msg.info.sessionID) : undefined;
|
|
23268
|
-
const targetDir = sessionSubdir ?
|
|
23439
|
+
const targetDir = sessionSubdir ? join7(saveDir, sessionSubdir) : saveDir;
|
|
23269
23440
|
try {
|
|
23270
23441
|
mkdirSync3(targetDir, { recursive: true });
|
|
23271
23442
|
} catch (e) {
|
|
@@ -29236,13 +29407,13 @@ import { existsSync as existsSync9 } from "node:fs";
|
|
|
29236
29407
|
// src/tools/ast-grep/constants.ts
|
|
29237
29408
|
import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
|
|
29238
29409
|
import { createRequire as createRequire3 } from "node:module";
|
|
29239
|
-
import { dirname as
|
|
29410
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
29240
29411
|
|
|
29241
29412
|
// src/tools/ast-grep/downloader.ts
|
|
29242
29413
|
import { chmodSync, existsSync as existsSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "node:fs";
|
|
29243
29414
|
import { createRequire as createRequire2 } from "node:module";
|
|
29244
29415
|
import { homedir as homedir5 } from "node:os";
|
|
29245
|
-
import { join as
|
|
29416
|
+
import { join as join10 } from "node:path";
|
|
29246
29417
|
var REPO = "ast-grep/ast-grep";
|
|
29247
29418
|
var DEFAULT_VERSION = "0.40.0";
|
|
29248
29419
|
function getAstGrepVersion() {
|
|
@@ -29266,18 +29437,18 @@ var PLATFORM_MAP = {
|
|
|
29266
29437
|
function getCacheDir2() {
|
|
29267
29438
|
if (process.platform === "win32") {
|
|
29268
29439
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
29269
|
-
const base2 = localAppData ||
|
|
29270
|
-
return
|
|
29440
|
+
const base2 = localAppData || join10(homedir5(), "AppData", "Local");
|
|
29441
|
+
return join10(base2, "agent-forge", "bin");
|
|
29271
29442
|
}
|
|
29272
29443
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
29273
|
-
const base = xdgCache ||
|
|
29274
|
-
return
|
|
29444
|
+
const base = xdgCache || join10(homedir5(), ".cache");
|
|
29445
|
+
return join10(base, "agent-forge", "bin");
|
|
29275
29446
|
}
|
|
29276
29447
|
function getBinaryName() {
|
|
29277
29448
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
29278
29449
|
}
|
|
29279
29450
|
function getCachedBinaryPath() {
|
|
29280
|
-
const binaryPath =
|
|
29451
|
+
const binaryPath = join10(getCacheDir2(), getBinaryName());
|
|
29281
29452
|
return existsSync7(binaryPath) ? binaryPath : null;
|
|
29282
29453
|
}
|
|
29283
29454
|
async function downloadAstGrep(version = DEFAULT_VERSION) {
|
|
@@ -29289,7 +29460,7 @@ async function downloadAstGrep(version = DEFAULT_VERSION) {
|
|
|
29289
29460
|
}
|
|
29290
29461
|
const cacheDir = getCacheDir2();
|
|
29291
29462
|
const binaryName = getBinaryName();
|
|
29292
|
-
const binaryPath =
|
|
29463
|
+
const binaryPath = join10(cacheDir, binaryName);
|
|
29293
29464
|
if (existsSync7(binaryPath)) {
|
|
29294
29465
|
return binaryPath;
|
|
29295
29466
|
}
|
|
@@ -29305,7 +29476,7 @@ async function downloadAstGrep(version = DEFAULT_VERSION) {
|
|
|
29305
29476
|
if (!response.ok) {
|
|
29306
29477
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
29307
29478
|
}
|
|
29308
|
-
const archivePath =
|
|
29479
|
+
const archivePath = join10(cacheDir, assetName);
|
|
29309
29480
|
const arrayBuffer = await response.arrayBuffer();
|
|
29310
29481
|
await crossWrite(archivePath, arrayBuffer);
|
|
29311
29482
|
await extractZip(archivePath, cacheDir);
|
|
@@ -29393,8 +29564,8 @@ function findSgCliPathSync() {
|
|
|
29393
29564
|
try {
|
|
29394
29565
|
const require2 = createRequire3(import.meta.url);
|
|
29395
29566
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
29396
|
-
const cliDir =
|
|
29397
|
-
const sgPath =
|
|
29567
|
+
const cliDir = dirname6(cliPkgPath);
|
|
29568
|
+
const sgPath = join11(cliDir, binaryName);
|
|
29398
29569
|
if (existsSync8(sgPath) && isValidBinary(sgPath)) {
|
|
29399
29570
|
return sgPath;
|
|
29400
29571
|
}
|
|
@@ -29404,9 +29575,9 @@ function findSgCliPathSync() {
|
|
|
29404
29575
|
try {
|
|
29405
29576
|
const require2 = createRequire3(import.meta.url);
|
|
29406
29577
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
29407
|
-
const pkgDir =
|
|
29578
|
+
const pkgDir = dirname6(pkgPath);
|
|
29408
29579
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
29409
|
-
const binaryPath =
|
|
29580
|
+
const binaryPath = join11(pkgDir, astGrepName);
|
|
29410
29581
|
if (existsSync8(binaryPath) && isValidBinary(binaryPath)) {
|
|
29411
29582
|
return binaryPath;
|
|
29412
29583
|
}
|
|
@@ -30072,18 +30243,15 @@ Usage: /preset <name> to switch.`);
|
|
|
30072
30243
|
};
|
|
30073
30244
|
}
|
|
30074
30245
|
// src/tools/skill.ts
|
|
30075
|
-
import { existsSync as
|
|
30246
|
+
import { existsSync as existsSync11, readFileSync as readFileSync5 } from "node:fs";
|
|
30076
30247
|
import { join as join14 } from "node:path";
|
|
30077
30248
|
import { tool as tool4 } from "@opencode-ai/plugin/tool";
|
|
30249
|
+
init_package_root();
|
|
30078
30250
|
var z5 = tool4.schema;
|
|
30079
30251
|
function resolveSkillPath(skillName) {
|
|
30080
|
-
const
|
|
30081
|
-
if (existsSync10(installedPath)) {
|
|
30082
|
-
return installedPath;
|
|
30083
|
-
}
|
|
30084
|
-
const packageRoot = join14(import.meta.dirname, "..", "..");
|
|
30252
|
+
const packageRoot = resolvePackageRoot();
|
|
30085
30253
|
const bundledPath = join14(packageRoot, "src", "skills", skillName, "SKILL.md");
|
|
30086
|
-
if (
|
|
30254
|
+
if (existsSync11(bundledPath)) {
|
|
30087
30255
|
return bundledPath;
|
|
30088
30256
|
}
|
|
30089
30257
|
return;
|
|
@@ -31739,9 +31907,10 @@ function isInvalidLlmsResult(fetchResult) {
|
|
|
31739
31907
|
}
|
|
31740
31908
|
|
|
31741
31909
|
// src/tools/smartfetch/secondary-model.ts
|
|
31742
|
-
import { existsSync as
|
|
31910
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
31743
31911
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
31744
31912
|
import path17 from "node:path";
|
|
31913
|
+
init_paths();
|
|
31745
31914
|
function parseModelRef(value) {
|
|
31746
31915
|
if (!value)
|
|
31747
31916
|
return;
|
|
@@ -31768,7 +31937,7 @@ function pickAgentModelRef(value) {
|
|
|
31768
31937
|
function findPreferredOpenCodeConfigPath(baseDir) {
|
|
31769
31938
|
for (const file of ["opencode.jsonc", "opencode.json"]) {
|
|
31770
31939
|
const fullPath = path17.join(baseDir, file);
|
|
31771
|
-
if (
|
|
31940
|
+
if (existsSync12(fullPath))
|
|
31772
31941
|
return fullPath;
|
|
31773
31942
|
}
|
|
31774
31943
|
return;
|
|
@@ -32610,6 +32779,15 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
32610
32779
|
mcps = createBuiltinMcps(config.disabled_mcps, config.websearch);
|
|
32611
32780
|
webfetch = createWebfetchTool(ctx);
|
|
32612
32781
|
skillTools = createSkillTool(config);
|
|
32782
|
+
try {
|
|
32783
|
+
const { syncBuiltinSkills: syncBuiltinSkills2 } = await Promise.resolve().then(() => (init_skill_sync(), exports_skill_sync));
|
|
32784
|
+
const syncResult = syncBuiltinSkills2();
|
|
32785
|
+
if (syncResult.installed > 0 || syncResult.skipped > 0) {
|
|
32786
|
+
log("[plugin] skill sync:", syncResult);
|
|
32787
|
+
}
|
|
32788
|
+
} catch (err) {
|
|
32789
|
+
log("[plugin] skill sync failed (non-fatal):", err);
|
|
32790
|
+
}
|
|
32613
32791
|
multiplexerSessionManager = new MultiplexerSessionManager(ctx, multiplexerConfig);
|
|
32614
32792
|
autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
|
|
32615
32793
|
autoUpdate: config.autoUpdate ?? true
|
package/dist/tui.js
CHANGED
|
@@ -30,6 +30,20 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
30
30
|
return to;
|
|
31
31
|
};
|
|
32
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
37
|
+
var __export = (target, all) => {
|
|
38
|
+
for (var name in all)
|
|
39
|
+
__defProp(target, name, {
|
|
40
|
+
get: all[name],
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
33
47
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
34
48
|
|
|
35
49
|
// src/tui.ts
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolvePackageRoot(): string;
|
package/package.json
CHANGED