@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 +26 -19
- 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 +98 -4
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
|
|
|
@@ -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
|
-
[](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.
|
|
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
|
|
@@ -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
|
-
|
|
277
|
+
|
|
274
278
|
if (!content.includes(linkStr)) {
|
|
275
|
-
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
|
+
}
|
|
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
|
};
|