agentloom 0.1.5 → 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.
@@ -1,16 +1,116 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { listMarkdownFiles, slugify } from "./fs.js";
3
+ import TOML from "@iarna/toml";
4
+ import matter from "gray-matter";
5
+ import YAML from "yaml";
6
+ import { isObject, listMarkdownFiles, slugify } from "./fs.js";
7
+ import { ALL_PROVIDERS } from "../types.js";
4
8
  export function parseCommandsDir(commandsDir) {
5
9
  if (!fs.existsSync(commandsDir))
6
10
  return [];
7
11
  return listMarkdownFiles(commandsDir)
8
12
  .sort((a, b) => a.localeCompare(b))
9
- .map((sourcePath) => ({
10
- fileName: path.basename(sourcePath),
11
- sourcePath,
12
- content: fs.readFileSync(sourcePath, "utf8"),
13
- }));
13
+ .map((sourcePath) => {
14
+ const content = fs.readFileSync(sourcePath, "utf8");
15
+ const fileName = path.basename(sourcePath);
16
+ const parsed = parseCommandContent(content);
17
+ return {
18
+ fileName,
19
+ sourcePath,
20
+ content,
21
+ body: parsed.body,
22
+ frontmatter: parsed.frontmatter,
23
+ };
24
+ });
25
+ }
26
+ export function parseCommandContent(content) {
27
+ const parsed = matter(content);
28
+ if (isObject(parsed.data) &&
29
+ Object.keys(parsed.data).length > 0) {
30
+ return {
31
+ body: parsed.content.trimStart(),
32
+ frontmatter: parsed.data,
33
+ };
34
+ }
35
+ return {
36
+ body: content,
37
+ };
38
+ }
39
+ export function getCommandProviderConfig(command, provider) {
40
+ if (!command.frontmatter)
41
+ return {};
42
+ const value = command.frontmatter[provider];
43
+ if (value === false)
44
+ return null;
45
+ if (isObject(value))
46
+ return value;
47
+ return {};
48
+ }
49
+ export function isCommandProviderEnabled(command, provider) {
50
+ return getCommandProviderConfig(command, provider) !== null;
51
+ }
52
+ export function renderCommandForProvider(command, provider) {
53
+ if (!isCommandProviderEnabled(command, provider)) {
54
+ return null;
55
+ }
56
+ const providerConfig = getCommandProviderConfig(command, provider);
57
+ if (providerConfig === null) {
58
+ return null;
59
+ }
60
+ const body = command.frontmatter ? command.body : command.content;
61
+ const normalizedBody = normalizeCommandArgumentsForProvider(body, provider);
62
+ const frontmatter = buildProviderCommandFrontmatter(command, providerConfig);
63
+ if (provider === "gemini") {
64
+ return buildGeminiCommandToml(frontmatter, normalizedBody);
65
+ }
66
+ if (Object.keys(frontmatter).length === 0) {
67
+ return normalizedBody;
68
+ }
69
+ const fm = YAML.stringify(frontmatter, { lineWidth: 0 }).trimEnd();
70
+ return `---\n${fm}\n---\n\n${normalizedBody.trimStart()}${normalizedBody.endsWith("\n") ? "" : "\n"}`;
71
+ }
72
+ function buildGeminiCommandToml(frontmatter, body) {
73
+ const payload = {
74
+ ...frontmatter,
75
+ prompt: body.endsWith("\n") ? body : `${body}\n`,
76
+ };
77
+ return TOML.stringify(payload);
78
+ }
79
+ function buildProviderCommandFrontmatter(command, providerConfig) {
80
+ return {
81
+ ...getSharedCommandMetadata(command.frontmatter),
82
+ ...providerConfig,
83
+ };
84
+ }
85
+ function getSharedCommandMetadata(frontmatter) {
86
+ if (!frontmatter)
87
+ return {};
88
+ const shared = {};
89
+ for (const [key, value] of Object.entries(frontmatter)) {
90
+ if (ALL_PROVIDERS.includes(key))
91
+ continue;
92
+ shared[key] = value;
93
+ }
94
+ return shared;
95
+ }
96
+ const COMMAND_ARGUMENT_PLACEHOLDER_BY_PROVIDER = {
97
+ copilot: "${input:args}",
98
+ gemini: "{{args}}",
99
+ };
100
+ export function normalizeCommandArgumentsForProvider(body, provider) {
101
+ const providerPlaceholder = COMMAND_ARGUMENT_PLACEHOLDER_BY_PROVIDER[provider];
102
+ if (!providerPlaceholder || providerPlaceholder === "$ARGUMENTS") {
103
+ return body;
104
+ }
105
+ return body.replace(/\$ARGUMENTS\b/g, providerPlaceholder);
106
+ }
107
+ export function normalizeCommandArgumentsForCanonical(body, provider) {
108
+ const providerPlaceholder = COMMAND_ARGUMENT_PLACEHOLDER_BY_PROVIDER[provider];
109
+ if (!providerPlaceholder || providerPlaceholder === "$ARGUMENTS") {
110
+ return body;
111
+ }
112
+ const escapedPlaceholder = providerPlaceholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
113
+ return body.replace(new RegExp(escapedPlaceholder, "g"), "$ARGUMENTS");
14
114
  }
