@p_tipso/agentive 1.1.1 → 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
 
@@ -103,13 +111,7 @@ Please read our [Contributing Guide](CONTRIBUTING.md) to get started with settin
103
111
 
104
112
  ## ⭐ Star History
105
113
 
106
- <a href="https://star-history.com/#TiPS0/agentive&Date">
107
- <picture>
108
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=TiPS0/agentive&type=Date&theme=dark" />
109
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=TiPS0/agentive&type=Date" />
110
- <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=TiPS0/agentive&type=Date" />
111
- </picture>
112
- </a>
114
+ [![Star History Chart](https://raw.githubusercontent.com/TiPS0/agentive/star-tracker-data/charts/star-history.svg)](https://star-history.com/#TiPS0/agentive&Date)
113
115
 
114
116
  ---
115
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p_tipso/agentive",
3
- "version": "1.1.1",
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
@@ -42,13 +43,12 @@ async function createAgentDirectory(cwd, overwrite = false) {
42
43
 
43
44
  if (overwrite) {
44
45
  await fs.rm(agentsDir, { recursive: true, force: true });
45
- // Also remove old AGENTS.md if overwriting
46
- try { await fs.rm(path.join(cwd, 'AGENTS.md'), { force: true }); } catch { /* noop */ }
47
46
  }
48
47
 
49
48
  await fs.mkdir(agentsDir, { recursive: true });
50
49
  await fs.mkdir(path.join(agentsDir, 'commands'), { recursive: true });
51
50
  await fs.mkdir(path.join(agentsDir, 'skills'), { recursive: true });
51
+ await fs.mkdir(path.join(agentsDir, 'library'), { recursive: true });
52
52
  await fs.mkdir(path.join(agentsDir, 'rules'), { recursive: true });
53
53
 
54
54
  return agentsDir;
@@ -130,10 +130,21 @@ async function copyTemplates(templatesDir, agentsDir, projectName, projectType =
130
130
 
131
131
  // 1. Copy AGENTS.md from base to project root
132
132
  const agentMdSrc = path.join(templatesDir, 'base', 'AGENTS.md');
133
+ const targetAgentMdPath = path.join(cwd, 'AGENTS.md');
133
134
  try {
134
135
  let content = await fs.readFile(agentMdSrc, 'utf-8');
135
136
  content = replacePlaceholders(content, projectName);
136
- await fs.writeFile(path.join(cwd, 'AGENTS.md'), content, 'utf-8');
137
+
138
+ let finalContent = content;
139
+ try {
140
+ const existingContent = await fs.readFile(targetAgentMdPath, 'utf-8');
141
+ if (existingContent.trim()) {
142
+ // Prepend the new template content to the existing content
143
+ finalContent = content + '\n\n' + existingContent;
144
+ }
145
+ } catch { /* target may not exist, that's fine */ }
146
+
147
+ await fs.writeFile(targetAgentMdPath, finalContent, 'utf-8');
137
148
  } catch { /* template may not exist */ }
138
149
 
139
150
  // 1.5 Copy aiignore from base to project root as .aiignore
@@ -143,7 +154,7 @@ async function copyTemplates(templatesDir, agentsDir, projectName, projectType =
143
154
  await fs.writeFile(path.join(cwd, '.aiignore'), content, 'utf-8');
144
155
  } catch { /* template may not exist */ }
145
156
 
146
- const foldersToInclude = ['commands', 'skills', 'rules'];
157
+ const foldersToInclude = ['commands', 'skills', 'rules', 'library'];
147
158
 
148
159
  // Helper to copy a specific template layer
149
160
  const copyLayer = async (layerPath) => {
@@ -257,13 +268,19 @@ async function removeDependency(agentsDir, packageName) {
257
268
  /**
258
269
  * Link a downloaded agent file into AGENTS.md
259
270
  */
260
- async function linkAgentFile(cwd, relativePath, title) {
271
+ async function linkAgentFile(cwd, relativePath, title, heading = '## Installed Skills') {
261
272
  const agentMdPath = path.join(cwd, 'AGENTS.md');
273
+ const linkStr = `- [${title}](${relativePath})`;
274
+
262
275
  try {
263
276
  let content = await fs.readFile(agentMdPath, 'utf-8');
264
- const linkStr = `- [${title}](${relativePath})`;
277
+
265
278
  if (!content.includes(linkStr)) {
266
- 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
+ }
267
284
  await fs.writeFile(agentMdPath, content, 'utf-8');
268
285
  }
269
286
  } catch (err) {
@@ -292,6 +309,88 @@ function escapeRegex(string) {
292
309
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
293
310
  }
294
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
+
295
394
  module.exports = {
296
395
  agentDirectoryExists,
297
396
  createAgentDirectory,
@@ -303,4 +402,8 @@ module.exports = {
303
402
  removeDependency,
304
403
  linkAgentFile,
305
404
  unlinkAgentFile,
405
+ updateSkillsReadme,
406
+ removeSkillFromReadme,
407
+ updateLibraryReadme,
408
+ removeLibraryFromReadme,
306
409
  };