@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 +26 -24
- package/package.json +1 -1
- package/src/commands/init.js +2 -1
- package/src/commands/install.js +128 -58
- package/src/commands/remove.js +20 -5
- package/src/templates/base/AGENTS.md +3 -1
- package/src/templates/base/library/README.md +17 -0
- package/src/utils/fileSystem.js +110 -7
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**
|
|
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**
|
|
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
|
|
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
|
|
54
|
-
|
|
|
55
|
-
| `npx @p_tipso/agentive init`
|
|
56
|
-
| `npx @p_tipso/agentive install <pkg
|
|
57
|
-
| `npx @p_tipso/agentive remove <pkg>`
|
|
58
|
-
| `npx @p_tipso/agentive --version`
|
|
59
|
-
| `npx @p_tipso/agentive --help`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
[](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.
|
|
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",
|
package/src/commands/init.js
CHANGED
|
@@ -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(
|
|
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.'));
|
package/src/commands/install.js
CHANGED
|
@@ -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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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('
|
|
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
|
|
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
|
-
|
|
52
|
-
if (!
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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.
|
|
65
|
-
console.log(chalk.gray('
|
|
66
|
-
|
|
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,
|
|
86
|
-
const
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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);
|
package/src/commands/remove.js
CHANGED
|
@@ -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('
|
|
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
|
|
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(
|
|
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.
|
|
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
|
package/src/utils/fileSystem.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
277
|
+
|
|
265
278
|
if (!content.includes(linkStr)) {
|
|
266
|
-
content
|
|
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
|
};
|