@lean-agent/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/adapters/claude.d.ts +11 -0
  4. package/dist/adapters/claude.js +70 -0
  5. package/dist/adapters/claude.js.map +1 -0
  6. package/dist/adapters/cursor.d.ts +17 -0
  7. package/dist/adapters/cursor.js +49 -0
  8. package/dist/adapters/cursor.js.map +1 -0
  9. package/dist/adapters/index.d.ts +3 -0
  10. package/dist/adapters/index.js +18 -0
  11. package/dist/adapters/index.js.map +1 -0
  12. package/dist/adapters/types.d.ts +22 -0
  13. package/dist/adapters/types.js +2 -0
  14. package/dist/adapters/types.js.map +1 -0
  15. package/dist/adapters/vscode.d.ts +17 -0
  16. package/dist/adapters/vscode.js +48 -0
  17. package/dist/adapters/vscode.js.map +1 -0
  18. package/dist/adapters/windsurf.d.ts +20 -0
  19. package/dist/adapters/windsurf.js +52 -0
  20. package/dist/adapters/windsurf.js.map +1 -0
  21. package/dist/adapters/yaml.d.ts +9 -0
  22. package/dist/adapters/yaml.js +15 -0
  23. package/dist/adapters/yaml.js.map +1 -0
  24. package/dist/banner.d.ts +1 -0
  25. package/dist/banner.js +19 -0
  26. package/dist/banner.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +38 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/commands/add.d.ts +7 -0
  31. package/dist/commands/add.js +63 -0
  32. package/dist/commands/add.js.map +1 -0
  33. package/dist/commands/init.d.ts +7 -0
  34. package/dist/commands/init.js +37 -0
  35. package/dist/commands/init.js.map +1 -0
  36. package/dist/commands/list.d.ts +5 -0
  37. package/dist/commands/list.js +44 -0
  38. package/dist/commands/list.js.map +1 -0
  39. package/dist/commands/remove.d.ts +7 -0
  40. package/dist/commands/remove.js +31 -0
  41. package/dist/commands/remove.js.map +1 -0
  42. package/dist/config.d.ts +22 -0
  43. package/dist/config.js +55 -0
  44. package/dist/config.js.map +1 -0
  45. package/dist/index.d.ts +7 -0
  46. package/dist/index.js +7 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/skill.d.ts +14 -0
  49. package/dist/skill.js +54 -0
  50. package/dist/skill.js.map +1 -0
  51. package/package.json +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peter Rupp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @lean-agent/core
