@p_tipso/agentive 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,27 +20,31 @@
20
20
 
21
21
  ---
22
22
 
23
- Stop maintaining separate rule files for every AI tool. **agentive** scaffolds a universal `.agents/` directory, an `AGENTS.md` file, and an `.aiignore` file in your project — providing a **single source of truth** for all your agent commands, skills, and rules, while significantly saving token usage.
23
+ Stop maintaining separate rule files for every AI tool. **agentive** scaffolds a universal `.agents/` directory, an `AGENTS.md` file, and an `.aiignore` file in your project — providing a **single source of truth** for all your agent commands, skills, libraries, and rules, while significantly saving token usage.
24
24
 
25
25
  ## ✨ Why Agentive?
26
26
 
27
- | Feature | Description |
28
- | :---------------------------- | :------------------------------------------------------------------- |
29
- | 🚀 **Interactive Setup** | Select your environment (General, Expo, React Native) to get tailored rules. |
30
- | 🌍 **Universal** | Framework-agnostic setup. Works with React, Python, Go, you name it. |
31
- | 🧠 **Single Source of Truth** | Centralize skills and rules for _all_ your AI agents in one place. |
32
- | ⚡ **Dynamic Layering** | Scaffolds base rules and safely merges framework-specific guardrails. |
27
+ | Feature | Description |
28
+ | :---------------------------- | :----------------------------------------------------------------------------- |
29
+ | 🚀 **Interactive Setup** | Select your environment (General, Expo, React Native) to get tailored rules. |
30
+ | 🌍 **Universal** | Framework-agnostic setup. Works with React, Python, Go, you name it. |
31
+ | 🧠 **Single Source of Truth** | Centralize skills, libraries, and rules for _all_ your AI agents in one place. |
32
+ | ⚡ **Dynamic Layering** | Scaffolds base rules and safely merges framework-specific guardrails. |
33
33
 
34
34
  ---
35
35
 
36
36
  ## 📦 Installation & Usage
37
37
 
38
- Run one command, get a fully structured AI agent workspace instantly:
38
+ Run one command to get a fully structured AI agent workspace instantly.
39
+ **Note for Users:** Always append `@latest` to ensure `npx` downloads the newest public release from the npm registry, avoiding any locally cached versions:
39
40
 
40
41
  ```bash
41
- npx @p_tipso/agentive
42
+ npx @p_tipso/agentive@latest
42
43
  ```
43
44
 
45
+ > **Note for Contributors:** If you are testing this package locally (e.g., via `npm link`), simply run `npx @p_tipso/agentive` (without `@latest`) to execute your local codebase.
46
+
47
+
44
48
  If you prefer to install it globally for frequent usage:
45
49
 
46
50
  ```bash
@@ -50,13 +54,13 @@ agentive
50
54
 
51
55
  ### CLI Commands
52
56
 
53
- | Command | Description |
54
- | :-------------------------------- | :----------------------------------------------------------- |
55
- | `npx @p_tipso/agentive init` | Scaffold `.agents/` workspace instantly in current directory |
56
- | `npx @p_tipso/agentive install <pkg>`| Install an agent skill or rule from the registry (alias: `add`) |
57
- | `npx @p_tipso/agentive remove <pkg>` | Remove an installed skill or rule (alias: `rm`) |
58
- | `npx @p_tipso/agentive --version` | Print the current CLI version |
59
- | `npx @p_tipso/agentive --help` | Show available commands and options |
57
+ | Command | Description |
58
+ | :------------------------------------ | :------------------------------------------------------------------------ |
59
+ | `npx @p_tipso/agentive init` | Scaffold `.agents/` workspace instantly in current directory |
60
+ | `npx @p_tipso/agentive install <pkg>` | Install an agent skill, library, or rule from the registry (alias: `add`) |
61
+ | `npx @p_tipso/agentive remove <pkg>` | Remove an installed skill, library, or rule (alias: `rm`) |
62
+ | `npx @p_tipso/agentive --version` | Print the current CLI version |
63
+ | `npx @p_tipso/agentive --help` | Show available commands and options |
60
64
 
61
65
  ---
62
66
 
@@ -77,6 +81,8 @@ your-project/
77
81
  │ │ └── fix-issue.md ← Example zero-error fix command
78
82
  │ ├── skills/
79
83
  │ │ └── README.md ← Guide: how to add skills
84
+ │ ├── library/
85
+ │ │ └── README.md ← Guide: passive library references
80
86
  │ └── rules/
81
87
  │ └── README.md ← Guide: how to add rules
82
88
  ```