15
115
  export function stripCommandFileExtension(fileName) {
16
116
  const lower = fileName.toLowerCase();
package/dist/core/copy.js CHANGED
@@ -8,7 +8,7 @@ Usage:
8
8
  agentloom <entity> <verb> [options]
9
9
 
10
10
  Aggregate commands:
11
- add <source> Import agents/commands/mcp/skills from a source
11
+ add <source> Import agents/commands/mcp/rules/skills from a source
12
12
  init Bootstrap canonical files, migrate providers, then sync
13
13
  find <query> Search remote + local entities
14
14
  update [source] Refresh lockfile-managed imports
@@ -20,6 +20,7 @@ Entity commands:
20
20
  agent <add|list|delete|find|update|sync>
21
21
  command <add|list|delete|find|update|sync>
22
22
  mcp <add|list|delete|find|update|sync>
23
+ rule <add|list|delete|find|update|sync>
23
24
  skill <add|list|delete|find|update|sync>
24
25
 
25
26
  MCP manual server commands:
@@ -35,6 +36,8 @@ Common options:
35
36
  --agents <csv> Agent selectors for add/delete
36
37
  --commands <csv> Command selectors for add/delete
37
38
  --mcps <csv> MCP selectors for add/delete
39
+ --rule <csv> Alias for --rules
40
+ --rules <csv> Rule selectors for add/delete
38
41
  --skills <csv> Skill selectors for add/delete
39
42
  --selection-mode <mode> Add mode: all (default) or custom
40
43
  --source <value> Explicit source filter for update/delete
@@ -46,6 +49,7 @@ Examples:
46
49
  agentloom agent add farnoodma/agents --agents issue-creator
47
50
  agentloom command add farnoodma/agents --commands review
48
51
  agentloom mcp add farnoodma/agents --mcps browser
52
+ agentloom rule add farnoodma/agents --rules always-test
49
53
  agentloom skill add farnoodma/agents --skills pr-review
50
54
  agentloom update
51
55
  agentloom command update farnoodma/agents
@@ -58,7 +62,7 @@ export function getFindHelpText() {
58
62
 
59
63
  Usage:
60
64
  agentloom find <query>
61
- agentloom <agent|command|mcp|skill> find <query>
65
+ agentloom <agent|command|mcp|rule|skill> find <query>
62
66
 
63
67
  Examples:
64
68
  agentloom find reviewer
@@ -70,7 +74,8 @@ export function getAddHelpText() {
70
74
 
71
75
  Source discovery:
72
76
  agents: .agents/agents -> agents
73
- commands: .agents/commands -> commands -> prompts
77
+ commands: .agents/commands -> commands -> prompts -> (.github/prompts + .gemini/commands fallback)
78
+ rules: .agents/rules -> rules
74
79
  skills: .agents/skills -> skills -> root SKILL.md
75
80
 
76
81
  Usage:
@@ -82,6 +87,8 @@ Options:
82
87
  --agents <name> Import selected agents (repeatable/csv)
83
88
  --commands <name> Import selected commands (repeatable/csv)
84
89
  --mcps <name> Import selected MCP servers (repeatable/csv)
90
+ --rule <name> Alias for --rules
91
+ --rules <name> Import selected rules (repeatable/csv)
85
92
  --skills <name> Import selected skills (repeatable/csv)
86
93
  --selection-mode <mode> all|sync-all (include future items) or custom (pin selection)
87
94
  --rename <name> Rename imported item for single-item add flows
@@ -100,7 +107,7 @@ export function getUpdateHelpText() {
100
107
 
101
108
  Usage:
102
109
  agentloom update [source] [options]
103
- agentloom <agent|command|mcp|skill> update [source] [options]
110
+ agentloom <agent|command|mcp|rule|skill> update [source] [options]
104
111
 
105
112
  Options:
106
113
  --source <value> Explicit source filter
@@ -131,7 +138,7 @@ export function getSyncHelpText() {
131
138
 
132
139
  Usage:
133
140
  agentloom sync [options]
134
- agentloom <agent|command|mcp|skill> sync [options]
141
+ agentloom <agent|command|mcp|rule|skill> sync [options]
135
142
 
136
143
  Options:
137
144
  --local | --global Choose canonical scope (interactive prompts when omitted)
@@ -20,6 +20,11 @@ export interface ImportOptions {
20
20
  requireMcp?: boolean;
21
21
  mcpSelectors?: string[];
22
22
  promptForMcp?: boolean;
23
+ importRules?: boolean;
24
+ requireRules?: boolean;
25
+ ruleSelectors?: string[];
26
+ promptForRules?: boolean;
27
+ ruleRenameMap?: Record<string, string>;
23
28
  importSkills?: boolean;
24
29
  requireSkills?: boolean;
25
30
  skillSelectors?: string[];
@@ -38,7 +43,12 @@ export interface ImportSummary {
38
43
  importedAgents: string[];
39
44
  importedCommands: string[];
40
45
  importedMcpServers: string[];
46
+ importedRules: string[];
41
47
  importedSkills: string[];
48
+ telemetryRules?: Array<{
49
+ name: string;
50
+ filePath: string;
51
+ }>;
42
52
  telemetrySkills?: Array<{
43
53
  name: string;
44
54
  filePath: string;