agentloom 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -6
- package/dist/cli.js +11 -4
- package/dist/commands/add.js +14 -0
- package/dist/commands/delete.js +89 -3
- package/dist/commands/entity-utils.js +3 -0
- package/dist/commands/find.js +146 -12
- package/dist/commands/rule.d.ts +2 -0
- package/dist/commands/rule.js +86 -0
- package/dist/commands/sync.js +13 -4
- package/dist/commands/update.js +90 -7
- package/dist/core/agents.js +1 -1
- package/dist/core/argv.js +2 -0
- package/dist/core/commands.d.ts +12 -0
- package/dist/core/commands.js +106 -6
- package/dist/core/copy.js +12 -5
- package/dist/core/importer.d.ts +10 -0
- package/dist/core/importer.js +629 -13
- package/dist/core/lockfile.js +8 -0
- package/dist/core/manifest.js +123 -6
- package/dist/core/migration.js +655 -66
- package/dist/core/provider-entity-validation.d.ts +8 -0
- package/dist/core/provider-entity-validation.js +34 -0
- package/dist/core/provider-paths.d.ts +8 -1
- package/dist/core/provider-paths.js +69 -5
- package/dist/core/router.js +7 -1
- package/dist/core/rules.d.ts +34 -0
- package/dist/core/rules.js +149 -0
- package/dist/core/scope.js +1 -0
- package/dist/core/skills.d.ts +1 -0
- package/dist/core/skills.js +21 -2
- package/dist/core/sources.d.ts +2 -0
- package/dist/core/sources.js +34 -5
- package/dist/core/telemetry.d.ts +1 -1
- package/dist/core/telemetry.js +16 -0
- package/dist/sync/index.js +376 -18
- package/dist/types.d.ts +5 -1
- package/package.json +1 -1
package/dist/commands/find.js
CHANGED
|
@@ -6,6 +6,7 @@ import { parseAgentMarkdown, parseAgentsDir } from "../core/agents.js";
|
|
|
6
6
|
import { parseCommandsDir } from "../core/commands.js";
|
|
7
7
|
import { formatUsageError, getFindHelpText } from "../core/copy.js";
|
|
8
8
|
import { readCanonicalMcp } from "../core/mcp.js";
|
|
9
|
+
import { parseRulesDir } from "../core/rules.js";
|
|
9
10
|
import { buildScopePaths } from "../core/scope.js";
|
|
10
11
|
import { parseSkillsDir } from "../core/skills.js";
|
|
11
12
|
const FIND_API_BASE = process.env.AGENTLOOM_FIND_API_BASE || "https://api.github.com";
|
|
@@ -34,6 +35,7 @@ export async function runScopedFindCommand(argv, target, searchClient = searchAg
|
|
|
34
35
|
const shouldSearchAgents = target === "all" || target === "agent";
|
|
35
36
|
const shouldSearchCommands = target === "all" || target === "command";
|
|
36
37
|
const shouldSearchMcp = target === "all" || target === "mcp";
|
|
38
|
+
const shouldSearchRules = target === "all" || target === "rule";
|
|
37
39
|
const shouldSearchSkills = target === "all" || target === "skill";
|
|
38
40
|
const agentResult = shouldSearchAgents
|
|
39
41
|
? normalizeSearchResult(await searchClient(query, DEFAULT_RESULT_LIMIT))
|
|
@@ -44,6 +46,9 @@ export async function runScopedFindCommand(argv, target, searchClient = searchAg
|
|
|
44
46
|
const mcpResult = shouldSearchMcp
|
|
45
47
|
? await searchMcpWithDiagnostics(query, DEFAULT_RESULT_LIMIT, FIND_API_BASE)
|
|
46
48
|
: { items: [], failures: [] };
|
|
49
|
+
const ruleResult = shouldSearchRules
|
|
50
|
+
? await searchRulesWithDiagnostics(query, DEFAULT_RESULT_LIMIT, FIND_API_BASE)
|
|
51
|
+
: { items: [], failures: [] };
|
|
47
52
|
const skillResult = shouldSearchSkills
|
|
48
53
|
? await searchSkillsWithDiagnostics(query, DEFAULT_RESULT_LIMIT, FIND_API_BASE)
|
|
49
54
|
: { items: [], failures: [] };
|
|
@@ -85,12 +90,14 @@ export async function runScopedFindCommand(argv, target, searchClient = searchAg
|
|
|
85
90
|
const total = agentResult.agents.length +
|
|
86
91
|
commandResult.items.length +
|
|
87
92
|
mcpResult.items.length +
|
|
93
|
+
ruleResult.items.length +
|
|
88
94
|
skillResult.items.length +
|
|
89
95
|
local.total;
|
|
90
96
|
if (total === 0) {
|
|
91
97
|
const failureCount = agentResult.failures.length +
|
|
92
98
|
commandResult.failures.length +
|
|
93
99
|
mcpResult.failures.length +
|
|
100
|
+
ruleResult.failures.length +
|
|
94
101
|
skillResult.failures.length;
|
|
95
102
|
if (failureCount > 0) {
|
|
96
103
|
throw new Error(`Search returned no results and encountered ${failureCount} remote scan failure(s).`);
|
|
@@ -134,6 +141,15 @@ export async function runScopedFindCommand(argv, target, searchClient = searchAg
|
|
|
134
141
|
}
|
|
135
142
|
console.log("");
|
|
136
143
|
}
|
|
144
|
+
if (ruleResult.items.length > 0) {
|
|
145
|
+
console.log("Rule matches:");
|
|
146
|
+
for (const result of ruleResult.items) {
|
|
147
|
+
console.log(` - ${result.repo}@${result.ruleName}${formatStars(result.stars)} (${result.filePath})`);
|
|
148
|
+
console.log(` ${result.fileUrl}`);
|
|
149
|
+
console.log(` Install: ${buildRuleInstallCommand(result)}`);
|
|
150
|
+
}
|
|
151
|
+
console.log("");
|
|
152
|
+
}
|
|
137
153
|
if (skillResult.items.length > 0) {
|
|
138
154
|
console.log("Skill matches:");
|
|
139
155
|
for (const result of skillResult.items) {
|
|
@@ -147,6 +163,7 @@ export async function runScopedFindCommand(argv, target, searchClient = searchAg
|
|
|
147
163
|
...agentResult.failures,
|
|
148
164
|
...commandResult.failures,
|
|
149
165
|
...mcpResult.failures,
|
|
166
|
+
...ruleResult.failures,
|
|
150
167
|
...skillResult.failures,
|
|
151
168
|
];
|
|
152
169
|
if (failures.length > 0) {
|
|
@@ -351,6 +368,42 @@ async function searchSkillsWithDiagnostics(query, limit, apiBase) {
|
|
|
351
368
|
}));
|
|
352
369
|
return normalizeEntityScanResults(scanned, limit);
|
|
353
370
|
}
|
|
371
|
+
async function searchRulesWithDiagnostics(query, limit, apiBase) {
|
|
372
|
+
const tokens = tokenizeQuery(query);
|
|
373
|
+
const repos = await searchReposByQuery(query, apiBase);
|
|
374
|
+
const candidates = repos.slice(0, DEFAULT_REPO_SCAN_LIMIT);
|
|
375
|
+
const scanned = await Promise.allSettled(candidates.map(async (repo) => {
|
|
376
|
+
const tree = await getRepoTree(repo, apiBase);
|
|
377
|
+
const matches = [];
|
|
378
|
+
for (const entry of tree) {
|
|
379
|
+
if (entry.type !== "blob")
|
|
380
|
+
continue;
|
|
381
|
+
const filePath = toTrimmedString(entry.path);
|
|
382
|
+
if (!filePath)
|
|
383
|
+
continue;
|
|
384
|
+
const parsed = parseRulePath(filePath);
|
|
385
|
+
if (!parsed)
|
|
386
|
+
continue;
|
|
387
|
+
const haystack = `${repo.fullName} ${filePath} ${parsed.ruleName}`.toLowerCase();
|
|
388
|
+
if (tokens.length > 0 &&
|
|
389
|
+
!tokens.some((token) => haystack.includes(token))) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
matches.push({
|
|
393
|
+
repo: repo.fullName,
|
|
394
|
+
ruleName: parsed.ruleName,
|
|
395
|
+
installRuleSelector: parsed.installRuleSelector,
|
|
396
|
+
filePath,
|
|
397
|
+
fileUrl: `${repo.repoWebUrl}/blob/${repo.defaultBranch}/${filePath}`,
|
|
398
|
+
source: repo.installSource,
|
|
399
|
+
stars: repo.stars,
|
|
400
|
+
subdir: parsed.subdir,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return matches;
|
|
404
|
+
}));
|
|
405
|
+
return normalizeEntityScanResults(scanned, limit);
|
|
406
|
+
}
|
|
354
407
|
async function searchReposByQuery(query, apiBase) {
|
|
355
408
|
const url = buildApiUrl(apiBase, "search/repositories");
|
|
356
409
|
url.searchParams.set("q", `${query} in:name,description,readme`);
|
|
@@ -433,6 +486,17 @@ async function getRepoTree(repo, apiBase) {
|
|
|
433
486
|
return payload.tree;
|
|
434
487
|
}
|
|
435
488
|
function parseAgentPath(filePath) {
|
|
489
|
+
const directGitHub = filePath.match(/^\.github\/agents\/([^/]+)\.agent\.md$/i);
|
|
490
|
+
if (directGitHub) {
|
|
491
|
+
return { agentName: directGitHub[1] };
|
|
492
|
+
}
|
|
493
|
+
const nestedGitHub = filePath.match(/^(.+)\/\.github\/agents\/([^/]+)\.agent\.md$/i);
|
|
494
|
+
if (nestedGitHub) {
|
|
495
|
+
return {
|
|
496
|
+
subdir: nestedGitHub[1],
|
|
497
|
+
agentName: nestedGitHub[2],
|
|
498
|
+
};
|
|
499
|
+
}
|
|
436
500
|
const directAgentloom = filePath.match(/^\.agents\/agents\/([^/]+)\.md$/);
|
|
437
501
|
if (directAgentloom) {
|
|
438
502
|
return { agentName: directAgentloom[1] };
|
|
@@ -458,41 +522,55 @@ function parseAgentPath(filePath) {
|
|
|
458
522
|
return null;
|
|
459
523
|
}
|
|
460
524
|
function parseCommandPath(filePath) {
|
|
461
|
-
const
|
|
525
|
+
const directGitHub = filePath.match(/^\.github\/prompts\/([^/]+)\.prompt\.md$/i);
|
|
526
|
+
if (directGitHub) {
|
|
527
|
+
return { commandName: stripPromptSuffix(directGitHub[1]) };
|
|
528
|
+
}
|
|
529
|
+
const nestedGitHub = filePath.match(/^(.+)\/\.github\/prompts\/([^/]+)\.prompt\.md$/i);
|
|
530
|
+
if (nestedGitHub) {
|
|
531
|
+
return {
|
|
532
|
+
subdir: nestedGitHub[1],
|
|
533
|
+
commandName: stripPromptSuffix(nestedGitHub[2]),
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const directAgentloom = filePath.match(/^\.agents\/commands\/([^/]+)(?:\.prompt)?\.(md|mdc)$/i);
|
|
462
537
|
if (directAgentloom) {
|
|
463
|
-
return { commandName: directAgentloom[1] };
|
|
538
|
+
return { commandName: stripPromptSuffix(directAgentloom[1]) };
|
|
464
539
|
}
|
|
465
|
-
const nestedAgentloom = filePath.match(/^(.+)\/\.agents\/commands\/([^/]+)
|
|
540
|
+
const nestedAgentloom = filePath.match(/^(.+)\/\.agents\/commands\/([^/]+)(?:\.prompt)?\.(md|mdc)$/i);
|
|
466
541
|
if (nestedAgentloom) {
|
|
467
542
|
return {
|
|
468
543
|
subdir: nestedAgentloom[1],
|
|
469
|
-
commandName: nestedAgentloom[2],
|
|
544
|
+
commandName: stripPromptSuffix(nestedAgentloom[2]),
|
|
470
545
|
};
|
|
471
546
|
}
|
|
472
|
-
const directCommands = filePath.match(/^commands\/([^/]+)
|
|
547
|
+
const directCommands = filePath.match(/^commands\/([^/]+)(?:\.prompt)?\.(md|mdc)$/i);
|
|
473
548
|
if (directCommands) {
|
|
474
|
-
return { commandName: directCommands[1] };
|
|
549
|
+
return { commandName: stripPromptSuffix(directCommands[1]) };
|
|
475
550
|
}
|
|
476
|
-
const nestedCommands = filePath.match(/^(.+)\/commands\/([^/]+)
|
|
551
|
+
const nestedCommands = filePath.match(/^(.+)\/commands\/([^/]+)(?:\.prompt)?\.(md|mdc)$/i);
|
|
477
552
|
if (nestedCommands) {
|
|
478
553
|
return {
|
|
479
554
|
subdir: nestedCommands[1],
|
|
480
|
-
commandName: nestedCommands[2],
|
|
555
|
+
commandName: stripPromptSuffix(nestedCommands[2]),
|
|
481
556
|
};
|
|
482
557
|
}
|
|
483
|
-
const directPrompts = filePath.match(/^prompts\/([^/]+)
|
|
558
|
+
const directPrompts = filePath.match(/^prompts\/([^/]+)(?:\.prompt)?\.(md|mdc)$/i);
|
|
484
559
|
if (directPrompts) {
|
|
485
|
-
return { commandName: directPrompts[1] };
|
|
560
|
+
return { commandName: stripPromptSuffix(directPrompts[1]) };
|
|
486
561
|
}
|
|
487
|
-
const nestedPrompts = filePath.match(/^(.+)\/prompts\/([^/]+)
|
|
562
|
+
const nestedPrompts = filePath.match(/^(.+)\/prompts\/([^/]+)(?:\.prompt)?\.(md|mdc)$/i);
|
|
488
563
|
if (nestedPrompts) {
|
|
489
564
|
return {
|
|
490
565
|
subdir: nestedPrompts[1],
|
|
491
|
-
commandName: nestedPrompts[2],
|
|
566
|
+
commandName: stripPromptSuffix(nestedPrompts[2]),
|
|
492
567
|
};
|
|
493
568
|
}
|
|
494
569
|
return null;
|
|
495
570
|
}
|
|
571
|
+
function stripPromptSuffix(commandName) {
|
|
572
|
+
return commandName.replace(/\.prompt$/i, "");
|
|
573
|
+
}
|
|
496
574
|
function parseSkillPath(filePath) {
|
|
497
575
|
const directAgentloom = filePath.match(/^\.agents\/skills\/([^/]+)\/SKILL\.md$/i);
|
|
498
576
|
if (directAgentloom) {
|
|
@@ -533,6 +611,39 @@ function parseSkillPath(filePath) {
|
|
|
533
611
|
}
|
|
534
612
|
return null;
|
|
535
613
|
}
|
|
614
|
+
function parseRulePath(filePath) {
|
|
615
|
+
const directCanonical = filePath.match(/^\.agents\/rules\/([^/]+)\.md$/i);
|
|
616
|
+
if (directCanonical) {
|
|
617
|
+
return {
|
|
618
|
+
ruleName: directCanonical[1],
|
|
619
|
+
installRuleSelector: directCanonical[1],
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
const nestedCanonical = filePath.match(/^(.+)\/\.agents\/rules\/([^/]+)\.md$/i);
|
|
623
|
+
if (nestedCanonical) {
|
|
624
|
+
return {
|
|
625
|
+
subdir: nestedCanonical[1],
|
|
626
|
+
ruleName: nestedCanonical[2],
|
|
627
|
+
installRuleSelector: nestedCanonical[2],
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
const directRules = filePath.match(/^rules\/([^/]+)\.md$/i);
|
|
631
|
+
if (directRules) {
|
|
632
|
+
return {
|
|
633
|
+
ruleName: directRules[1],
|
|
634
|
+
installRuleSelector: directRules[1],
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
const nestedRules = filePath.match(/^(.+)\/rules\/([^/]+)\.md$/i);
|
|
638
|
+
if (nestedRules) {
|
|
639
|
+
return {
|
|
640
|
+
subdir: nestedRules[1],
|
|
641
|
+
ruleName: nestedRules[2],
|
|
642
|
+
installRuleSelector: nestedRules[2],
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
536
647
|
function isMcpPath(filePath) {
|
|
537
648
|
return (/^mcp\.json$/i.test(filePath) ||
|
|
538
649
|
/^\.agents\/mcp\.json$/i.test(filePath) ||
|
|
@@ -701,6 +812,21 @@ function buildSkillInstallCommand(result) {
|
|
|
701
812
|
}
|
|
702
813
|
return `agentloom skill add ${repoArg} --skills ${quoteShellArg(selector)}`;
|
|
703
814
|
}
|
|
815
|
+
function buildRuleInstallCommand(result) {
|
|
816
|
+
const source = result.source?.trim() || result.repo;
|
|
817
|
+
const repoArg = quoteShellArg(source);
|
|
818
|
+
const selector = result.installRuleSelector?.trim();
|
|
819
|
+
if (result.subdir && result.subdir.trim()) {
|
|
820
|
+
if (!selector) {
|
|
821
|
+
return `agentloom rule add ${repoArg} --subdir ${quoteShellArg(result.subdir)}`;
|
|
822
|
+
}
|
|
823
|
+
return `agentloom rule add ${repoArg} --subdir ${quoteShellArg(result.subdir)} --rules ${quoteShellArg(selector)}`;
|
|
824
|
+
}
|
|
825
|
+
if (!selector) {
|
|
826
|
+
return `agentloom rule add ${repoArg}`;
|
|
827
|
+
}
|
|
828
|
+
return `agentloom rule add ${repoArg} --rules ${quoteShellArg(selector)}`;
|
|
829
|
+
}
|
|
704
830
|
function tokenizeQuery(query) {
|
|
705
831
|
return query
|
|
706
832
|
.toLowerCase()
|
|
@@ -749,6 +875,14 @@ function searchLocalMatches(query, target) {
|
|
|
749
875
|
lines.push(`mcp: ${name}`);
|
|
750
876
|
}
|
|
751
877
|
}
|
|
878
|
+
if ((target === "all" || target === "rule") && fsExists(paths.rulesDir)) {
|
|
879
|
+
const rules = parseRulesDir(paths.rulesDir);
|
|
880
|
+
for (const rule of rules) {
|
|
881
|
+
if (!matchesTokens(`${rule.name} ${rule.fileName}`, tokens))
|
|
882
|
+
continue;
|
|
883
|
+
lines.push(`rule: ${rule.name} (${rule.fileName})`);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
752
886
|
if ((target === "all" || target === "skill") && fsExists(paths.skillsDir)) {
|
|
753
887
|
const skills = parseSkillsDir(paths.skillsDir);
|
|
754
888
|
for (const skill of skills) {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { formatUsageError } from "../core/copy.js";
|
|
2
|
+
import { parseRulesDir } from "../core/rules.js";
|
|
3
|
+
import { runScopedAddCommand } from "./add.js";
|
|
4
|
+
import { runScopedDeleteCommand } from "./delete.js";
|
|
5
|
+
import { resolvePathsForCommand } from "./entity-utils.js";
|
|
6
|
+
import { runScopedFindCommand } from "./find.js";
|
|
7
|
+
import { runScopedSyncCommand } from "./sync.js";
|
|
8
|
+
import { runScopedUpdateCommand } from "./update.js";
|
|
9
|
+
export async function runRuleCommand(argv, cwd) {
|
|
10
|
+
const action = argv._[1];
|
|
11
|
+
if (argv.help || !action) {
|
|
12
|
+
console.log("Usage:\n agentloom rule <add|list|delete|find|update|sync> [options]");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (action !== "add" &&
|
|
16
|
+
action !== "list" &&
|
|
17
|
+
action !== "delete" &&
|
|
18
|
+
action !== "find" &&
|
|
19
|
+
action !== "update" &&
|
|
20
|
+
action !== "sync") {
|
|
21
|
+
throw new Error(formatUsageError({
|
|
22
|
+
issue: "Invalid rule command.",
|
|
23
|
+
usage: "agentloom rule <add|list|delete|find|update|sync> [options]",
|
|
24
|
+
example: "agentloom rule add farnoodma/agents",
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
if (action === "list") {
|
|
28
|
+
const paths = await resolvePathsForCommand(argv, cwd);
|
|
29
|
+
const rules = parseRulesDir(paths.rulesDir);
|
|
30
|
+
if (Boolean(argv.json)) {
|
|
31
|
+
console.log(JSON.stringify({
|
|
32
|
+
version: 1,
|
|
33
|
+
rules: rules.map((rule) => ({
|
|
34
|
+
id: rule.id,
|
|
35
|
+
name: rule.name,
|
|
36
|
+
fileName: rule.fileName,
|
|
37
|
+
})),
|
|
38
|
+
}, null, 2));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (rules.length === 0) {
|
|
42
|
+
console.log("No canonical rules configured.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const rule of rules) {
|
|
46
|
+
console.log(`${rule.name} (${rule.fileName})`);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (action === "add") {
|
|
51
|
+
await runScopedAddCommand({
|
|
52
|
+
argv,
|
|
53
|
+
cwd,
|
|
54
|
+
entity: "rule",
|
|
55
|
+
sourceIndex: 2,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (action === "delete") {
|
|
60
|
+
await runScopedDeleteCommand({
|
|
61
|
+
argv,
|
|
62
|
+
cwd,
|
|
63
|
+
entity: "rule",
|
|
64
|
+
sourceIndex: 2,
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (action === "find") {
|
|
69
|
+
await runScopedFindCommand(argv, "rule");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (action === "update") {
|
|
73
|
+
await runScopedUpdateCommand({
|
|
74
|
+
argv,
|
|
75
|
+
cwd,
|
|
76
|
+
entity: "rule",
|
|
77
|
+
sourceIndex: 2,
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await runScopedSyncCommand({
|
|
82
|
+
argv,
|
|
83
|
+
cwd,
|
|
84
|
+
target: "rule",
|
|
85
|
+
});
|
|
86
|
+
}
|
package/dist/commands/sync.js
CHANGED
|
@@ -74,10 +74,18 @@ function createDryRunCanonicalPaths(paths) {
|
|
|
74
74
|
const tempAgentsRoot = path.join(tempRoot, ".agents");
|
|
75
75
|
if (fs.existsSync(paths.agentsRoot) &&
|
|
76
76
|
fs.statSync(paths.agentsRoot).isDirectory()) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
try {
|
|
78
|
+
fs.cpSync(paths.agentsRoot, tempAgentsRoot, {
|
|
79
|
+
recursive: true,
|
|
80
|
+
force: true,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const code = error.code;
|
|
85
|
+
if (code !== "ENOENT") {
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
81
89
|
}
|
|
82
90
|
return {
|
|
83
91
|
paths: {
|
|
@@ -85,6 +93,7 @@ function createDryRunCanonicalPaths(paths) {
|
|
|
85
93
|
agentsRoot: tempAgentsRoot,
|
|
86
94
|
agentsDir: path.join(tempAgentsRoot, "agents"),
|
|
87
95
|
commandsDir: path.join(tempAgentsRoot, "commands"),
|
|
96
|
+
rulesDir: path.join(tempAgentsRoot, "rules"),
|
|
88
97
|
skillsDir: path.join(tempAgentsRoot, "skills"),
|
|
89
98
|
mcpPath: path.join(tempAgentsRoot, "mcp.json"),
|
|
90
99
|
lockPath: path.join(tempAgentsRoot, "agents.lock.json"),
|
package/dist/commands/update.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { parseProvidersFlag } from "../core/argv.js";
|
|
3
3
|
import { importSource, NonInteractiveConflictError } from "../core/importer.js";
|
|
4
4
|
import { normalizeCommandSelector, stripCommandFileExtension, } from "../core/commands.js";
|
|
5
|
+
import { normalizeRuleSelector, stripRuleFileExtension, } from "../core/rules.js";
|
|
5
6
|
import { readLockfile } from "../core/lockfile.js";
|
|
6
7
|
import { prepareSource, parseSourceSpec } from "../core/sources.js";
|
|
7
8
|
import { getUpdateHelpText } from "../core/copy.js";
|
|
@@ -79,6 +80,7 @@ async function runEntityAwareUpdate(options) {
|
|
|
79
80
|
if (!updatePlan.importAgents &&
|
|
80
81
|
!updatePlan.importCommands &&
|
|
81
82
|
!updatePlan.importMcp &&
|
|
83
|
+
!updatePlan.importRules &&
|
|
82
84
|
!updatePlan.importSkills) {
|
|
83
85
|
skipped += 1;
|
|
84
86
|
continue;
|
|
@@ -92,6 +94,7 @@ async function runEntityAwareUpdate(options) {
|
|
|
92
94
|
promptForAgentSelection: false,
|
|
93
95
|
promptForCommands: false,
|
|
94
96
|
promptForMcp: false,
|
|
97
|
+
promptForRules: false,
|
|
95
98
|
promptForSkills: false,
|
|
96
99
|
yes: Boolean(options.argv.yes),
|
|
97
100
|
nonInteractive,
|
|
@@ -100,6 +103,7 @@ async function runEntityAwareUpdate(options) {
|
|
|
100
103
|
requireAgents: updatePlan.importAgents,
|
|
101
104
|
importCommands: updatePlan.importCommands,
|
|
102
105
|
importMcp: updatePlan.importMcp,
|
|
106
|
+
importRules: updatePlan.importRules,
|
|
103
107
|
};
|
|
104
108
|
if (updatePlan.importSkills) {
|
|
105
109
|
importOptions.importSkills = true;
|
|
@@ -120,6 +124,12 @@ async function runEntityAwareUpdate(options) {
|
|
|
120
124
|
if (updatePlan.mcpSelectors) {
|
|
121
125
|
importOptions.mcpSelectors = updatePlan.mcpSelectors;
|
|
122
126
|
}
|
|
127
|
+
if (updatePlan.ruleSelectors) {
|
|
128
|
+
importOptions.ruleSelectors = updatePlan.ruleSelectors;
|
|
129
|
+
}
|
|
130
|
+
if (updatePlan.ruleRenameMap) {
|
|
131
|
+
importOptions.ruleRenameMap = updatePlan.ruleRenameMap;
|
|
132
|
+
}
|
|
123
133
|
if (updatePlan.skillSelectors) {
|
|
124
134
|
importOptions.skillSelectors = updatePlan.skillSelectors;
|
|
125
135
|
}
|
|
@@ -152,19 +162,24 @@ function buildEntryUpdatePlan(entry, target) {
|
|
|
152
162
|
const includeAgents = shouldUpdateEntity(entry, "agent", target);
|
|
153
163
|
const includeCommands = shouldUpdateEntity(entry, "command", target);
|
|
154
164
|
const includeMcp = shouldUpdateEntity(entry, "mcp", target);
|
|
165
|
+
const includeRules = shouldUpdateEntity(entry, "rule", target);
|
|
155
166
|
const includeSkills = shouldUpdateEntity(entry, "skill", target);
|
|
156
167
|
const commandOptions = getUpdateCommandOptions(entry, includeCommands);
|
|
157
168
|
const mcpOptions = getUpdateMcpOptions(entry, includeMcp);
|
|
169
|
+
const ruleOptions = getUpdateRuleOptions(entry, includeRules);
|
|
158
170
|
const skillOptions = getUpdateSkillOptions(entry, includeSkills);
|
|
159
171
|
return {
|
|
160
172
|
importAgents: includeAgents,
|
|
161
173
|
importCommands: commandOptions.importCommands,
|
|
162
174
|
importMcp: mcpOptions.importMcp,
|
|
175
|
+
importRules: ruleOptions.importRules,
|
|
163
176
|
importSkills: skillOptions.importSkills,
|
|
164
177
|
requestedAgents: includeAgents ? entry.requestedAgents : undefined,
|
|
165
178
|
commandSelectors: commandOptions.commandSelectors,
|
|
166
179
|
commandRenameMap: commandOptions.commandRenameMap,
|
|
167
180
|
mcpSelectors: mcpOptions.mcpSelectors,
|
|
181
|
+
ruleSelectors: ruleOptions.ruleSelectors,
|
|
182
|
+
ruleRenameMap: ruleOptions.ruleRenameMap,
|
|
168
183
|
skillSelectors: skillOptions.skillSelectors,
|
|
169
184
|
skillsProviders: skillOptions.skillsProviders,
|
|
170
185
|
skillRenameMap: skillOptions.skillRenameMap,
|
|
@@ -204,7 +219,7 @@ function shouldUpdateEntity(entry, entity, target) {
|
|
|
204
219
|
if (target !== "all" && target !== entity)
|
|
205
220
|
return false;
|
|
206
221
|
if (target === "all" && !entry.trackedEntities) {
|
|
207
|
-
if (entity === "skill") {
|
|
222
|
+
if (entity === "skill" || entity === "rule") {
|
|
208
223
|
return tracksEntity(entry, entity);
|
|
209
224
|
}
|
|
210
225
|
return true;
|
|
@@ -221,6 +236,9 @@ function tracksEntity(entry, entity) {
|
|
|
221
236
|
const importedMcpServers = Array.isArray(entry.importedMcpServers)
|
|
222
237
|
? entry.importedMcpServers
|
|
223
238
|
: [];
|
|
239
|
+
const importedRules = Array.isArray(entry.importedRules)
|
|
240
|
+
? entry.importedRules
|
|
241
|
+
: [];
|
|
224
242
|
const importedSkills = Array.isArray(entry.importedSkills)
|
|
225
243
|
? entry.importedSkills
|
|
226
244
|
: [];
|
|
@@ -237,6 +255,11 @@ function tracksEntity(entry, entity) {
|
|
|
237
255
|
if (entity === "mcp") {
|
|
238
256
|
return (importedMcpServers.length > 0 || Boolean(entry.selectedSourceMcpServers));
|
|
239
257
|
}
|
|
258
|
+
if (entity === "rule") {
|
|
259
|
+
return (importedRules.length > 0 ||
|
|
260
|
+
Boolean(entry.selectedSourceRules) ||
|
|
261
|
+
Boolean(entry.ruleRenameMap));
|
|
262
|
+
}
|
|
240
263
|
return (importedSkills.length > 0 ||
|
|
241
264
|
Boolean(entry.selectedSourceSkills) ||
|
|
242
265
|
Boolean(entry.skillsProviders) ||
|
|
@@ -272,6 +295,26 @@ function getUpdateMcpOptions(entry, includeMcp) {
|
|
|
272
295
|
mcpSelectors: entry.selectedSourceMcpServers,
|
|
273
296
|
};
|
|
274
297
|
}
|
|
298
|
+
function getUpdateRuleOptions(entry, includeRules) {
|
|
299
|
+
if (!includeRules) {
|
|
300
|
+
return { importRules: false };
|
|
301
|
+
}
|
|
302
|
+
const ruleSelectors = getUpdateRuleSelectors(entry);
|
|
303
|
+
if (ruleSelectors && ruleSelectors.length === 0) {
|
|
304
|
+
return { importRules: false };
|
|
305
|
+
}
|
|
306
|
+
if (!ruleSelectors) {
|
|
307
|
+
return {
|
|
308
|
+
importRules: true,
|
|
309
|
+
ruleRenameMap: entry.ruleRenameMap,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
importRules: true,
|
|
314
|
+
ruleSelectors,
|
|
315
|
+
ruleRenameMap: getUpdateRuleRenameMap(entry, ruleSelectors),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
275
318
|
function getUpdateSkillOptions(entry, includeSkills) {
|
|
276
319
|
if (!includeSkills) {
|
|
277
320
|
return { importSkills: false };
|
|
@@ -294,7 +337,11 @@ function getUpdateCommandSelectors(entry) {
|
|
|
294
337
|
return undefined;
|
|
295
338
|
}
|
|
296
339
|
function getUpdateCommandRenameMap(entry, commandSelectors) {
|
|
297
|
-
const renameMapFromLock = filterRenameMapBySelectors(
|
|
340
|
+
const renameMapFromLock = filterRenameMapBySelectors({
|
|
341
|
+
renameMap: entry.commandRenameMap,
|
|
342
|
+
selectors: commandSelectors,
|
|
343
|
+
normalizeSelector: normalizeCommandSelector,
|
|
344
|
+
});
|
|
298
345
|
if (renameMapFromLock) {
|
|
299
346
|
return renameMapFromLock;
|
|
300
347
|
}
|
|
@@ -319,12 +366,48 @@ function inferUpdateCommandRename(entry, commandSelectors) {
|
|
|
319
366
|
}
|
|
320
367
|
return importedName;
|
|
321
368
|
}
|
|
322
|
-
function
|
|
323
|
-
if (
|
|
369
|
+
function getUpdateRuleSelectors(entry) {
|
|
370
|
+
if (entry.selectedSourceRules !== undefined) {
|
|
371
|
+
return entry.selectedSourceRules;
|
|
372
|
+
}
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
function getUpdateRuleRenameMap(entry, ruleSelectors) {
|
|
376
|
+
const renameMapFromLock = filterRenameMapBySelectors({
|
|
377
|
+
renameMap: entry.ruleRenameMap,
|
|
378
|
+
selectors: ruleSelectors,
|
|
379
|
+
normalizeSelector: normalizeRuleSelector,
|
|
380
|
+
});
|
|
381
|
+
if (renameMapFromLock) {
|
|
382
|
+
return renameMapFromLock;
|
|
383
|
+
}
|
|
384
|
+
const inferredRename = inferUpdateRuleRename(entry, ruleSelectors);
|
|
385
|
+
if (!inferredRename)
|
|
386
|
+
return undefined;
|
|
387
|
+
return {
|
|
388
|
+
[ruleSelectors[0]]: inferredRename,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function inferUpdateRuleRename(entry, ruleSelectors) {
|
|
392
|
+
if (!ruleSelectors || ruleSelectors.length !== 1) {
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
if (entry.importedRules.length !== 1) {
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
const sourceName = stripRuleFileExtension(ruleSelectors[0]);
|
|
399
|
+
const importedName = stripRuleFileExtension(path.basename(entry.importedRules[0]));
|
|
400
|
+
if (!sourceName || !importedName || sourceName === importedName) {
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
return importedName;
|
|
404
|
+
}
|
|
405
|
+
function filterRenameMapBySelectors(options) {
|
|
406
|
+
if (!options.renameMap)
|
|
324
407
|
return undefined;
|
|
325
|
-
const selectorSet = new Set(selectors.map(
|
|
326
|
-
const filteredEntries = Object.entries(renameMap)
|
|
327
|
-
.filter(([sourceSelector]) => selectorSet.has(
|
|
408
|
+
const selectorSet = new Set(options.selectors.map(options.normalizeSelector));
|
|
409
|
+
const filteredEntries = Object.entries(options.renameMap)
|
|
410
|
+
.filter(([sourceSelector]) => selectorSet.has(options.normalizeSelector(sourceSelector)))
|
|
328
411
|
.map(([sourceSelector, importedFileName]) => [
|
|
329
412
|
sourceSelector,
|
|
330
413
|
path.basename(importedFileName),
|
package/dist/core/agents.js
CHANGED
|
@@ -46,7 +46,7 @@ export function parseAgentMarkdown(raw, sourcePath, fileName = path.basename(sou
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
export function buildAgentMarkdown(frontmatter, body) {
|
|
49
|
-
const fm = YAML.stringify(frontmatter).trimEnd();
|
|
49
|
+
const fm = YAML.stringify(frontmatter, { lineWidth: 0 }).trimEnd();
|
|
50
50
|
const normalizedBody = body.trimStart();
|
|
51
51
|
return `---\n${fm}\n---\n\n${normalizedBody}${normalizedBody.endsWith("\n") ? "" : "\n"}`;
|
|
52
52
|
}
|
package/dist/core/argv.js
CHANGED
package/dist/core/commands.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
import type { Provider } from "../types.js";
|
|
1
2
|
export interface CanonicalCommandFile {
|
|
2
3
|
fileName: string;
|
|
3
4
|
sourcePath: string;
|
|
4
5
|
content: string;
|
|
6
|
+
body: string;
|
|
7
|
+
frontmatter?: Record<string, unknown>;
|
|
5
8
|
}
|
|
6
9
|
export declare function parseCommandsDir(commandsDir: string): CanonicalCommandFile[];
|
|
10
|
+
export declare function parseCommandContent(content: string): {
|
|
11
|
+
body: string;
|
|
12
|
+
frontmatter?: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export declare function getCommandProviderConfig(command: CanonicalCommandFile, provider: Provider): Record<string, unknown> | null;
|
|
15
|
+
export declare function isCommandProviderEnabled(command: CanonicalCommandFile, provider: Provider): boolean;
|
|
16
|
+
export declare function renderCommandForProvider(command: CanonicalCommandFile, provider: Provider): string | null;
|
|
17
|
+
export declare function normalizeCommandArgumentsForProvider(body: string, provider: Provider): string;
|
|
18
|
+
export declare function normalizeCommandArgumentsForCanonical(body: string, provider: Provider): string;
|
|
7
19
|
export declare function stripCommandFileExtension(fileName: string): string;
|
|
8
20
|
export declare function commandFileMatchesSelector(fileName: string, selector: string): boolean;
|
|
9
21
|
export declare function normalizeCommandSelector(selector: string): string;
|