@@ -86,10 +92,12 @@ your-project/
86
92
  - **`.aiignore`**: Prevents context pollution and saves tokens by hiding files (like `node_modules` or build outputs) from your AI agents.
87
93
  - **`commands/`**: Reusable prompt instructions that agents can execute on demand (e.g. `review.md`).
88
94
  - **`skills/`**: Skill definitions that teach agents how to behave in specific roles.
95
+ - **`library/`**: Passive library documentation and API references.
89
96
  - **`rules/`**: Project-wide rules that all agents must follow strictly.
90
97
 
91
98
  ### 🌟 Framework-Specific Guardrails
92
- By selecting a specific framework (like **Expo** or **React Native**), `agentive` overlays expertly crafted rules into your `.agents/` folder. This ensures your AI understands nuances like *Expo Router*, *Native Modules*, or *Unitless Pixels* right out of the box—preventing common hallucinations.
99
+
100
+ By selecting a specific framework (like **Expo** or **React Native**), `agentive` overlays expertly crafted rules into your `.agents/` folder. This ensures your AI understands nuances like _Expo Router_, _Native Modules_, or _Unitless Pixels_ right out of the box—preventing common hallucinations.
93
101
 
94
102
  ---
95
103
 
@@ -99,12 +107,11 @@ We love contributions! Whether it's adding new built-in skills, fixing bugs, or
99
107
 
100
108
  Please read our [Contributing Guide](CONTRIBUTING.md) to get started with setting up your local environment and submitting a Pull Request.
101
109
 
102
-
103
110
  ---
104
111
 
105
112
  ## ⭐ Star History
106
113
 