2
+
3
+ > CLI and SDK for [lean-agent](https://github.com/disRupptive/lean-agent) — the lean framework for agentic software development.
4
+
5
+ Install portable skills, workflows, and commands into any AI coding tool with a single command.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npx lean-agent add challenger --tool=claude
11
+ ```
12
+
13
+ Or install globally:
14
+
15
+ ```bash
16
+ npm install -g @lean-agent/core @lean-agent/skills
17
+ lean-agent add challenger
18
+ ```
19
+
20
+ ## Commands
21
+
22
+ ```
23
+ lean-agent init Set up the project (or --global)
24
+ lean-agent add <skill> [--tool=NAME] Install a primitive
25
+ lean-agent remove <skill> [--tool=NAME] Uninstall a primitive
26
+ lean-agent list Show what's available and installed
27
+ ```
28
+
29
+ Short alias: `la`. Flags: `--tool=claude|cursor|windsurf|vscode`, `--global`.
30
+
31
+ ## Supported tools
32
+
33
+ | Tool | Where it lives |
34
+ | --- | --- |
35
+ | Claude Code | `.claude/skills/<name>/SKILL.md` (symlink) |
36
+ | Cursor | `.cursor/rules/<name>.mdc` |
37
+ | Windsurf | `.windsurf/rules/<name>.md` |
38
+ | VS Code (GitHub Copilot) | `.github/prompts/<name>.prompt.md` |
39
+
40
+ See the [main repository](https://github.com/disRupptive/lean-agent) for the full vision, roadmap, and skill catalog.
41
+
42
+ ## License
43
+
44
+ MIT
@@ -0,0 +1,11 @@
1
+ import type { Adapter } from './types.js';
2
+ /**
3
+ * Claude Code adapter.
4
+ *
5
+ * Project scope installs into `<targetRoot>/.claude/skills/<name>`.
6
+ * Global scope installs into `<targetRoot>/.claude/skills/<name>` where targetRoot is the homedir.
7
+ *
8
+ * Uses directory symlinks by default; falls back to a recursive copy on platforms
9
+ * (or permission modes) where symlinks can't be created — e.g. Windows without Developer Mode.
10
+ */
11
+ export declare const claudeAdapter: Adapter;
@@ -0,0 +1,70 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Claude Code adapter.
5
+ *
6
+ * Project scope installs into `<targetRoot>/.claude/skills/<name>`.
7
+ * Global scope installs into `<targetRoot>/.claude/skills/<name>` where targetRoot is the homedir.
8
+ *
9
+ * Uses directory symlinks by default; falls back to a recursive copy on platforms
10
+ * (or permission modes) where symlinks can't be created — e.g. Windows without Developer Mode.
11
+ */
12
+ export const claudeAdapter = {
13
+ tool: 'claude',
14
+ async install(ctx) {
15
+ const skillsDir = path.join(ctx.targetRoot, '.claude', 'skills');
16
+ fs.mkdirSync(skillsDir, { recursive: true });
17
+ const linkPath = path.join(skillsDir, ctx.skill.name);
18
+ // Clear any existing entry (symlink or directory) so we can re-install cleanly.
19
+ clearPath(linkPath);
20
+ try {
21
+ fs.symlinkSync(ctx.skill.sourcePath, linkPath, 'dir');
22
+ }
23
+ catch (err) {
24
+ const code = err.code;
25
+ if (code === 'EPERM' || code === 'EACCES' || code === 'ENOSYS') {
26
+ // Windows without dev mode, or filesystem that doesn't support symlinks.
27
+ copyDir(ctx.skill.sourcePath, linkPath);
28
+ }
29
+ else {
30
+ throw err;
31
+ }
32
+ }
33
+ return linkPath;
34
+ },
35
+ async remove(ctx) {
36
+ const skillPath = path.join(ctx.targetRoot, '.claude', 'skills', ctx.skillName);
37
+ clearPath(skillPath);
38
+ },
39
+ };
40
+ function clearPath(p) {
41
+ try {
42
+ // lstat so we see symlinks themselves, not their targets.
43
+ const stat = fs.lstatSync(p);
44
+ if (stat.isSymbolicLink() || stat.isFile()) {
45
+ fs.unlinkSync(p);
46
+ }
47
+ else if (stat.isDirectory()) {
48
+ fs.rmSync(p, { recursive: true, force: true });
49
+ }
50
+ }
51
+ catch (err) {
52
+ if (err.code !== 'ENOENT') {
53
+ throw err;
54
+ }
55
+ }
56
+ }
57
+ function copyDir(src, dest) {
58
+ fs.mkdirSync(dest, { recursive: true });
59
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
60
+ const srcPath = path.join(src, entry.name);
61
+ const destPath = path.join(dest, entry.name);
62
+ if (entry.isDirectory()) {
63
+ copyDir(srcPath, destPath);
64
+ }
65
+ else if (entry.isFile()) {
66
+ fs.copyFileSync(srcPath, destPath);
67
+ }
68
+ }
69
+ }
70
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/adapters/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,IAAI,EAAE,QAAQ;IAEd,KAAK,CAAC,OAAO,CAAC,GAAmB;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACjE,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE7C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEtD,gFAAgF;QAChF,SAAS,CAAC,QAAQ,CAAC,CAAC;QAEpB,IAAI,CAAC;YACH,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;YACjD,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC/D,yEAAyE;gBACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAkB;QAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QAChF,SAAS,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;CACF,CAAC;AAEF,SAAS,SAAS,CAAC,CAAS;IAC1B,IAAI,CAAC;QACH,0DAA0D;QAC1D,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;aAAM,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC9B,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,GAAW,EAAE,IAAY;IACxC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Adapter } from './types.js';
2
+ /**
3
+ * Cursor adapter.
4
+ *
5
+ * Cursor reads project rules from `.cursor/rules/*.mdc` — a file format with
6
+ * YAML frontmatter plus a markdown body. (Global scope uses `~/.cursor/rules/`.)
7
+ *
8
+ * Unlike Claude Code, Cursor has no native SKILL.md support, so we transpile
9
+ * the skill into a rules file. Because this is a DERIVED file (not a symlink),
10
+ * it will NOT auto-update when `@lean-agent/skills` changes — re-run
11
+ * `lean-agent add <skill>` to refresh it.
12
+ *
13
+ * `alwaysApply: false` makes the rule "agent-requested": Cursor's AI decides
14
+ * whether to attach it based on the `description` field, which is the closest
15
+ * analogue to Claude Code's description-triggered auto-invocation.
16
+ */
17
+ export declare const cursorAdapter: Adapter;
@@ -0,0 +1,49 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { yamlString } from './yaml.js';
4
+ /**
5
+ * Cursor adapter.
6
+ *
7
+ * Cursor reads project rules from `.cursor/rules/*.mdc` — a file format with
8
+ * YAML frontmatter plus a markdown body. (Global scope uses `~/.cursor/rules/`.)
9
+ *
10
+ * Unlike Claude Code, Cursor has no native SKILL.md support, so we transpile
11
+ * the skill into a rules file. Because this is a DERIVED file (not a symlink),
12
+ * it will NOT auto-update when `@lean-agent/skills` changes — re-run
13
+ * `lean-agent add <skill>` to refresh it.
14
+ *
15
+ * `alwaysApply: false` makes the rule "agent-requested": Cursor's AI decides
16
+ * whether to attach it based on the `description` field, which is the closest
17
+ * analogue to Claude Code's description-triggered auto-invocation.
18
+ */
19
+ export const cursorAdapter = {
20
+ tool: 'cursor',
21
+ async install(ctx) {
22
+ const rulesDir = path.join(ctx.targetRoot, '.cursor', 'rules');
23
+ fs.mkdirSync(rulesDir, { recursive: true });
24
+ const filePath = path.join(rulesDir, `${ctx.skill.name}.mdc`);
25
+ const content = renderMdc(ctx.skill.metadata.description, ctx.skill.body);
26
+ fs.writeFileSync(filePath, content);
27
+ return filePath;
28
+ },
29
+ async remove(ctx) {
30
+ const filePath = path.join(ctx.targetRoot, '.cursor', 'rules', `${ctx.skillName}.mdc`);
31
+ try {
32
+ fs.unlinkSync(filePath);
33
+ }
34
+ catch (err) {
35
+ if (err.code !== 'ENOENT')
36
+ throw err;
37
+ }
38
+ },
39
+ };
40
+ function renderMdc(description, body) {
41
+ return `---
42
+ description: ${yamlString(description)}
43
+ alwaysApply: false
44
+ ---
45
+
46
+ ${body.trim()}
47
+ `;
48
+ }
49
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/adapters/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,IAAI,EAAE,QAAQ;IAEd,KAAK,CAAC,OAAO,CAAC,GAAmB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAC/D,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE5C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC;QAC9D,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1E,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEpC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAkB;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,GAAG,CAAC,UAAU,EACd,SAAS,EACT,OAAO,EACP,GAAG,GAAG,CAAC,SAAS,MAAM,CACvB,CAAC;QACF,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;QAClE,CAAC;IACH,CAAC;CACF,CAAC;AAEF,SAAS,SAAS,CAAC,WAAmB,EAAE,IAAY;IAClD,OAAO;eACM,UAAU,CAAC,WAAW,CAAC;;;;EAIpC,IAAI,CAAC,IAAI,EAAE;CACZ,CAAC;AACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Adapter, Tool } from './types.js';
2
+ export declare const ADAPTERS: Record<Tool, Adapter>;
3
+ export declare function getAdapter(tool: Tool): Adapter;
@@ -0,0 +1,18 @@
1
+ import { claudeAdapter } from './claude.js';
2
+ import { cursorAdapter } from './cursor.js';
3
+ import { windsurfAdapter } from './windsurf.js';
4
+ import { vscodeAdapter } from './vscode.js';
5
+ export const ADAPTERS = {
6
+ claude: claudeAdapter,
7
+ cursor: cursorAdapter,
8
+ windsurf: windsurfAdapter,
9
+ vscode: vscodeAdapter,
10
+ };
11
+ export function getAdapter(tool) {
12
+ const adapter = ADAPTERS[tool];
13
+ if (!adapter) {
14
+ throw new Error(`Unknown tool: ${tool}`);
15
+ }
16
+ return adapter;
17
+ }
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,CAAC,MAAM,QAAQ,GAA0B;IAC7C,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,aAAa;IACrB,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,aAAa;CACtB,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Skill } from '../skill.js';
2
+ import type { Scope } from '../config.js';
3
+ export type Tool = 'claude' | 'cursor' | 'windsurf' | 'vscode';
4
+ export declare const ALL_TOOLS: Tool[];
5
+ export interface InstallContext {
6
+ skill: Skill;
7
+ scope: Scope;
8
+ /** The filesystem root to install under (cwd for project, homedir for global). */
9
+ targetRoot: string;
10
+ }
11
+ export interface RemoveContext {
12
+ skillName: string;
13
+ scope: Scope;
14
+ targetRoot: string;
15
+ }
16
+ export interface Adapter {
17
+ tool: Tool;
18
+ /** Install a skill. Returns the path the skill was installed at. */
19
+ install(ctx: InstallContext): Promise<string>;
20
+ /** Remove a previously installed skill. Idempotent. */
21
+ remove(ctx: RemoveContext): Promise<void>;
22
+ }
@@ -0,0 +1,2 @@
1
+ export const ALL_TOOLS = ['claude', 'cursor', 'windsurf', 'vscode'];
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,SAAS,GAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Adapter } from './types.js';
2
+ /**
3
+ * VS Code / GitHub Copilot adapter.
4
+ *
5
+ * GitHub Copilot in VS Code supports three customization file types:
6
+ * - `.github/copilot-instructions.md` — always-on, repo-wide
7
+ * - `.github/instructions/*.instructions.md` — file-scoped via `applyTo` globs
8
+ * - `.github/prompts/*.prompt.md` — user-invoked reusable prompts
9
+ *
10
+ * Skills are user-triggered by design ("grill me on X", "code review me"),
11
+ * which maps cleanly onto prompt files — the user invokes them from chat
12
+ * by name rather than having them injected into every request.
13
+ *
14
+ * Derived file (not a symlink): re-run `lean-agent add <skill>` after
15
+ * updating `@lean-agent/skills` to refresh the transpiled prompt.
16
+ */
17
+ export declare const vscodeAdapter: Adapter;
@@ -0,0 +1,48 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { yamlString } from './yaml.js';
4
+ /**
5
+ * VS Code / GitHub Copilot adapter.
6
+ *
7
+ * GitHub Copilot in VS Code supports three customization file types:
8
+ * - `.github/copilot-instructions.md` — always-on, repo-wide
9
+ * - `.github/instructions/*.instructions.md` — file-scoped via `applyTo` globs
10
+ * - `.github/prompts/*.prompt.md` — user-invoked reusable prompts
11
+ *
12
+ * Skills are user-triggered by design ("grill me on X", "code review me"),
13
+ * which maps cleanly onto prompt files — the user invokes them from chat
14
+ * by name rather than having them injected into every request.
15
+ *
16
+ * Derived file (not a symlink): re-run `lean-agent add <skill>` after
17
+ * updating `@lean-agent/skills` to refresh the transpiled prompt.
18
+ */
19
+ export const vscodeAdapter = {
20
+ tool: 'vscode',
21
+ async install(ctx) {
22
+ const promptsDir = path.join(ctx.targetRoot, '.github', 'prompts');
23
+ fs.mkdirSync(promptsDir, { recursive: true });
24
+ const filePath = path.join(promptsDir, `${ctx.skill.name}.prompt.md`);
25
+ const content = renderPrompt(ctx.skill.metadata.description, ctx.skill.body);
26
+ fs.writeFileSync(filePath, content);
27
+ return filePath;
28
+ },
29
+ async remove(ctx) {
30
+ const filePath = path.join(ctx.targetRoot, '.github', 'prompts', `${ctx.skillName}.prompt.md`);
31
+ try {
32
+ fs.unlinkSync(filePath);
33
+ }
34
+ catch (err) {
35
+ if (err.code !== 'ENOENT')
36
+ throw err;
37
+ }
38
+ },
39
+ };
40
+ function renderPrompt(description, body) {
41
+ return `---
42
+ description: ${yamlString(description)}
43
+ ---
44
+
45
+ ${body.trim()}
46
+ `;
47
+ }
48
+ //# sourceMappingURL=vscode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vscode.js","sourceRoot":"","sources":["../../src/adapters/vscode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,IAAI,EAAE,QAAQ;IAEd,KAAK,CAAC,OAAO,CAAC,GAAmB;QAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACnE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7E,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEpC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAkB;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,GAAG,CAAC,UAAU,EACd,SAAS,EACT,SAAS,EACT,GAAG,GAAG,CAAC,SAAS,YAAY,CAC7B,CAAC;QACF,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;QAClE,CAAC;IACH,CAAC;CACF,CAAC;AAEF,SAAS,YAAY,CAAC,WAAmB,EAAE,IAAY;IACrD,OAAO;eACM,UAAU,CAAC,WAAW,CAAC;;;EAGpC,IAAI,CAAC,IAAI,EAAE;CACZ,CAAC;AACF,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { Adapter } from './types.js';
2
+ /**
3
+ * Windsurf adapter.
4
+ *
5
+ * Windsurf reads workspace rules from `.windsurf/rules/*.md` — markdown files
6
+ * with YAML frontmatter. The `trigger` field controls activation:
7
+ * - always_on: always injected into every request
8
+ * - model_decision: AI decides based on `description`
9
+ * - glob: activated when files matching `globs` are in context
10
+ * - manual: only when user explicitly references the rule
11
+ *
12
+ * For skills like challenger that should activate when the user expresses an
13
+ * intent (not for specific file types, not always-on), `trigger: model_decision`
14
+ * with a rich description is the right mapping — it mirrors Claude Code's
15
+ * description-triggered invocation model.
16
+ *
17
+ * Derived file (not a symlink): re-run `lean-agent add <skill>` after
18
+ * updating `@lean-agent/skills` to refresh the transpiled rule.
19
+ */
20
+ export declare const windsurfAdapter: Adapter;
@@ -0,0 +1,52 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { yamlString } from './yaml.js';
4
+ /**
5
+ * Windsurf adapter.
6
+ *
7
+ * Windsurf reads workspace rules from `.windsurf/rules/*.md` — markdown files
8
+ * with YAML frontmatter. The `trigger` field controls activation:
9
+ * - always_on: always injected into every request
10
+ * - model_decision: AI decides based on `description`
11
+ * - glob: activated when files matching `globs` are in context
12
+ * - manual: only when user explicitly references the rule
13
+ *
14
+ * For skills like challenger that should activate when the user expresses an
15
+ * intent (not for specific file types, not always-on), `trigger: model_decision`
16
+ * with a rich description is the right mapping — it mirrors Claude Code's
17
+ * description-triggered invocation model.
18
+ *
19
+ * Derived file (not a symlink): re-run `lean-agent add <skill>` after
20
+ * updating `@lean-agent/skills` to refresh the transpiled rule.
21
+ */
22
+ export const windsurfAdapter = {
23
+ tool: 'windsurf',
24
+ async install(ctx) {
25
+ const rulesDir = path.join(ctx.targetRoot, '.windsurf', 'rules');
26
+ fs.mkdirSync(rulesDir, { recursive: true });
27
+ const filePath = path.join(rulesDir, `${ctx.skill.name}.md`);
28
+ const content = renderRule(ctx.skill.metadata.description, ctx.skill.body);
29
+ fs.writeFileSync(filePath, content);
30
+ return filePath;
31
+ },
32
+ async remove(ctx) {
33
+ const filePath = path.join(ctx.targetRoot, '.windsurf', 'rules', `${ctx.skillName}.md`);
34
+ try {
35
+ fs.unlinkSync(filePath);
36
+ }
37
+ catch (err) {
38
+ if (err.code !== 'ENOENT')
39
+ throw err;
40
+ }
41
+ },
42
+ };
43
+ function renderRule(description, body) {
44
+ return `---
45
+ trigger: model_decision
46
+ description: ${yamlString(description)}
47
+ ---
48
+
49
+ ${body.trim()}
50
+ `;
51
+ }
52
+ //# sourceMappingURL=windsurf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windsurf.js","sourceRoot":"","sources":["../../src/adapters/windsurf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAY;IACtC,IAAI,EAAE,UAAU;IAEhB,KAAK,CAAC,OAAO,CAAC,GAAmB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QACjE,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE5C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3E,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEpC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAkB;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,GAAG,CAAC,UAAU,EACd,WAAW,EACX,OAAO,EACP,GAAG,GAAG,CAAC,SAAS,KAAK,CACtB,CAAC;QACF,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;QAClE,CAAC;IACH,CAAC;CACF,CAAC;AAEF,SAAS,UAAU,CAAC,WAAmB,EAAE,IAAY;IACnD,OAAO;;eAEM,UAAU,CAAC,WAAW,CAAC;;;EAGpC,IAAI,CAAC,IAAI,EAAE;CACZ,CAAC;AACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Serialize a string as a YAML scalar safely.
3
+ *
4
+ * Uses the flow-style double-quoted form when the input contains any
5
+ * YAML-significant character, which lets us pass through descriptions
6
+ * containing colons, quotes, or other punctuation without worrying about
7
+ * the full YAML escape machinery.
8
+ */
9
+ export declare function yamlString(s: string): string;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Serialize a string as a YAML scalar safely.
3
+ *
4
+ * Uses the flow-style double-quoted form when the input contains any
5
+ * YAML-significant character, which lets us pass through descriptions
6
+ * containing colons, quotes, or other punctuation without worrying about
7
+ * the full YAML escape machinery.
8
+ */
9
+ export function yamlString(s) {
10
+ if (/[:#&*!|>'"%@`\n]/.test(s)) {
11
+ return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
12
+ }
13
+ return s;
14
+ }
15
+ //# sourceMappingURL=yaml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../src/adapters/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,OAAO,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC;IACnE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function banner(): string;
package/dist/banner.js ADDED
@@ -0,0 +1,19 @@
1
+ import pc from 'picocolors';
2
+ const LOGO = String.raw `
3
+ _ _ ___ _
4
+ / \ / | / _ \ / \
5
+ / _ \ | || | | | / _ \
6
+ / ___ \| || |_| |/ ___ \
7
+ /_/ \_\_| \___//_/ \_\
8
+ `;
9
+ export function banner() {
10
+ return (pc.cyan(pc.bold(LOGO)) +
11
+ '\n' +
12
+ pc.bold(' agentic-agile') +
13
+ pc.dim(' (a10a)') +
14
+ pc.dim(' v0.0.1') +
15
+ '\n' +
16
+ pc.dim(' The lean framework for agentic software development.') +
17
+ '\n');
18
+ }
19
+ //# sourceMappingURL=banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banner.js","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;CAMtB,CAAC;AAEF,MAAM,UAAU,MAAM;IACpB,OAAO,CACL,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI;QACJ,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC;QAC1B,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;QACjB,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;QACjB,IAAI;QACJ,EAAE,CAAC,GAAG,CAAC,wDAAwD,CAAC;QAChE,IAAI,CACL,CAAC;AACJ,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { initCommand } from './commands/init.js';
4
+ import { addCommand } from './commands/add.js';
5
+ import { removeCommand } from './commands/remove.js';
6
+ import { listCommand } from './commands/list.js';
7
+ const program = new Command();
8
+ program
9
+ .name('lean-agent')
10
+ .description('The lean framework for agentic software development.')
11
+ .version('0.0.1');
12
+ program
13
+ .command('init')
14
+ .description('Set up lean-agent in the current project (or --global).')
15
+ .option('--global', 'Write global config to ~/.lean-agent.json')
16
+ .action(initCommand);
17
+ program
18
+ .command('add <skill>')
19
+ .description('Install a skill from the catalog.')
20
+ .option('--tool <name>', 'Tool to install for (claude, cursor, windsurf, vscode)')
21
+ .option('--global', 'Install into the user-global scope instead of this project')
22
+ .action(addCommand);
23
+ program
24
+ .command('remove <skill>')
25
+ .description('Uninstall a skill.')
26
+ .option('--tool <name>', 'Tool to remove from')
27
+ .option('--global', 'Remove from the user-global scope')
28
+ .action(removeCommand);
29
+ program
30
+ .command('list')
31
+ .description('List installed skills and the catalog of available skills.')
32
+ .option('--global', 'List against the user-global scope')
33
+ .action(listCommand);
34
+ program.parseAsync(process.argv).catch((err) => {
35
+ console.error(err);
36
+ process.exit(1);
37
+ });
38
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,sDAAsD,CAAC;KACnE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,yDAAyD,CAAC;KACtE,MAAM,CAAC,UAAU,EAAE,2CAA2C,CAAC;KAC/D,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,aAAa,CAAC;KACtB,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,eAAe,EAAE,wDAAwD,CAAC;KACjF,MAAM,CAAC,UAAU,EAAE,4DAA4D,CAAC;KAChF,MAAM,CAAC,UAAU,CAAC,CAAC;AAEtB,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,oBAAoB,CAAC;KACjC,MAAM,CAAC,eAAe,EAAE,qBAAqB,CAAC;KAC9C,MAAM,CAAC,UAAU,EAAE,mCAAmC,CAAC;KACvD,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,4DAA4D,CAAC;KACzE,MAAM,CAAC,UAAU,EAAE,oCAAoC,CAAC;KACxD,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Tool } from '../adapters/types.js';
2
+ interface AddOptions {
3
+ tool?: Tool;
4
+ global?: boolean;
5
+ }
6
+ export declare function addCommand(skillName: string, options: AddOptions): Promise<void>;
7
+ export {};
@@ -0,0 +1,63 @@
1
+ import os from 'node:os';
2
+ import pc from 'picocolors';
3
+ import { readConfig, writeConfig, markInstalled, } from '../config.js';
4
+ import { resolveSkillPath, loadSkill, listAvailableSkills } from '../skill.js';
5
+ import { getAdapter } from '../adapters/index.js';
6
+ import { pickTool } from './init.js';
7
+ export async function addCommand(skillName, options) {
8
+ const scope = options.global ? 'global' : 'project';
9
+ // 1. Resolve the skill in the catalog.
10
+ const skillDir = resolveSkillPath(skillName);
11
+ if (!skillDir) {
12
+ const available = listAvailableSkills();
13
+ console.error(pc.red(`skill "${skillName}" not found in @lean-agent/skills.`));
14
+ if (available.length > 0) {
15
+ console.error(pc.dim(`available: ${available.join(', ')}`));
16
+ }
17
+ else {
18
+ console.error(pc.dim('no skills available — is @lean-agent/skills installed?'));
19
+ }
20
+ process.exit(1);
21
+ }
22
+ const skill = loadSkill(skillDir);
23
+ // 2. Load or bootstrap config (auto-init on first use).
24
+ let config = readConfig(scope);
25
+ let tool = options.tool;
26
+ if (!tool) {
27
+ tool = config?.tools?.[0] ?? undefined;
28
+ }
29
+ if (!tool) {
30
+ const picked = await pickTool();
31
+ if (!picked) {
32
+ console.error(pc.red('aborted: no tool selected.'));
33
+ process.exit(1);
34
+ }
35
+ tool = picked;
36
+ }
37
+ if (!config) {
38
+ config = { tools: [tool], installed: {} };
39
+ }
40
+ if (!config.tools.includes(tool)) {
41
+ config.tools.push(tool);
42
+ }
43
+ // 3. Install via the adapter.
44
+ const adapter = getAdapter(tool);
45
+ const targetRoot = scope === 'global' ? os.homedir() : process.cwd();
46
+ try {
47
+ const installedAt = await adapter.install({ skill, scope, targetRoot });
48
+ markInstalled(config, skillName, tool);
49
+ writeConfig(scope, config);
50
+ console.log(pc.green('installed') +
51
+ ` ${pc.bold(skill.name)} ` +
52
+ pc.dim('→ ') +
53
+ pc.cyan(tool) +
54
+ pc.dim(' (' + installedAt + ')'));
55
+ }
56
+ catch (err) {
57
+ const msg = err instanceof Error ? err.message : String(err);
58
+ console.error(pc.red(`failed to install ${skillName}:`));
59
+ console.error(pc.red(' ' + msg));
60
+ process.exit(1);
61
+ }
62
+ }
63
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Tool } from '../adapters/types.js';
2
+ interface InitOptions {
3
+ global?: boolean;
4
+ }
5
+ export declare function initCommand(options: InitOptions): Promise<void>;
6
+ export declare function pickTool(): Promise<Tool | null>;
7
+ export {};
@@ -0,0 +1,37 @@
1
+ import prompts from 'prompts';
2
+ import pc from 'picocolors';
3
+ import { readConfig, writeConfig } from '../config.js';
4
+ export async function initCommand(options) {
5
+ const scope = options.global ? 'global' : 'project';
6
+ const existing = readConfig(scope);
7
+ if (existing && existing.tools.length > 0) {
8
+ console.log(pc.yellow(`lean-agent is already initialized in this ${scope} scope.`) +
9
+ ` tools: ${pc.cyan(existing.tools.join(', '))}`);
10
+ return;
11
+ }
12
+ const tool = await pickTool();
13
+ if (!tool) {
14
+ console.error(pc.red('aborted: no tool selected.'));
15
+ process.exit(1);
16
+ }
17
+ writeConfig(scope, { tools: [tool], installed: {} });
18
+ console.log(pc.green('initialized') +
19
+ ` lean-agent for ${pc.cyan(tool)} (${scope} scope).`);
20
+ console.log(pc.dim('next: lean-agent add challenger'));
21
+ }
22
+ export async function pickTool() {
23
+ const response = await prompts({
24
+ type: 'select',
25
+ name: 'tool',
26
+ message: 'Which AI coding tool are you using?',
27
+ choices: [
28
+ { title: 'Claude Code', value: 'claude' },
29
+ { title: 'Cursor', value: 'cursor' },
30
+ { title: 'Windsurf', value: 'windsurf' },
31
+ { title: 'VS Code (GitHub Copilot)', value: 'vscode' },
32
+ ],
33
+ initial: 0,
34
+ });
35
+ return response.tool ?? null;
36
+ }
37
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAOvD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAoB;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAEnC,IAAI,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,GAAG,CACT,EAAE,CAAC,MAAM,CAAC,6CAA6C,KAAK,SAAS,CAAC;YACpE,WAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAClD,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,WAAW,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CACT,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC;QACrB,mBAAmB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,UAAU,CACvD,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC;QAC7B,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,qCAAqC;QAC9C,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE;YACzC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;YACpC,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE;YACxC,EAAE,KAAK,EAAE,0BAA0B,EAAE,KAAK,EAAE,QAAQ,EAAE;SACvD;QACD,OAAO,EAAE,CAAC;KACX,CAAC,CAAC;IACH,OAAQ,QAAQ,CAAC,IAAa,IAAI,IAAI,CAAC;AACzC,CAAC"}
@@ -0,0 +1,5 @@
1
+ interface ListOptions {
2
+ global?: boolean;
3
+ }
4
+ export declare function listCommand(options: ListOptions): Promise<void>;
5
+ export {};
@@ -0,0 +1,44 @@
1
+ import pc from 'picocolors';
2
+ import { readConfig } from '../config.js';
3
+ import { listAvailableSkills, resolveSkillPath, loadSkill } from '../skill.js';
4
+ export async function listCommand(options) {
5
+ const scope = options.global ? 'global' : 'project';
6
+ console.log(pc.bold('catalog (@lean-agent/skills):'));
7
+ const available = listAvailableSkills();
8
+ if (available.length === 0) {
9
+ console.log(pc.dim(' (empty — is @lean-agent/skills installed?)'));
10
+ }
11
+ else {
12
+ for (const name of available) {
13
+ const dir = resolveSkillPath(name);
14
+ if (!dir) {
15
+ console.log(' ' + pc.bold(name));
16
+ continue;
17
+ }
18
+ try {
19
+ const skill = loadSkill(dir);
20
+ const summary = skill.metadata.description.split(/\.\s/)[0];
21
+ console.log(' ' + pc.bold(name) + pc.dim(' — ' + summary));
22
+ }
23
+ catch {
24
+ console.log(' ' + pc.bold(name));
25
+ }
26
+ }
27
+ }
28
+ console.log('');
29
+ console.log(pc.bold(`installed (${scope} scope):`));
30
+ const config = readConfig(scope);
31
+ if (!config) {
32
+ console.log(pc.dim(' (no config — run `lean-agent init`)'));
33
+ return;
34
+ }
35
+ const entries = Object.entries(config.installed);
36
+ if (entries.length === 0) {
37
+ console.log(pc.dim(' (none yet — run `lean-agent add <skill>`)'));
38
+ return;
39
+ }
40
+ for (const [name, tools] of entries) {
41
+ console.log(' ' + pc.bold(name) + pc.dim(' → ') + pc.cyan(tools.join(', ')));
42
+ }
43
+ }
44
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM/E,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAoB;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,mBAAmB,EAAE,CAAC;IACxC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC,CAAC;IACtE,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAClC,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,KAAK,UAAU,CAAC,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChF,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Tool } from '../adapters/types.js';
2
+ interface RemoveOptions {
3
+ tool?: Tool;
4
+ global?: boolean;
5
+ }
6
+ export declare function removeCommand(skillName: string, options: RemoveOptions): Promise<void>;
7
+ export {};
@@ -0,0 +1,31 @@
1
+ import os from 'node:os';
2
+ import pc from 'picocolors';
3
+ import { readConfig, writeConfig, markRemoved } from '../config.js';
4
+ import { getAdapter } from '../adapters/index.js';
5
+ export async function removeCommand(skillName, options) {
6
+ const scope = options.global ? 'global' : 'project';
7
+ const config = readConfig(scope);
8
+ if (!config) {
9
+ console.error(pc.red(`no lean-agent config in this ${scope} scope — nothing to remove.`));
10
+ process.exit(1);
11
+ }
12
+ const tool = options.tool ?? config.tools[0];
13
+ if (!tool) {
14
+ console.error(pc.red('no tool configured. pass --tool=<name>.'));
15
+ process.exit(1);
16
+ }
17
+ const adapter = getAdapter(tool);
18
+ const targetRoot = scope === 'global' ? os.homedir() : process.cwd();
19
+ try {
20
+ await adapter.remove({ skillName, scope, targetRoot });
21
+ }
22
+ catch (err) {
23
+ const msg = err instanceof Error ? err.message : String(err);
24
+ console.error(pc.red(`failed to remove ${skillName}: ${msg}`));
25
+ process.exit(1);
26
+ }
27
+ markRemoved(config, skillName, tool);
28
+ writeConfig(scope, config);
29
+ console.log(pc.green('removed') + ` ${pc.bold(skillName)} from ${pc.cyan(tool)}`);
30
+ }
31
+ //# sourceMappingURL=remove.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove.js","sourceRoot":"","sources":["../../src/commands/remove.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAQlD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,OAAsB;IAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAEjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CACX,EAAE,CAAC,GAAG,CAAC,gCAAgC,KAAK,6BAA6B,CAAC,CAC3E,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAqB,OAAO,CAAC,IAAI,IAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAsB,CAAC;IACrF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,UAAU,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IAErE,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,oBAAoB,SAAS,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACrC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAE3B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpF,CAAC"}
@@ -0,0 +1,22 @@
1
+ export type Scope = 'project' | 'global';
2
+ /**
3
+ * Sticky config for an lean-agent scope.
4
+ *
5
+ * - `tools`: the set of tools this project/user is configured to use.
6
+ * First entry is the default target when `--tool` is omitted.
7
+ * - `installed`: map from skill name → list of tools it is currently installed for.
8
+ * Tracking per-tool lets us remove a skill from one tool without forgetting
9
+ * that it's still installed on another.
10
+ */
11
+ export interface LeanAgentConfig {
12
+ tools: string[];
13
+ installed: Record<string, string[]>;
14
+ }
15
+ export declare function getConfigPath(scope: Scope): string;
16
+ export declare function readConfig(scope: Scope): LeanAgentConfig | null;
17
+ export declare function writeConfig(scope: Scope, config: LeanAgentConfig): void;
18
+ /** Record that `skill` has been installed for `tool` in the given config. */
19
+ export declare function markInstalled(config: LeanAgentConfig, skill: string, tool: string): void;
20
+ /** Remove the record that `skill` is installed for `tool`. Drops the entry
21
+ * entirely if no tools remain. */
22
+ export declare function markRemoved(config: LeanAgentConfig, skill: string, tool: string): void;
package/dist/config.js ADDED
@@ -0,0 +1,55 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const CONFIG_NAME = '.lean-agent.json';
5
+ export function getConfigPath(scope) {
6
+ if (scope === 'global') {
7
+ return path.join(os.homedir(), CONFIG_NAME);
8
+ }
9
+ return path.join(process.cwd(), CONFIG_NAME);
10
+ }
11
+ export function readConfig(scope) {
12
+ const p = getConfigPath(scope);
13
+ if (!fs.existsSync(p))
14
+ return null;
15
+ try {
16
+ const raw = fs.readFileSync(p, 'utf8');
17
+ const parsed = JSON.parse(raw);
18
+ return {
19
+ tools: Array.isArray(parsed.tools) ? parsed.tools : [],
20
+ installed: parsed.installed && typeof parsed.installed === 'object'
21
+ ? parsed.installed
22
+ : {},
23
+ };
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ export function writeConfig(scope, config) {
30
+ const p = getConfigPath(scope);
31
+ fs.writeFileSync(p, JSON.stringify(config, null, 2) + '\n');
32
+ }
33
+ /** Record that `skill` has been installed for `tool` in the given config. */
34
+ export function markInstalled(config, skill, tool) {
35
+ const current = config.installed[skill] ?? [];
36
+ if (!current.includes(tool)) {
37
+ current.push(tool);
38
+ }
39
+ config.installed[skill] = current;
40
+ }
41
+ /** Remove the record that `skill` is installed for `tool`. Drops the entry
42
+ * entirely if no tools remain. */
43
+ export function markRemoved(config, skill, tool) {
44
+ const current = config.installed[skill];
45
+ if (!current)
46
+ return;
47
+ const next = current.filter((t) => t !== tool);
48
+ if (next.length === 0) {
49
+ delete config.installed[skill];
50
+ }
51
+ else {
52
+ config.installed[skill] = next;
53
+ }
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAkBzB,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAEvC,MAAM,UAAU,aAAa,CAAC,KAAY;IACxC,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,MAAM,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;YACtD,SAAS,EACP,MAAM,CAAC,SAAS,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;gBACtD,CAAC,CAAC,MAAM,CAAC,SAAS;gBAClB,CAAC,CAAC,EAAE;SACT,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAY,EAAE,MAAuB;IAC/D,MAAM,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/B,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,aAAa,CAC3B,MAAuB,EACvB,KAAa,EACb,IAAY;IAEZ,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC9C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;AACpC,CAAC;AAED;mCACmC;AACnC,MAAM,UAAU,WAAW,CACzB,MAAuB,EACvB,KAAa,EACb,IAAY;IAEZ,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IACjC,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { loadSkill, resolveSkillPath, listAvailableSkills } from './skill.js';
2
+ export type { Skill, SkillMetadata } from './skill.js';
3
+ export { readConfig, writeConfig, getConfigPath } from './config.js';
4
+ export type { LeanAgentConfig, Scope } from './config.js';
5
+ export { ADAPTERS, getAdapter } from './adapters/index.js';
6
+ export type { Adapter, Tool, InstallContext, RemoveContext } from './adapters/types.js';
7
+ export { ALL_TOOLS } from './adapters/types.js';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Public SDK surface. Consumers that want to build on top of lean-agent
2
+ // (e.g. authoring their own skill packages or custom adapters) import from here.
3
+ export { loadSkill, resolveSkillPath, listAvailableSkills } from './skill.js';
4
+ export { readConfig, writeConfig, getConfigPath } from './config.js';
5
+ export { ADAPTERS, getAdapter } from './adapters/index.js';
6
+ export { ALL_TOOLS } from './adapters/types.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,iFAAiF;AACjF,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE9E,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface SkillMetadata {
2
+ name: string;
3
+ description: string;
4
+ [key: string]: unknown;
5
+ }
6
+ export interface Skill {
7
+ name: string;
8
+ metadata: SkillMetadata;
9
+ body: string;
10
+ sourcePath: string;
11
+ }
12
+ export declare function resolveSkillPath(skillName: string): string | null;
13
+ export declare function loadSkill(skillDir: string): Skill;
14
+ export declare function listAvailableSkills(): string[];
package/dist/skill.js ADDED
@@ -0,0 +1,54 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import matter from 'gray-matter';
5
+ const require = createRequire(import.meta.url);
6
+ /**
7
+ * Resolve the on-disk root of @lean-agent/skills via Node's resolver.
8
+ * Works with npm workspaces (symlink into node_modules) and published installs.
9
+ */
10
+ function resolveSkillsPackageRoot() {
11
+ try {
12
+ const pkgJson = require.resolve('@lean-agent/skills/package.json');
13
+ return path.dirname(pkgJson);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export function resolveSkillPath(skillName) {
20
+ const root = resolveSkillsPackageRoot();
21
+ if (!root)
22
+ return null;
23
+ const skillDir = path.join(root, 'skills', skillName);
24
+ if (!fs.existsSync(path.join(skillDir, 'SKILL.md')))
25
+ return null;
26
+ return skillDir;
27
+ }
28
+ export function loadSkill(skillDir) {
29
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
30
+ const raw = fs.readFileSync(skillMdPath, 'utf8');
31
+ const parsed = matter(raw);
32
+ const metadata = parsed.data;
33
+ if (!metadata.name || !metadata.description) {
34
+ throw new Error(`Skill at ${skillDir} is missing required frontmatter fields (name, description).`);
35
+ }
36
+ return {
37
+ name: metadata.name,
38
+ metadata,
39
+ body: parsed.content.trim(),
40
+ sourcePath: skillDir,
41
+ };
42
+ }
43
+ export function listAvailableSkills() {
44
+ const root = resolveSkillsPackageRoot();
45
+ if (!root)
46
+ return [];
47
+ const skillsDir = path.join(root, 'skills');
48
+ if (!fs.existsSync(skillsDir))
49
+ return [];
50
+ return fs
51
+ .readdirSync(skillsDir)
52
+ .filter((name) => fs.existsSync(path.join(skillsDir, name, 'SKILL.md')));
53
+ }
54
+ //# sourceMappingURL=skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../src/skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAe/C;;;GAGG;AACH,SAAS,wBAAwB;IAC/B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,MAAM,IAAI,GAAG,wBAAwB,EAAE,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACjE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAqB,CAAC;IAC9C,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,YAAY,QAAQ,8DAA8D,CACnF,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ;QACR,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QAC3B,UAAU,EAAE,QAAQ;KACrB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,IAAI,GAAG,wBAAwB,EAAE,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,EAAE;SACN,WAAW,CAAC,SAAS,CAAC;SACtB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;AAC7E,CAAC"}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@lean-agent/core",
3
+ "version": "0.0.1",
4
+ "description": "CLI and SDK for lean-agent — the lean framework for agentic software development across every AI coding tool.",
5
+ "type": "module",
6
+ "bin": {
7
+ "lean-agent": "./dist/cli.js",
8
+ "la": "./dist/cli.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "files": [
13
+ "dist/**/*",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsx src/cli.ts",
20
+ "test": "vitest run",
21
+ "prepublishOnly": "npm run build && npm run test"
22
+ },
23
+ "dependencies": {
24
+ "@lean-agent/skills": "^0.0.1",
25
+ "commander": "^12.1.0",
26
+ "gray-matter": "^4.0.3",
27
+ "picocolors": "^1.1.0",
28
+ "prompts": "^2.4.2"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.14.0",
32
+ "@types/prompts": "^2.4.9",
33
+ "tsx": "^4.19.0",
34
+ "typescript": "^5.5.0",
35
+ "vitest": "^2.0.0"
36
+ },
37
+ "author": "Peter Rupp",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/disRupptive/lean-agent.git",
42
+ "directory": "packages/core"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/disRupptive/lean-agent/issues"
46
+ },
47
+ "homepage": "https://github.com/disRupptive/lean-agent/tree/main/packages/core#readme",
48
+ "keywords": [
49
+ "lean-agent",
50
+ "ai",
51
+ "cli",
52
+ "skills",
53
+ "claude",
54
+ "cursor",
55
+ "windsurf",
56
+ "copilot"
57
+ ],
58
+ "engines": {
59
+ "node": ">=18"
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ }
64
+ }