@ethlete/agent-rules 0.1.0-next.0

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +106 -0
  3. package/content/defaults.json +10 -0
  4. package/content/rules/comments.md +31 -0
  5. package/content/rules/lint-and-format.md +26 -0
  6. package/content/rules/reactive-state.md +15 -0
  7. package/content/rules/styling.md +35 -0
  8. package/content/skills/angular-patterns/SKILL.md +60 -0
  9. package/content/skills/git-commit/SKILL.md +29 -0
  10. package/content/skills/handoff/SKILL.md +98 -0
  11. package/content/skills/query/SKILL.md +114 -0
  12. package/content/skills/rxjs-signals/SKILL.md +65 -0
  13. package/content/skills/sdk-docs/SKILL.md +70 -0
  14. package/content/skills/story-styling/SKILL.md +83 -0
  15. package/content/skills/styleguide/SKILL.md +66 -0
  16. package/content/skills/styleguide/STYLEGUIDE.md +520 -0
  17. package/content/skills/theming/SKILL.md +110 -0
  18. package/content/skills/verify-in-storybook/SKILL.md +78 -0
  19. package/content/skills/verify-in-storybook/verify-template.mjs +42 -0
  20. package/package.json +15 -0
  21. package/src/index.d.ts +2 -0
  22. package/src/index.js +79 -0
  23. package/src/index.js.map +1 -0
  24. package/src/lib/config.d.ts +21 -0
  25. package/src/lib/config.js +54 -0
  26. package/src/lib/config.js.map +1 -0
  27. package/src/lib/filter.d.ts +16 -0
  28. package/src/lib/filter.js +49 -0
  29. package/src/lib/filter.js.map +1 -0
  30. package/src/lib/frontmatter.d.ts +23 -0
  31. package/src/lib/frontmatter.js +117 -0
  32. package/src/lib/frontmatter.js.map +1 -0
  33. package/src/lib/index.d.ts +9 -0
  34. package/src/lib/index.js +13 -0
  35. package/src/lib/index.js.map +1 -0
  36. package/src/lib/load-content.d.ts +19 -0
  37. package/src/lib/load-content.js +79 -0
  38. package/src/lib/load-content.js.map +1 -0
  39. package/src/lib/owned-paths.d.ts +7 -0
  40. package/src/lib/owned-paths.js +47 -0
  41. package/src/lib/owned-paths.js.map +1 -0
  42. package/src/lib/plan.d.ts +20 -0
  43. package/src/lib/plan.js +57 -0
  44. package/src/lib/plan.js.map +1 -0
  45. package/src/lib/render.d.ts +39 -0
  46. package/src/lib/render.js +73 -0
  47. package/src/lib/render.js.map +1 -0
  48. package/src/lib/sync.d.ts +9 -0
  49. package/src/lib/sync.js +90 -0
  50. package/src/lib/sync.js.map +1 -0
  51. package/src/lib/targets/claude.d.ts +7 -0
  52. package/src/lib/targets/claude.js +46 -0
  53. package/src/lib/targets/claude.js.map +1 -0
  54. package/src/lib/targets/codex.d.ts +11 -0
  55. package/src/lib/targets/codex.js +26 -0
  56. package/src/lib/targets/codex.js.map +1 -0
  57. package/src/lib/targets/copilot.d.ts +11 -0
  58. package/src/lib/targets/copilot.js +43 -0
  59. package/src/lib/targets/copilot.js.map +1 -0
  60. package/src/lib/targets/cursor.d.ts +7 -0
  61. package/src/lib/targets/cursor.js +35 -0
  62. package/src/lib/targets/cursor.js.map +1 -0
  63. package/src/lib/targets/neutral.d.ts +8 -0
  64. package/src/lib/targets/neutral.js +29 -0
  65. package/src/lib/targets/neutral.js.map +1 -0
  66. package/src/lib/targets/shared.d.ts +60 -0
  67. package/src/lib/targets/shared.js +50 -0
  68. package/src/lib/targets/shared.js.map +1 -0
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadDefaultVars = exports.loadContent = exports.resolveContentRoot = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const frontmatter_1 = require("./frontmatter");
7
+ /**
8
+ * The compiled CLI lives at `<pkg>/src/lib/`, the shipped markdown at `<pkg>/content/`.
9
+ * The same relative hop works from the TypeScript sources, so dev and published runs agree.
10
+ */
11
+ const resolveContentRoot = () => (0, path_1.join)(__dirname, '..', '..', 'content');
12
+ exports.resolveContentRoot = resolveContentRoot;
13
+ const readMarkdown = (path) => (0, fs_1.readFileSync)(path, 'utf8');
14
+ const loadRules = (contentRoot) => {
15
+ const dir = (0, path_1.join)(contentRoot, 'rules');
16
+ if (!(0, fs_1.existsSync)(dir))
17
+ return [];
18
+ return (0, fs_1.readdirSync)(dir)
19
+ .filter((entry) => entry.endsWith('.md'))
20
+ .sort()
21
+ .map((entry) => {
22
+ const sourcePath = (0, path_1.join)(dir, entry);
23
+ const { frontmatter, body } = (0, frontmatter_1.parseFrontmatter)(readMarkdown(sourcePath), `rules/${entry}`);
24
+ if (frontmatter.kind !== 'rule') {
25
+ throw new Error(`rules/${entry}: kind must be "rule" for a file under content/rules.`);
26
+ }
27
+ return { frontmatter, body, sourcePath, resources: [] };
28
+ });
29
+ };
30
+ const loadSkills = (contentRoot) => {
31
+ const dir = (0, path_1.join)(contentRoot, 'skills');
32
+ if (!(0, fs_1.existsSync)(dir))
33
+ return [];
34
+ return (0, fs_1.readdirSync)(dir)
35
+ .filter((entry) => (0, fs_1.statSync)((0, path_1.join)(dir, entry)).isDirectory())
36
+ .sort()
37
+ .map((entry) => {
38
+ const skillDir = (0, path_1.join)(dir, entry);
39
+ const sourcePath = (0, path_1.join)(skillDir, 'SKILL.md');
40
+ if (!(0, fs_1.existsSync)(sourcePath)) {
41
+ throw new Error(`skills/${entry}: directory has no SKILL.md.`);
42
+ }
43
+ const { frontmatter, body } = (0, frontmatter_1.parseFrontmatter)(readMarkdown(sourcePath), `skills/${entry}/SKILL.md`);
44
+ if (frontmatter.kind !== 'skill') {
45
+ throw new Error(`skills/${entry}/SKILL.md: kind must be "skill" for a file under content/skills.`);
46
+ }
47
+ if (frontmatter.name !== entry) {
48
+ throw new Error(`skills/${entry}/SKILL.md: frontmatter name "${frontmatter.name}" must match the folder.`);
49
+ }
50
+ const resources = (0, fs_1.readdirSync)(skillDir)
51
+ .filter((file) => file !== 'SKILL.md' && (0, fs_1.statSync)((0, path_1.join)(skillDir, file)).isFile())
52
+ .sort()
53
+ .map((file) => ({ fileName: file, absolutePath: (0, path_1.join)(skillDir, file) }));
54
+ return { frontmatter, body, sourcePath, resources };
55
+ });
56
+ };
57
+ const loadContent = (contentRoot = (0, exports.resolveContentRoot)()) => {
58
+ if (!(0, fs_1.existsSync)(contentRoot)) {
59
+ throw new Error(`Content directory not found at ${contentRoot}. The package build may be missing its assets.`);
60
+ }
61
+ const items = [...loadRules(contentRoot), ...loadSkills(contentRoot)];
62
+ const seen = new Set();
63
+ for (const item of items) {
64
+ if (seen.has(item.frontmatter.name)) {
65
+ throw new Error(`Duplicate content name "${item.frontmatter.name}" — names must be unique across rules and skills.`);
66
+ }
67
+ seen.add(item.frontmatter.name);
68
+ }
69
+ return items;
70
+ };
71
+ exports.loadContent = loadContent;
72
+ const loadDefaultVars = (contentRoot = (0, exports.resolveContentRoot)()) => {
73
+ const path = (0, path_1.join)(contentRoot, 'defaults.json');
74
+ if (!(0, fs_1.existsSync)(path))
75
+ return {};
76
+ return JSON.parse((0, fs_1.readFileSync)(path, 'utf8'));
77
+ };
78
+ exports.loadDefaultVars = loadDefaultVars;
79
+ //# sourceMappingURL=load-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-content.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/load-content.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Everything the generator owns lives under an `ethlete` directory or an `ethlete-` prefix, so a
3
+ * file that disappears from the package can be pruned without ever touching a hand-written one.
4
+ * Marker-block files (`AGENTS.md`, `copilot-instructions.md`) are deliberately absent: those are
5
+ * shared with the repo and are only ever edited between the markers.
6
+ */
7
+ export declare const collectOwnedPaths: (root: string) => string[];
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectOwnedPaths = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const OWNED_DIRECTORIES = ['.agents/ethlete', '.claude/rules/ethlete'];
7
+ const OWNED_PARENTS = [
8
+ { dir: '.claude/skills', match: (entry) => entry.startsWith('ethlete-') },
9
+ { dir: '.cursor/rules', match: (entry) => entry.startsWith('ethlete-') && entry.endsWith('.mdc') },
10
+ {
11
+ dir: '.github/instructions',
12
+ match: (entry) => entry.startsWith('ethlete-') && entry.endsWith('.instructions.md'),
13
+ },
14
+ ];
15
+ const walk = (root, absoluteDir) => {
16
+ if (!(0, fs_1.existsSync)(absoluteDir))
17
+ return [];
18
+ return (0, fs_1.readdirSync)(absoluteDir).flatMap((entry) => {
19
+ const absolute = (0, path_1.join)(absoluteDir, entry);
20
+ if ((0, fs_1.statSync)(absolute).isDirectory())
21
+ return walk(root, absolute);
22
+ return [(0, path_1.relative)(root, absolute).split(path_1.sep).join('/')];
23
+ });
24
+ };
25
+ /**
26
+ * Everything the generator owns lives under an `ethlete` directory or an `ethlete-` prefix, so a
27
+ * file that disappears from the package can be pruned without ever touching a hand-written one.
28
+ * Marker-block files (`AGENTS.md`, `copilot-instructions.md`) are deliberately absent: those are
29
+ * shared with the repo and are only ever edited between the markers.
30
+ */
31
+ const collectOwnedPaths = (root) => {
32
+ const owned = OWNED_DIRECTORIES.flatMap((dir) => walk(root, (0, path_1.join)(root, dir)));
33
+ for (const parent of OWNED_PARENTS) {
34
+ const absoluteParent = (0, path_1.join)(root, parent.dir);
35
+ if (!(0, fs_1.existsSync)(absoluteParent))
36
+ continue;
37
+ for (const entry of (0, fs_1.readdirSync)(absoluteParent)) {
38
+ if (!parent.match(entry))
39
+ continue;
40
+ const absolute = (0, path_1.join)(absoluteParent, entry);
41
+ owned.push(...((0, fs_1.statSync)(absolute).isDirectory() ? walk(root, absolute) : [`${parent.dir}/${entry}`]));
42
+ }
43
+ }
44
+ return owned;
45
+ };
46
+ exports.collectOwnedPaths = collectOwnedPaths;
47
+ //# sourceMappingURL=owned-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-paths.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/owned-paths.ts"],"names":[],"mappings":";;;AAAA,2BAAuD;AACvD,+BAA2C;AAE3C,MAAM,iBAAiB,GAAG,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,CAAC;AACvE,MAAM,aAAa,GAAG;IACpB,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE;IACjF,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;IAC1G;QACE,GAAG,EAAE,sBAAsB;QAC3B,KAAK,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC;KAC7F;CACF,CAAC;AAEF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,WAAmB,EAAY,EAAE;IAC3D,IAAI,CAAC,IAAA,eAAU,EAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,OAAO,IAAA,gBAAW,EAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAChD,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAE1C,IAAI,IAAA,aAAQ,EAAC,QAAQ,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAElE,OAAO,CAAC,IAAA,eAAQ,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,UAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;;GAKG;AACI,MAAM,iBAAiB,GAAG,CAAC,IAAY,EAAE,EAAE;IAChD,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAA,WAAI,EAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAE9E,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACnC,MAAM,cAAc,GAAG,IAAA,WAAI,EAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QAE9C,IAAI,CAAC,IAAA,eAAU,EAAC,cAAc,CAAC;YAAE,SAAS;QAE1C,KAAK,MAAM,KAAK,IAAI,IAAA,gBAAW,EAAC,cAAc,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;gBAAE,SAAS;YAEnC,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,cAAc,EAAE,KAAK,CAAC,CAAC;YAE7C,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAA,aAAQ,EAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACxG,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAlBW,QAAA,iBAAiB,qBAkB5B"}
@@ -0,0 +1,20 @@
1
+ import { SyncConfig } from './config';
2
+ import { SkippedItem } from './filter';
3
+ import { EmittedFile } from './targets/shared';
4
+ export type SyncPlan = {
5
+ files: EmittedFile[];
6
+ skipped: SkippedItem[];
7
+ /** Cross-references whose target was filtered out of this repo; rendered as a bare name. */
8
+ danglingLinks: {
9
+ from: string;
10
+ to: string;
11
+ }[];
12
+ };
13
+ /**
14
+ * Resolves the full set of files a sync would write, without touching disk — `sync` writes the
15
+ * result and `check` diffs it.
16
+ */
17
+ export declare const buildPlan: (options: {
18
+ config: SyncConfig;
19
+ version: string;
20
+ }) => SyncPlan;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildPlan = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const filter_1 = require("./filter");
7
+ const load_content_1 = require("./load-content");
8
+ const claude_1 = require("./targets/claude");
9
+ const codex_1 = require("./targets/codex");
10
+ const copilot_1 = require("./targets/copilot");
11
+ const cursor_1 = require("./targets/cursor");
12
+ const neutral_1 = require("./targets/neutral");
13
+ const SKILL_LINK_PATTERN = /\{%\s*skill:([a-zA-Z0-9_.-]+)\s*%\}/g;
14
+ const findDanglingLinks = (items, emittedSkills) => items.flatMap((item) => [...item.body.matchAll(SKILL_LINK_PATTERN)]
15
+ .map((match) => match[1])
16
+ .filter((name) => !emittedSkills.has(name))
17
+ .map((name) => ({ from: item.frontmatter.name, to: name })));
18
+ const readExisting = (root, relativePath) => {
19
+ const path = (0, path_1.join)(root, relativePath);
20
+ return (0, fs_1.existsSync)(path) ? (0, fs_1.readFileSync)(path, 'utf8') : '';
21
+ };
22
+ /**
23
+ * Resolves the full set of files a sync would write, without touching disk — `sync` writes the
24
+ * result and `check` diffs it.
25
+ */
26
+ const buildPlan = (options) => {
27
+ const { config, version } = options;
28
+ const { kept, skipped } = (0, filter_1.filterContent)((0, load_content_1.loadContent)(), config);
29
+ const skills = kept.filter((item) => item.frontmatter.kind === 'skill');
30
+ const emittedSkills = new Set(skills.map((item) => item.frontmatter.name));
31
+ const context = {
32
+ rules: kept.filter((item) => item.frontmatter.kind === 'rule'),
33
+ skills,
34
+ emittedSkills,
35
+ vars: config.vars,
36
+ version,
37
+ };
38
+ const files = [];
39
+ // Codex, Cursor and Copilot all resolve resource files (and, where they have no on-demand
40
+ // mechanism, whole skills) out of the neutral tree.
41
+ if (config.targets.some((target) => target !== 'claude')) {
42
+ files.push(...(0, neutral_1.emitNeutral)(context));
43
+ }
44
+ if (config.targets.includes('claude'))
45
+ files.push(...(0, claude_1.emitClaude)(context));
46
+ if (config.targets.includes('cursor'))
47
+ files.push(...(0, cursor_1.emitCursor)(context));
48
+ if (config.targets.includes('codex')) {
49
+ files.push(...(0, codex_1.emitCodex)({ context, existing: readExisting(config.root, codex_1.CODEX_FILE) }));
50
+ }
51
+ if (config.targets.includes('copilot')) {
52
+ files.push(...(0, copilot_1.emitCopilot)({ context, existing: readExisting(config.root, copilot_1.COPILOT_FILE) }));
53
+ }
54
+ return { files, skipped, danglingLinks: findDanglingLinks(kept, emittedSkills) };
55
+ };
56
+ exports.buildPlan = buildPlan;
57
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { ContentItem } from './load-content';
2
+ /**
3
+ * Bodies reference sibling content through tokens rather than hardcoded paths, because the same
4
+ * skill is a namespaced skill under Claude, an `.mdc` rule under Cursor and a plain file path
5
+ * under Codex. Each target supplies its own resolver.
6
+ */
7
+ export type LinkResolver = {
8
+ skill: (name: string) => string;
9
+ resource: (options: {
10
+ skillName: string;
11
+ fileName: string;
12
+ }) => string;
13
+ };
14
+ export declare const START_MARKER = "<!-- ethlete:agent-rules:start -->";
15
+ export declare const END_MARKER = "<!-- ethlete:agent-rules:end -->";
16
+ export declare const buildBanner: (version: string) => string;
17
+ export declare const renderBody: (options: {
18
+ item: ContentItem;
19
+ vars: Record<string, string | string[]>;
20
+ links: LinkResolver;
21
+ }) => string;
22
+ /** Variable substitution only — for text that has no business linking to other content. */
23
+ export declare const substituteVars: (options: {
24
+ text: string;
25
+ vars: Record<string, string | string[]>;
26
+ origin: string;
27
+ }) => string;
28
+ export declare const renderDescription: (options: {
29
+ item: ContentItem;
30
+ vars: Record<string, string | string[]>;
31
+ }) => string;
32
+ /**
33
+ * Rewrites the block between the markers, leaving everything a repo wrote around it untouched.
34
+ * A file without markers gets the block appended.
35
+ */
36
+ export declare const replaceMarkedBlock: (options: {
37
+ existing: string;
38
+ block: string;
39
+ }) => string;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replaceMarkedBlock = exports.renderDescription = exports.substituteVars = exports.renderBody = exports.buildBanner = exports.END_MARKER = exports.START_MARKER = void 0;
4
+ /**
5
+ * `{% … %}`, not `{{ … }}`: content bodies routinely quote Angular templates, and an example
6
+ * containing `{{ count() }}` must not be mistaken for a template token.
7
+ */
8
+ const TOKEN_PATTERN = /\{%\s*([a-zA-Z0-9_:.-]+)\s*%\}/g;
9
+ exports.START_MARKER = '<!-- ethlete:agent-rules:start -->';
10
+ exports.END_MARKER = '<!-- ethlete:agent-rules:end -->';
11
+ const buildBanner = (version) => `<!-- Generated by @ethlete/agent-rules v${version} — DO NOT EDIT. Run \`npx ethlete-agents sync\`. -->`;
12
+ exports.buildBanner = buildBanner;
13
+ const renderValue = (value) => Array.isArray(value) ? value.map((entry) => `\`${entry}\``).join(', ') : value;
14
+ const renderBody = (options) => {
15
+ const { item, vars, links } = options;
16
+ const rendered = item.body.replace(TOKEN_PATTERN, (match, token) => {
17
+ const [namespace, argument] = token.split(':');
18
+ if (namespace === 'skill' && argument)
19
+ return links.skill(argument);
20
+ if (namespace === 'resource' && argument) {
21
+ return links.resource({ skillName: item.frontmatter.name, fileName: argument });
22
+ }
23
+ const value = vars[token];
24
+ if (value === undefined) {
25
+ throw new Error(`${item.frontmatter.name}: no value for template token "${match}".`);
26
+ }
27
+ return renderValue(value);
28
+ });
29
+ const leftover = /\{%[^%]*%\}/.exec(rendered);
30
+ if (leftover) {
31
+ throw new Error(`${item.frontmatter.name}: unresolved template token "${leftover[0]}".`);
32
+ }
33
+ return rendered;
34
+ };
35
+ exports.renderBody = renderBody;
36
+ /** Variable substitution only — for text that has no business linking to other content. */
37
+ const substituteVars = (options) => {
38
+ const { text, vars, origin } = options;
39
+ return text.replace(TOKEN_PATTERN, (match, token) => {
40
+ if (token.includes(':')) {
41
+ throw new Error(`${origin}: "${match}" — skill and resource links only work in a SKILL.md body.`);
42
+ }
43
+ const value = vars[token];
44
+ if (value === undefined) {
45
+ throw new Error(`${origin}: no value for template token "${match}".`);
46
+ }
47
+ return renderValue(value);
48
+ });
49
+ };
50
+ exports.substituteVars = substituteVars;
51
+ const renderDescription = (options) => (0, exports.substituteVars)({
52
+ text: options.item.frontmatter.description,
53
+ vars: options.vars,
54
+ origin: `${options.item.frontmatter.name} (description)`,
55
+ });
56
+ exports.renderDescription = renderDescription;
57
+ /**
58
+ * Rewrites the block between the markers, leaving everything a repo wrote around it untouched.
59
+ * A file without markers gets the block appended.
60
+ */
61
+ const replaceMarkedBlock = (options) => {
62
+ const { existing, block } = options;
63
+ const wrapped = `${exports.START_MARKER}\n${block}\n${exports.END_MARKER}`;
64
+ const start = existing.indexOf(exports.START_MARKER);
65
+ const end = existing.indexOf(exports.END_MARKER);
66
+ if (start === -1 || end === -1 || end < start) {
67
+ const separator = existing.trim().length > 0 ? `${existing.trimEnd()}\n\n` : '';
68
+ return `${separator}${wrapped}\n`;
69
+ }
70
+ return `${existing.slice(0, start)}${wrapped}${existing.slice(end + exports.END_MARKER.length)}`;
71
+ };
72
+ exports.replaceMarkedBlock = replaceMarkedBlock;
73
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { AgentTarget } from './config';
2
+ export type RunOptions = {
3
+ root: string;
4
+ version: string;
5
+ targets?: AgentTarget[];
6
+ dryRun?: boolean;
7
+ };
8
+ export declare const sync: (options: RunOptions) => number;
9
+ export declare const check: (options: RunOptions) => 1 | 0;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.check = exports.sync = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const config_1 = require("./config");
7
+ const owned_paths_1 = require("./owned-paths");
8
+ const plan_1 = require("./plan");
9
+ const readIfPresent = (absolute) => ((0, fs_1.existsSync)(absolute) ? (0, fs_1.readFileSync)(absolute, 'utf8') : null);
10
+ const diffPlan = (options) => {
11
+ const { root, files, owned } = options;
12
+ const planned = new Set(files.map((file) => file.path));
13
+ const changes = [];
14
+ for (const file of files) {
15
+ const current = readIfPresent((0, path_1.join)(root, file.path));
16
+ if (current === file.contents)
17
+ continue;
18
+ changes.push({ path: file.path, action: current === null ? 'write' : 'update' });
19
+ }
20
+ for (const path of owned) {
21
+ if (!planned.has(path))
22
+ changes.push({ path, action: 'delete' });
23
+ }
24
+ return changes;
25
+ };
26
+ /** Removing the last generated file from a skill folder must not leave the folder behind. */
27
+ const pruneEmptyDirs = (options) => {
28
+ let current = (0, path_1.dirname)(options.from);
29
+ while (current.startsWith(options.root) && (0, path_1.relative)(options.root, current) !== '') {
30
+ if (!(0, fs_1.existsSync)(current) || (0, fs_1.readdirSync)(current).length > 0)
31
+ return;
32
+ (0, fs_1.rmdirSync)(current);
33
+ current = (0, path_1.dirname)(current);
34
+ }
35
+ };
36
+ const describe = (change) => {
37
+ const label = { write: 'create', update: 'update', delete: 'remove' }[change.action];
38
+ return ` ${label} ${change.path}`;
39
+ };
40
+ const sync = (options) => {
41
+ const config = (0, config_1.loadConfig)({ root: options.root, targetOverride: options.targets });
42
+ const { files, skipped, danglingLinks } = (0, plan_1.buildPlan)({ config, version: options.version });
43
+ const changes = diffPlan({ root: options.root, files, owned: (0, owned_paths_1.collectOwnedPaths)(options.root) });
44
+ console.log(`Targets: ${config.targets.join(', ')}`);
45
+ for (const entry of skipped) {
46
+ console.log(` skip ${entry.name} — ${entry.reason}`);
47
+ }
48
+ for (const link of danglingLinks) {
49
+ console.log(` note ${link.from} references "${link.to}", which is not emitted here — rendered as plain text`);
50
+ }
51
+ if (changes.length === 0) {
52
+ console.log('Everything is already up to date.');
53
+ return 0;
54
+ }
55
+ changes.forEach((change) => console.log(describe(change)));
56
+ if (options.dryRun) {
57
+ console.log(`\n${changes.length} file(s) would change. Re-run without --dry-run to apply.`);
58
+ return 0;
59
+ }
60
+ for (const file of files) {
61
+ const absolute = (0, path_1.join)(options.root, file.path);
62
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(absolute), { recursive: true });
63
+ (0, fs_1.writeFileSync)(absolute, file.contents, 'utf8');
64
+ }
65
+ for (const change of changes) {
66
+ if (change.action !== 'delete')
67
+ continue;
68
+ const absolute = (0, path_1.join)(options.root, change.path);
69
+ (0, fs_1.rmSync)(absolute, { force: true });
70
+ pruneEmptyDirs({ root: options.root, from: absolute });
71
+ }
72
+ console.log(`\n${changes.length} file(s) written.`);
73
+ return 0;
74
+ };
75
+ exports.sync = sync;
76
+ const check = (options) => {
77
+ const config = (0, config_1.loadConfig)({ root: options.root, targetOverride: options.targets });
78
+ const { files } = (0, plan_1.buildPlan)({ config, version: options.version });
79
+ const changes = diffPlan({ root: options.root, files, owned: (0, owned_paths_1.collectOwnedPaths)(options.root) });
80
+ if (changes.length === 0) {
81
+ console.log('Agent rules are in sync.');
82
+ return 0;
83
+ }
84
+ console.error('Agent rules are out of sync with @ethlete/agent-rules:');
85
+ changes.forEach((change) => console.error(describe(change)));
86
+ console.error('\nRun `npx ethlete-agents sync` and commit the result.');
87
+ return 1;
88
+ };
89
+ exports.check = check;
90
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { EmitContext, EmittedFile } from './shared';
2
+ /**
3
+ * Rules land in `.claude/rules/ethlete/` (loaded every session, or only for matching files when
4
+ * the content declares `paths`); skills land in `.claude/skills/` under an `ethlete-` prefix so a
5
+ * generated skill can never collide with one the repo wrote itself.
6
+ */
7
+ export declare const emitClaude: (context: EmitContext) => EmittedFile[];
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emitClaude = void 0;
4
+ const render_1 = require("../render");
5
+ const shared_1 = require("./shared");
6
+ const skillDir = (name) => `.claude/skills/ethlete-${name}`;
7
+ const quoteList = (values) => values.map((value) => ` - '${value}'`).join('\n');
8
+ /**
9
+ * Rules land in `.claude/rules/ethlete/` (loaded every session, or only for matching files when
10
+ * the content declares `paths`); skills land in `.claude/skills/` under an `ethlete-` prefix so a
11
+ * generated skill can never collide with one the repo wrote itself.
12
+ */
13
+ const emitClaude = (context) => {
14
+ const banner = (0, render_1.buildBanner)(context.version);
15
+ const links = (0, shared_1.makeLinks)({
16
+ context,
17
+ skill: (name) => `the **\`ethlete-${name}\`** skill`,
18
+ resource: (target) => `\`${target.fileName}\` (bundled next to this skill)`,
19
+ });
20
+ const files = [];
21
+ for (const item of context.rules) {
22
+ const { name, paths } = item.frontmatter;
23
+ const frontmatter = paths.length > 0 ? `---\npaths:\n${quoteList(paths)}\n---` : '';
24
+ files.push({
25
+ path: `.claude/rules/ethlete/${name}.md`,
26
+ contents: (0, shared_1.document)([frontmatter, banner, (0, shared_1.body)({ item, context, links })]),
27
+ });
28
+ }
29
+ for (const item of context.skills) {
30
+ const { name } = item.frontmatter;
31
+ const frontmatter = [
32
+ '---',
33
+ `name: ethlete-${name}`,
34
+ `description: ${(0, shared_1.yamlString)((0, shared_1.description)({ item, context }))}`,
35
+ '---',
36
+ ].join('\n');
37
+ files.push({
38
+ path: `${skillDir(name)}/SKILL.md`,
39
+ contents: (0, shared_1.document)([frontmatter, banner, (0, shared_1.body)({ item, context, links })]),
40
+ });
41
+ files.push(...(0, shared_1.resourceFiles)({ item, context, pathFor: (fileName) => `${skillDir(name)}/${fileName}` }));
42
+ }
43
+ return files;
44
+ };
45
+ exports.emitClaude = emitClaude;
46
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../../../../../libs/agent-rules/src/lib/targets/claude.ts"],"names":[],"mappings":";;;AAAA,sCAAwC;AACxC,qCAAuH;AAEvH,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,0BAA0B,IAAI,EAAE,CAAC;AAEpE,MAAM,SAAS,GAAG,CAAC,MAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAE3F;;;;GAIG;AACI,MAAM,UAAU,GAAG,CAAC,OAAoB,EAAiB,EAAE;IAChE,MAAM,MAAM,GAAG,IAAA,oBAAW,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAA,kBAAS,EAAC;QACtB,OAAO;QACP,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,IAAI,YAAY;QACpD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,QAAQ,iCAAiC;KAC5E,CAAC,CAAC;IACH,MAAM,KAAK,GAAkB,EAAE,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;QACzC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAEpF,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,yBAAyB,IAAI,KAAK;YACxC,QAAQ,EAAE,IAAA,iBAAQ,EAAC,CAAC,WAAW,EAAE,MAAM,EAAE,IAAA,aAAI,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;SAC1E,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;QAClC,MAAM,WAAW,GAAG;YAClB,KAAK;YACL,iBAAiB,IAAI,EAAE;YACvB,gBAAgB,IAAA,mBAAU,EAAC,IAAA,oBAAW,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE;YAC5D,KAAK;SACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW;YAClC,QAAQ,EAAE,IAAA,iBAAQ,EAAC,CAAC,WAAW,EAAE,MAAM,EAAE,IAAA,aAAI,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;SAC1E,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC,GAAG,IAAA,sBAAa,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1G,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AArCW,QAAA,UAAU,cAqCrB"}
@@ -0,0 +1,11 @@
1
+ import { EmitContext, EmittedFile } from './shared';
2
+ export declare const CODEX_FILE = "AGENTS.md";
3
+ /**
4
+ * `AGENTS.md` supports neither frontmatter nor includes, so the always-loaded rules are inlined and
5
+ * the on-demand skills become a manifest of paths into `.agents/ethlete/` (emitted separately by the
6
+ * neutral target). Everything outside the marker block is left as the repo wrote it.
7
+ */
8
+ export declare const emitCodex: (options: {
9
+ context: EmitContext;
10
+ existing: string;
11
+ }) => EmittedFile[];
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emitCodex = exports.CODEX_FILE = void 0;
4
+ const render_1 = require("../render");
5
+ const shared_1 = require("./shared");
6
+ exports.CODEX_FILE = 'AGENTS.md';
7
+ /**
8
+ * `AGENTS.md` supports neither frontmatter nor includes, so the always-loaded rules are inlined and
9
+ * the on-demand skills become a manifest of paths into `.agents/ethlete/` (emitted separately by the
10
+ * neutral target). Everything outside the marker block is left as the repo wrote it.
11
+ */
12
+ const emitCodex = (options) => {
13
+ const { context, existing } = options;
14
+ const sections = context.rules.map((item) => (0, shared_1.body)({ item, context, links: (0, shared_1.neutralLinks)(context) }));
15
+ if (context.skills.length > 0) {
16
+ sections.push([
17
+ '## Ethlete reference docs',
18
+ 'Read the matching file before starting that kind of work — do not work from memory.',
19
+ (0, shared_1.pointerTable)({ skills: context.skills, context, pathFor: shared_1.neutralBodyPath }),
20
+ ].join('\n\n'));
21
+ }
22
+ const block = (0, shared_1.document)([(0, render_1.buildBanner)(context.version), ...sections]).trimEnd();
23
+ return [{ path: exports.CODEX_FILE, contents: (0, render_1.replaceMarkedBlock)({ existing, block }) }];
24
+ };
25
+ exports.emitCodex = emitCodex;
26
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../../../../libs/agent-rules/src/lib/targets/codex.ts"],"names":[],"mappings":";;;AAAA,sCAA4D;AAC5D,qCAAiH;AAEpG,QAAA,UAAU,GAAG,WAAW,CAAC;AAEtC;;;;GAIG;AACI,MAAM,SAAS,GAAG,CAAC,OAAmD,EAAiB,EAAE;IAC9F,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAA,aAAI,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAA,qBAAY,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAEpG,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,QAAQ,CAAC,IAAI,CACX;YACE,2BAA2B;YAC3B,qFAAqF;YACrF,IAAA,qBAAY,EAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAe,EAAE,CAAC;SAC5E,CAAC,IAAI,CAAC,MAAM,CAAC,CACf,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,IAAA,iBAAQ,EAAC,CAAC,IAAA,oBAAW,EAAC,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAE9E,OAAO,CAAC,EAAE,IAAI,EAAE,kBAAU,EAAE,QAAQ,EAAE,IAAA,2BAAkB,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AACnF,CAAC,CAAC;AAjBW,QAAA,SAAS,aAiBpB"}
@@ -0,0 +1,11 @@
1
+ import { EmitContext, EmittedFile } from './shared';
2
+ export declare const COPILOT_FILE = ".github/copilot-instructions.md";
3
+ /**
4
+ * Copilot can scope an instruction file to a glob but has no way to load one on description alone,
5
+ * so a skill with `paths` becomes a real `.instructions.md` and everything else falls back to a
6
+ * manifest pointer in the always-loaded file.
7
+ */
8
+ export declare const emitCopilot: (options: {
9
+ context: EmitContext;
10
+ existing: string;
11
+ }) => EmittedFile[];
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emitCopilot = exports.COPILOT_FILE = void 0;
4
+ const render_1 = require("../render");
5
+ const shared_1 = require("./shared");
6
+ exports.COPILOT_FILE = '.github/copilot-instructions.md';
7
+ /**
8
+ * Copilot can scope an instruction file to a glob but has no way to load one on description alone,
9
+ * so a skill with `paths` becomes a real `.instructions.md` and everything else falls back to a
10
+ * manifest pointer in the always-loaded file.
11
+ */
12
+ const emitCopilot = (options) => {
13
+ const { context, existing } = options;
14
+ const banner = (0, render_1.buildBanner)(context.version);
15
+ const links = (0, shared_1.makeLinks)({
16
+ context,
17
+ skill: (name) => `\`${(0, shared_1.neutralBodyPath)(name)}\``,
18
+ resource: (target) => `\`${(0, shared_1.neutralResourcePath)(target)}\``,
19
+ });
20
+ const files = [];
21
+ const scoped = context.skills.filter((item) => item.frontmatter.paths.length > 0);
22
+ const unscoped = context.skills.filter((item) => item.frontmatter.paths.length === 0);
23
+ for (const item of scoped) {
24
+ const frontmatter = ['---', `applyTo: ${(0, shared_1.yamlString)(item.frontmatter.paths.join(','))}`, '---'].join('\n');
25
+ files.push({
26
+ path: `.github/instructions/ethlete-${item.frontmatter.name}.instructions.md`,
27
+ contents: (0, shared_1.document)([frontmatter, banner, (0, shared_1.body)({ item, context, links })]),
28
+ });
29
+ }
30
+ const sections = context.rules.map((item) => (0, shared_1.body)({ item, context, links }));
31
+ if (unscoped.length > 0) {
32
+ sections.push([
33
+ '## Ethlete reference docs',
34
+ 'Read the matching file before starting that kind of work — do not work from memory.',
35
+ (0, shared_1.pointerTable)({ skills: unscoped, context, pathFor: shared_1.neutralBodyPath }),
36
+ ].join('\n\n'));
37
+ }
38
+ const block = (0, shared_1.document)([banner, ...sections]).trimEnd();
39
+ files.push({ path: exports.COPILOT_FILE, contents: (0, render_1.replaceMarkedBlock)({ existing, block }) });
40
+ return files;
41
+ };
42
+ exports.emitCopilot = emitCopilot;
43
+ //# sourceMappingURL=copilot.js.map