107
- [![Star History Chart](https://raw.githubusercontent.com/TiPS0/agentive/star-tracker/star-tracker.svg)](https://star-history.com/#TiPS0/agentive&Date)
114
+ [![Star History Chart](https://raw.githubusercontent.com/TiPS0/agentive/star-tracker-data/charts/star-history.svg)](https://star-history.com/#TiPS0/agentive&Date)
108
115
 
109
116
  ---
110
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p_tipso/agentive",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "A universal, framework-agnostic AI agent repository setup CLI. Scaffold a .agents/ workspace, AGENTS.md, and .aiignore to sync and optimize context for Cursor, Claude, and Windsurf.",
5
5
  "license": "MIT",
6
6
  "author": "Pakawat Tipso",
@@ -16,7 +16,7 @@ async function runInit() {
16
16
  const projectName = path.basename(cwd);
17
17
 
18
18
  console.log('');
19
- console.log(chalk.bold.cyan(' 🤖 agentive') + chalk.gray(' — Universal AI Agent Workspace Setup'));
19
+ console.log(chalk.bold.cyan(` Agentive v${version}`) + chalk.gray(' — Universal AI Agent Workspace Setup'));
20
20
  console.log(chalk.gray(' ─────────────────────────────────────────────'));
21
21
  console.log('');
22
22
 
@@ -123,6 +123,7 @@ async function runInit() {
123
123
  console.log(chalk.gray(' │ ├── review.md ← code review command'));
124
124
  console.log(chalk.gray(' │ └── fix-issue.md ← zero-error fix command'));
125
125
  console.log(chalk.gray(' ├── skills/'));
126
+ console.log(chalk.gray(' ├── library/'));
126
127
  console.log(chalk.gray(' └── rules/'));
127
128
  console.log('');
128
129
  console.log(chalk.gray(' Edit the markdown files to customise agent behaviour.'));
@@ -3,33 +3,23 @@
3
3
  const path = require('path');
4
4
  const chalk = require('chalk');
5
5
  const fs = require('fs/promises');
6
- const { agentDirectoryExists, addDependency, linkAgentFile } = require('../utils/fileSystem');
7
-
8
- // A simple regex to parse YAML frontmatter block at the start of a string
9
- const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n/;
10
-
11
- function parseFrontmatter(content) {
12
- const match = content.match(frontmatterRegex);
13
- const metadata = {};
14
- if (match && match[1]) {
15
- const lines = match[1].split('\n');
16
- lines.forEach(line => {
17
- const parts = line.split(':');
18
- if (parts.length >= 2) {
19
- const key = parts[0].trim();
20
- const value = parts.slice(1).join(':').trim().replace(/^['"](.*)['"]$/, '$1');
21
- metadata[key] = value;
22
- }
23
- });
6
+ const prompts = require('prompts');
7
+ const { agentDirectoryExists, addDependency, linkAgentFile, updateLibraryReadme } = require('../utils/fileSystem');
8
+
9
+ async function checkExists(p) {
10
+ try {
11
+ await fs.access(p);
12
+ return true;
13
+ } catch {
14
+ return false;
24
15
  }
25
- return { metadata, rawMatch: match ? match[0] : '' };
26
16
  }
27
17
 
28
18
  async function runInstall(packageName) {
29
19
  const cwd = process.cwd();
30
20
 
31
21
  console.log('');
32
- console.log(chalk.bold.cyan(' 🤖 agentive') + chalk.gray(' — Installing Package'));
22
+ console.log(chalk.bold.cyan(' Agentive') + chalk.gray(' — Installing Library Package'));
33
23
  console.log(chalk.gray(' ─────────────────────────────────────────────'));
34
24
  console.log('');
35
25
 
@@ -46,54 +36,134 @@ async function runInstall(packageName) {
46
36
 
47
37
  console.log(chalk.gray(` Fetching ${packageName}...`));
48
38
 
49
- let fileContent = '';
39
+ let infoContent = '';
40
+ let typesContent = '';
41
+ let rulesContent = '';
42
+ let combinedContent = '';
43
+ let name = packageName;
44
+ let version = '1.0.0';
45
+ let description = `A library package for ${packageName}`;
46
+
50
47
  try {
51
- let url = packageName;
52
- if (!url.startsWith('http')) {
53
- // Mock registry URL for demonstration
54
- url = `https://raw.githubusercontent.com/TiPS0/agentive-registry/main/packages/${packageName}.md`;
48
+ const response = await fetch(`https://registry.npmjs.org/${packageName}`);
49
+ if (!response.ok) {
50
+ throw new Error(`Failed to fetch from NPM registry (Status: ${response.status})`);
55
51
  }
52
+ const npmData = await response.json();
56
53
 
57
- // Attempt fetch
58
- const response = await fetch(url);
59
- if (!response.ok) {
60
- throw new Error(`Failed to fetch from registry (Status: ${response.status})`);
54
+ version = npmData['dist-tags']?.latest || '1.0.0';
55
+ description = npmData.description || description;
56
+ name = npmData.name || packageName;
57
+ const homepage = npmData.homepage || npmData.repository?.url || `https://www.npmjs.com/package/${packageName}`;
58
+ let readmeContent = npmData.readme || '';
59
+
60
+ if (!readmeContent) {
61
+ try {
62
+ const unpkgResponse = await fetch(`https://unpkg.com/${packageName}/README.md`);
63
+ if (unpkgResponse.ok) {
64
+ readmeContent = await unpkgResponse.text();
65
+ }
66
+ } catch (err) {}
61
67
  }
62
- fileContent = await response.text();
68
+
69
+ try {
70
+ const typesResponse = await fetch(`https://unpkg.com/${packageName}/index.d.ts`);
71
+ if (typesResponse.ok) {
72
+ typesContent = await typesResponse.text();
73
+ }
74
+ } catch (err) {}
75
+
76
+ infoContent = `# ${name} Reference\n\n## Installation Summary\n\`\`\`bash\nnpm install ${name}\n\`\`\`\n\n## Overview\n${description}\n\nFor more information, visit [${name}](${homepage}).\n\n${readmeContent ? `## Documentation\n\n${readmeContent}` : ''}`;
77
+
78
+ const rulesObj = {
79
+ library: name,
80
+ constraints: [
81
+ `Follow standard practices for ${name}.`,
82
+ `Check official documentation for latest patterns.`
83
+ ]
84
+ };
85
+ rulesContent = JSON.stringify(rulesObj, null, 2);
86
+
87
+ combinedContent = `${infoContent}\n\n## Types\n\`\`\`typescript\n${typesContent || '// No types found'}\n\`\`\`\n\n## Rules\n\`\`\`json\n${rulesContent}\n\`\`\``;
88
+
63
89
  } catch (err) {
64
- console.log(chalk.yellow(' ') + `Fetch failed: ${err.message}`);
65
- console.log(chalk.gray(' Creating a mock package for demonstration...'));
66
- fileContent = `---
67
- name: ${packageName}
68
- type: skill
69
- version: 1.0.0
70
- description: A mock skill for ${packageName}
71
- ---
72
- # ${packageName}
73
- This is a generated placeholder skill because the registry could not be reached.
74
- `;
90
+ console.log(chalk.red(' ') + `Fetch failed: ${err.message}`);
91
+ console.log(chalk.gray(' Skipping installation. Please check the package name and try again.'));
92
+ process.exit(1);
75
93
  }
76
94
 
77
- const { metadata } = parseFrontmatter(fileContent);
78
- const type = metadata.type || 'skill';
79
- const name = metadata.name || packageName;
80
- const version = metadata.version || '1.0.0';
81
-
82
- // Pluralize type for folder name (skill -> skills, rule -> rules)
83
- const folderName = type.endsWith('s') ? type : `${type}s`;
84
95
  const agentsDir = path.join(cwd, '.agents');
85
- const targetFolder = path.join(agentsDir, folderName);
86
- const targetFile = path.join(targetFolder, `${packageName}.md`);
96
+ const targetFolder = path.join(agentsDir, 'library', packageName);
97
+ const relativePath = `.agents/library/${packageName}/info.md`;
87
98
 
99
+ const choices = [
100
+ { title: '.agents/library (Agentive Knowledge)', value: 'agents', selected: true }
101
+ ];
102
+
103
+ if (await checkExists(path.join(cwd, '.cursor'))) {
104
+ choices.push({ title: '.cursor/rules (Cursor Editor)', value: 'cursor' });
105
+ }
106
+ if (await checkExists(path.join(cwd, '.windsurfrules'))) {
107
+ choices.push({ title: '.windsurfrules (Windsurf Editor)', value: 'windsurf' });
108
+ }
109
+ if (await checkExists(path.join(cwd, '.claude.md'))) {
110
+ choices.push({ title: '.claude.md (Claude AI)', value: 'claude' });
111
+ }
112
+
113
+ let selectedDestinations = ['agents'];
114
+ if (choices.length > 1) {
115
+ const { dests } = await prompts({
116
+ type: 'multiselect',
117
+ name: 'dests',
118
+ message: 'Where would you like to install this library?',
119
+ instructions: '\n↑/↓: Move ◦ ←/→ or [Space]: Toggle ◦ A: Toggle all ◦ ↵ Submit',
120
+ choices,
121
+ min: 1
122
+ });
123
+ if (dests && dests.length > 0) {
124
+ selectedDestinations = dests;
125
+ }
126
+ }
127
+
88
128
  try {
89
- await fs.mkdir(targetFolder, { recursive: true });
90
- await fs.writeFile(targetFile, fileContent, 'utf-8');
91
-
92
- await addDependency(agentsDir, packageName, version, type);
93
- await linkAgentFile(cwd, `.agents/${folderName}/${packageName}.md`, `[${type.toUpperCase()}] ${name}`);
94
-
95
- console.log(chalk.green(' ✔ ') + `Successfully installed ${chalk.cyan(name)} v${version}`);
96
- console.log(chalk.gray(` Location: .agents/${folderName}/${packageName}.md`));
129
+ if (selectedDestinations.includes('agents')) {
130
+ await fs.mkdir(targetFolder, { recursive: true });
131
+ await fs.writeFile(path.join(targetFolder, 'info.md'), infoContent, 'utf-8');
132
+ await fs.writeFile(path.join(targetFolder, 'types.d.ts'), typesContent, 'utf-8');
133
+ await fs.writeFile(path.join(targetFolder, 'rules.json'), rulesContent, 'utf-8');
134
+
135
+ await addDependency(agentsDir, packageName, version, 'library');
136
+ await linkAgentFile(cwd, relativePath, `[LIBRARY] ${name}`, '## Installed Libraries');
137
+ await updateLibraryReadme(agentsDir, packageName, description, version);
138
+
139
+ console.log(chalk.green(' ✔ ') + `Successfully installed ${chalk.cyan(name)} v${version} to .agents/library/${packageName}/`);
140
+ }
141
+
142
+ if (selectedDestinations.includes('cursor')) {
143
+ const cursorRulesDir = path.join(cwd, '.cursor', 'rules');
144
+ await fs.mkdir(cursorRulesDir, { recursive: true });
145
+ const cursorFile = path.join(cursorRulesDir, `${packageName}.mdc`);
146
+ await fs.writeFile(cursorFile, combinedContent, 'utf-8');
147
+ console.log(chalk.green(' ✔ ') + `Successfully installed ${chalk.cyan(name)} to .cursor/rules/${packageName}.mdc`);
148
+ }
149
+
150
+ if (selectedDestinations.includes('windsurf')) {
151
+ const windsurfFile = path.join(cwd, '.windsurfrules');
152
+ let existing = '';
153
+ try { existing = await fs.readFile(windsurfFile, 'utf-8'); } catch(e) {}
154
+ const newContent = existing + `\n\n# Library: ${name}\n\n` + combinedContent;
155
+ await fs.writeFile(windsurfFile, newContent, 'utf-8');
156
+ console.log(chalk.green(' ✔ ') + `Successfully appended ${chalk.cyan(name)} to .windsurfrules`);
157
+ }
158
+
159
+ if (selectedDestinations.includes('claude')) {
160
+ const claudeFile = path.join(cwd, '.claude.md');
161
+ let existing = '';
162
+ try { existing = await fs.readFile(claudeFile, 'utf-8'); } catch(e) {}
163
+ const newContent = existing + `\n\n# Library: ${name}\n\n` + combinedContent;
164
+ await fs.writeFile(claudeFile, newContent, 'utf-8');
165
+ console.log(chalk.green(' ✔ ') + `Successfully appended ${chalk.cyan(name)} to .claude.md`);
166
+ }
97
167
  } catch (err) {
98
168
  console.log(chalk.red(' ✖ ') + `Failed to write file: ${err.message}`);
99
169
  process.exit(1);
@@ -3,13 +3,13 @@
3
3
  const path = require('path');
4
4
  const chalk = require('chalk');
5
5
  const fs = require('fs/promises');
6
- const { agentDirectoryExists, removeDependency, readSettings, unlinkAgentFile } = require('../utils/fileSystem');
6
+ const { agentDirectoryExists, removeDependency, readSettings, unlinkAgentFile, removeSkillFromReadme, removeLibraryFromReadme } = require('../utils/fileSystem');
7
7
 
8
8
  async function runRemove(packageName) {
9
9
  const cwd = process.cwd();
10
10
 
11
11
  console.log('');
12
- console.log(chalk.bold.cyan(' 🤖 agentive') + chalk.gray(' — Removing Package'));
12
+ console.log(chalk.bold.cyan(' Agentive') + chalk.gray(' — Removing Package'));
13
13
  console.log(chalk.gray(' ─────────────────────────────────────────────'));
14
14
  console.log('');
15
15
 
@@ -38,14 +38,29 @@ async function runRemove(packageName) {
38
38
  name = settings.dependencies[packageName].name || packageName;
39
39
  }
40
40
 
41
- const folderName = type.endsWith('s') ? type : `${type}s`;
42
- const targetFile = path.join(agentsDir, folderName, `${packageName}.md`);
41
+ const folderName = type === 'library' ? 'library' : (type.endsWith('s') ? type : `${type}s`);
42
+ const oldTargetFile = path.join(agentsDir, folderName, `${packageName}.md`);
43
+ const newTargetFolder = path.join(agentsDir, folderName, packageName);
43
44
 
44
45
  try {
45
- await fs.rm(targetFile, { force: true });
46
+ await fs.rm(oldTargetFile, { force: true });
47
+ await fs.rm(newTargetFolder, { recursive: true, force: true });
48
+
49
+ // Also try to remove cursor file if it exists
50
+ const cursorFile = path.join(cwd, '.cursor', 'rules', `${packageName}.mdc`);
51
+ await fs.rm(cursorFile, { force: true }).catch(() => {});
52
+
46
53
  const removedFromSettings = await removeDependency(agentsDir, packageName);
54
+
47
55
  await unlinkAgentFile(cwd, `.agents/${folderName}/${packageName}.md`, `[${type.toUpperCase()}] ${name}`);
48
56
 
57
+ if (type === 'skill') {
58
+ await unlinkAgentFile(cwd, `.agents/skills/${packageName}/SKILL.md`, `[${type.toUpperCase()}] ${name}`);
59
+ await removeSkillFromReadme(agentsDir, packageName);
60
+ } else if (type === 'library') {
61
+ await unlinkAgentFile(cwd, `.agents/library/${packageName}/info.md`, `[LIBRARY] ${name}`);
62
+ await removeLibraryFromReadme(agentsDir, packageName);
63
+ }
49
64
  if (removedFromSettings) {
50
65
  console.log(chalk.green(' ✔ ') + `Successfully removed ${chalk.cyan(packageName)}`);
51
66
  } else {
@@ -17,6 +17,7 @@ All agent configuration lives inside the `.agents/` directory:
17
17
  | `.agents/settings.local.json` | Local machine overrides (gitignored) |
18
18
  | `.agents/commands/` | Reusable prompt commands your agents can execute |
19
19
  | `.agents/skills/` | Skill definitions that teach agents how to behave |
20
+ | `.agents/library/` | Passive library documentation and API references |
20
21
  | `.agents/rules/` | Project-wide rules all agents must follow |
21
22
 
22
23
  ## Context Management
@@ -29,6 +30,7 @@ Additionally, you must also read and respect any rules defined in `.gitignore`,
29
30
  1. Edit the markdown files inside `.agents/` to customise agent behaviour
30
31
  2. Add new commands in `.agents/commands/` for common tasks
31
32
  3. Add new skills in `.agents/skills/` to teach agents new capabilities
32
- 4. Add new rules in `.agents/rules/` to enforce coding standards
33
+ 4. Install NPM packages to `.agents/library/` via `npx agentive install <pkg>`
34
+ 5. Add new rules in `.agents/rules/` to enforce coding standards
33
35
 
34
36
  Re-run `npx @p_tipso/agentive` any time to re-sync changes to your AI tools.
@@ -0,0 +1,17 @@
1
+ # Library Directory
2
+
3
+ This directory contains passive AI agent references for your NPM packages and libraries.
4
+
5
+ Unlike **skills** (which are active behaviors and workflows), **libraries** are optimized references (`info.md`, `types.d.ts`, `rules.json`) that teach your AI agents how to correctly write code for specific packages without hallucinating or reading outdated syntax.
6
+
7
+ ## How to Install Libraries
8
+
9
+ Use the `agentive` CLI to automatically fetch documentation and types for a package:
10
+
11
+ ```bash
12
+ npx agentive install <pkg>
13
+ ```
14
+
15
+ This will automatically create `.agents/library/<pkg>/` and update this file with the new reference.
16
+
17
+ ## Installed Libraries
@@ -31,6 +31,7 @@ async function agentDirectoryExists(cwd) {
31
31
  * ├── settings.local.json
32
32
  * ├── commands/
33
33
  * ├── skills/
34
+ * ├── library/
34
35
  * └── rules/
35
36
  *
36
37
  * @param {string} cwd - The user's current working directory
@@ -47,6 +48,7 @@ async function createAgentDirectory(cwd, overwrite = false) {
47
48
  await fs.mkdir(agentsDir, { recursive: true });
48
49
  await fs.mkdir(path.join(agentsDir, 'commands'), { recursive: true });
49
50
  await fs.mkdir(path.join(agentsDir, 'skills'), { recursive: true });
51
+ await fs.mkdir(path.join(agentsDir, 'library'), { recursive: true });
50
52
  await fs.mkdir(path.join(agentsDir, 'rules'), { recursive: true });
51
53
 
52
54
  return agentsDir;
@@ -152,7 +154,7 @@ async function copyTemplates(templatesDir, agentsDir, projectName, projectType =
152
154
  await fs.writeFile(path.join(cwd, '.aiignore'), content, 'utf-8');
153
155
  } catch { /* template may not exist */ }
154
156
 
155
- const foldersToInclude = ['commands', 'skills', 'rules'];
157
+ const foldersToInclude = ['commands', 'skills', 'rules', 'library'];
156
158
 
157
159
  // Helper to copy a specific template layer
158
160
  const copyLayer = async (layerPath) => {
@@ -266,13 +268,19 @@ async function removeDependency(agentsDir, packageName) {
266
268
  /**
267
269
  * Link a downloaded agent file into AGENTS.md
268
270
  */
269
- async function linkAgentFile(cwd, relativePath, title) {
271
+ async function linkAgentFile(cwd, relativePath, title, heading = '## Installed Skills') {
270
272
  const agentMdPath = path.join(cwd, 'AGENTS.md');
273
+ const linkStr = `- [${title}](${relativePath})`;
274
+
271
275
  try {
272
276
  let content = await fs.readFile(agentMdPath, 'utf-8');
273
- const linkStr = `- [${title}](${relativePath})`;
277
+
274
278
  if (!content.includes(linkStr)) {
275
- content += `\n${linkStr}\n`;
279
+ if (content.includes(heading)) {
280
+ content = content.replace(heading, `${heading}\n${linkStr}`);
281
+ } else {
282
+ content = content.trimEnd() + `\n\n${heading}\n${linkStr}\n`;
283
+ }
276
284
  await fs.writeFile(agentMdPath, content, 'utf-8');
277
285
  }
278
286
  } catch (err) {
@@ -301,6 +309,88 @@ function escapeRegex(string) {
301
309
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
302
310
  }
303
311
 
312
+ /**
313
+ * Update the .agents/skills/README.md
314
+ */
315
+ async function updateSkillsReadme(agentsDir, packageName, description, version) {
316
+ const readmePath = path.join(agentsDir, 'skills', 'README.md');
317
+ const heading = '## Installed Skills';
318
+ const linkStr = `- **[${packageName}](./${packageName}/SKILL.md)** (v${version}): ${description || 'No description available.'}`;
319
+
320
+ try {
321
+ let content = await fs.readFile(readmePath, 'utf-8');
322
+
323
+ if (!content.includes(linkStr)) {
324
+ if (content.includes(heading)) {
325
+ content = content.replace(heading, `${heading}\n${linkStr}`);
326
+ } else {
327
+ content = content.trimEnd() + `\n\n${heading}\n${linkStr}\n`;
328
+ }
329
+ await fs.writeFile(readmePath, content, 'utf-8');
330
+ }
331
+ } catch (err) {
332
+ // README doesn't exist, create it
333
+ const content = `# Skills Directory\n\nThis directory contains AI agent skills.\n\n${heading}\n${linkStr}\n`;
334
+ await fs.writeFile(readmePath, content, 'utf-8');
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Remove a skill from .agents/skills/README.md
340
+ */
341
+ async function removeSkillFromReadme(agentsDir, packageName) {
342
+ const readmePath = path.join(agentsDir, 'skills', 'README.md');
343
+ try {
344
+ let content = await fs.readFile(readmePath, 'utf-8');
345
+ const lines = content.split('\n');
346
+ const newLines = lines.filter(line => !line.includes(`[${packageName}](./${packageName}/SKILL.md)`));
347
+ await fs.writeFile(readmePath, newLines.join('\n'), 'utf-8');
348
+ } catch (err) {
349
+ // If it doesn't exist, do nothing
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Update the .agents/library/README.md
355
+ */
356
+ async function updateLibraryReadme(agentsDir, packageName, description, version) {
357
+ const readmePath = path.join(agentsDir, 'library', 'README.md');
358
+ const heading = '## Installed Libraries';
359
+ const linkStr = `- **[${packageName}](./${packageName}/info.md)** (v${version}): ${description || 'No description available.'}`;
360
+
361
+ try {
362
+ let content = await fs.readFile(readmePath, 'utf-8');
363
+
364
+ if (!content.includes(linkStr)) {
365
+ if (content.includes(heading)) {
366
+ content = content.replace(heading, `${heading}\n${linkStr}`);
367
+ } else {
368
+ content = content.trimEnd() + `\n\n${heading}\n${linkStr}\n`;
369
+ }
370
+ await fs.writeFile(readmePath, content, 'utf-8');
371
+ }
372
+ } catch (err) {
373
+ await fs.mkdir(path.dirname(readmePath), { recursive: true });
374
+ const content = `# Library Directory\n\nThis directory contains AI agent passive library references.\n\n${heading}\n${linkStr}\n`;
375
+ await fs.writeFile(readmePath, content, 'utf-8');
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Remove a library from .agents/library/README.md
381
+ */
382
+ async function removeLibraryFromReadme(agentsDir, packageName) {
383
+ const readmePath = path.join(agentsDir, 'library', 'README.md');
384
+ try {
385
+ let content = await fs.readFile(readmePath, 'utf-8');
386
+ const lines = content.split('\n');
387
+ const newLines = lines.filter(line => !line.includes(`[${packageName}](./${packageName}/info.md)`));
388
+ await fs.writeFile(readmePath, newLines.join('\n'), 'utf-8');
389
+ } catch (err) {
390
+ // If it doesn't exist, do nothing
391
+ }
392
+ }
393
+
304
394
  module.exports = {
305
395
  agentDirectoryExists,
306
396
  createAgentDirectory,
@@ -312,4 +402,8 @@ module.exports = {
312
402
  removeDependency,
313
403
  linkAgentFile,
314
404
  unlinkAgentFile,
405
+ updateSkillsReadme,
406
+ removeSkillFromReadme,
407
+ updateLibraryReadme,
408
+ removeLibraryFromReadme,
315
409
  };