@p_tipso/agentive 1.0.2 → 1.1.0

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,16 +20,16 @@
20
20
 
21
21
  ---
22
22
 
23
- Stop maintaining separate rule files for every AI tool. **agentive** scaffolds a universal `.agents/` directory and an `AGENTS.md` file in your project — providing a **single source of truth** for all your agent commands, skills, and rules.
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.
24
24
 
25
25
  ## ✨ Why Agentive?
26
26
 
27
27
  | Feature | Description |
28
28
  | :---------------------------- | :------------------------------------------------------------------- |
29
- | 🚀 **Zero Config** | Run one command and get a fully structured workspace instantly. |
29
+ | 🚀 **Interactive Setup** | Select your environment (General, Expo, React Native) to get tailored rules. |
30
30
  | 🌍 **Universal** | Framework-agnostic setup. Works with React, Python, Go, you name it. |
31
31
  | 🧠 **Single Source of Truth** | Centralize skills and rules for _all_ your AI agents in one place. |
32
- | ⚡ **No Prompts** | Just run it. No annoying interactive setup wizards to slow you down. |
32
+ | ⚡ **Dynamic Layering** | Scaffolds base rules and safely merges framework-specific guardrails. |
33
33
 
34
34
  ---
35
35
 
@@ -52,7 +52,9 @@ agentive
52
52
 
53
53
  | Command | Description |
54
54
  | :-------------------------------- | :----------------------------------------------------------- |
55
- | `npx @p_tipso/agentive` | Scaffold `.agents/` workspace instantly in current directory |
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`) |
56
58
  | `npx @p_tipso/agentive --version` | Print the current CLI version |
57
59
  | `npx @p_tipso/agentive --help` | Show available commands and options |
58
60
 
@@ -60,11 +62,12 @@ agentive
60
62
 
61
63
  ## 🏗 What Happens Under the Hood?
62
64
 
63
- When you run `agentive`, it detects your current directory and instantly scaffolds this structure:
65
+ When you run `agentive`, it launches an interactive wizard asking about your project environment (e.g., General, Mobile > Expo, React Native). It then intelligently scaffolds a tailored workspace:
64
66
 
65
67
  ```text
66
68
  your-project/
67
69
  ├── AGENTS.md ← Root agent instructions
70
+ ├── .aiignore ← Hides irrelevant files from AI to save tokens
68
71
  ├── .agents/
69
72
  │ ├── settings.json ← Project config
70
73
  │ ├── settings.local.json ← Local machine overrides (auto-gitignored)
@@ -78,12 +81,16 @@ your-project/
78
81
  │ └── README.md ← Guide: how to add rules
79
82
  ```
80
83
 
81
- ### 📂 Folder Guide
84
+ ### 📂 File & Folder Guide
82
85
 
86
+ - **`.aiignore`**: Prevents context pollution and saves tokens by hiding files (like `node_modules` or build outputs) from your AI agents.
83
87
  - **`commands/`**: Reusable prompt instructions that agents can execute on demand (e.g. `review.md`).
84
88
  - **`skills/`**: Skill definitions that teach agents how to behave in specific roles.
85
89
  - **`rules/`**: Project-wide rules that all agents must follow strictly.
86
90
 
91
+ ### 🌟 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.
93
+
87
94
  ---
88
95
 
89
96
  ## 🤝 Contributing
package/bin/index.js CHANGED
@@ -20,4 +20,22 @@ program
20
20
  await runInit();
21
21
  });
22
22
 
23
+ program
24
+ .command('install <package>')
25
+ .alias('add')
26
+ .description('Install a skill or rule from the registry')
27
+ .action(async (packageName) => {
28
+ const { runInstall } = require('../src/commands/install');
29
+ await runInstall(packageName);
30
+ });
31
+
32
+ program
33
+ .command('remove <package>')
34
+ .alias('rm')
35
+ .description('Remove an installed skill or rule')
36
+ .action(async (packageName) => {
37
+ const { runRemove } = require('../src/commands/remove');
38
+ await runRemove(packageName);
39
+ });
40
+
23
41
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@p_tipso/agentive",
3
- "version": "1.0.2",
4
- "description": "A universal, framework-agnostic AI agent repository setup CLI. Scaffold a .agent/ workspace and sync to Cursor, Claude, and Windsurf.",
3
+ "version": "1.1.0",
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",
7
7
  "type": "commonjs",
@@ -27,7 +27,8 @@
27
27
  "scaffold",
28
28
  "init",
29
29
  "rules",
30
- "context"
30
+ "context",
31
+ "aiignore"
31
32
  ],
32
33
  "repository": {
33
34
  "type": "git",
@@ -57,7 +57,7 @@ async function runInit() {
57
57
  name: 'framework',
58
58
  message: 'Which framework are you using?',
59
59
  choices: [
60
- { title: 'Expo', value: 'expo' },
60
+ { title: 'Expo (Recommended)', value: 'expo' },
61
61
  { title: 'React Native', value: 'react-native' },
62
62
  ],
63
63
  initial: 0,
@@ -88,6 +88,7 @@ async function runInit() {
88
88
  });
89
89
 
90
90
  console.log(chalk.green(' ✔ ') + chalk.white('Created ') + chalk.cyan('AGENTS.md'));
91
+ console.log(chalk.green(' ✔ ') + chalk.white('Created ') + chalk.cyan('.aiignore'));
91
92
  console.log(chalk.green(' ✔ ') + chalk.white('Scaffolded ') + chalk.cyan('.agents/') + chalk.white(' workspace'));
92
93
 
93
94
  // --- Done ---
@@ -97,6 +98,7 @@ async function runInit() {
97
98
  console.log(chalk.white(' Your AI workspace is ready:'));
98
99
  console.log('');
99
100
  console.log(chalk.gray(' AGENTS.md ← root agent instructions'));
101
+ console.log(chalk.gray(' .aiignore ← hides files from AI to save tokens'));
100
102
  console.log(chalk.gray(' .agents/'));
101
103
  console.log(chalk.gray(' ├── settings.json ← project config'));
102
104
  console.log(chalk.gray(' ├── settings.local.json ← local overrides (gitignored)'));
@@ -0,0 +1,104 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const chalk = require('chalk');
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
+ });
24
+ }
25
+ return { metadata, rawMatch: match ? match[0] : '' };
26
+ }
27
+
28
+ async function runInstall(packageName) {
29
+ const cwd = process.cwd();
30
+
31
+ console.log('');
32
+ console.log(chalk.bold.cyan(' 🤖 agentive') + chalk.gray(' — Installing Package'));
33
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
34
+ console.log('');
35
+
36
+ const exists = await agentDirectoryExists(cwd);
37
+ if (!exists) {
38
+ console.log(chalk.red(' ✖ ') + 'No .agents/ directory found. Run `npx agentive init` first.');
39
+ process.exit(1);
40
+ }
41
+
42
+ if (!packageName) {
43
+ console.log(chalk.red(' ✖ ') + 'Please specify a package to install.');
44
+ process.exit(1);
45
+ }
46
+
47
+ console.log(chalk.gray(` Fetching ${packageName}...`));
48
+
49
+ let fileContent = '';
50
+ 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`;
55
+ }
56
+
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})`);
61
+ }
62
+ fileContent = await response.text();
63
+ } 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
+ `;
75
+ }
76
+
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
+ const agentsDir = path.join(cwd, '.agents');
85
+ const targetFolder = path.join(agentsDir, folderName);
86
+ const targetFile = path.join(targetFolder, `${packageName}.md`);
87
+
88
+ 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`));
97
+ } catch (err) {
98
+ console.log(chalk.red(' ✖ ') + `Failed to write file: ${err.message}`);
99
+ process.exit(1);
100
+ }
101
+ console.log('');
102
+ }
103
+
104
+ module.exports = { runInstall };
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const chalk = require('chalk');
5
+ const fs = require('fs/promises');
6
+ const { agentDirectoryExists, removeDependency, readSettings, unlinkAgentFile } = require('../utils/fileSystem');
7
+
8
+ async function runRemove(packageName) {
9
+ const cwd = process.cwd();
10
+
11
+ console.log('');
12
+ console.log(chalk.bold.cyan(' 🤖 agentive') + chalk.gray(' — Removing Package'));
13
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
14
+ console.log('');
15
+
16
+ const exists = await agentDirectoryExists(cwd);
17
+ if (!exists) {
18
+ console.log(chalk.red(' ✖ ') + 'No .agents/ directory found.');
19
+ process.exit(1);
20
+ }
21
+
22
+ if (!packageName) {
23
+ console.log(chalk.red(' ✖ ') + 'Please specify a package to remove.');
24
+ process.exit(1);
25
+ }
26
+
27
+ const agentsDir = path.join(cwd, '.agents');
28
+ const settings = await readSettings(agentsDir);
29
+
30
+ if (!settings || !settings.dependencies || !settings.dependencies[packageName]) {
31
+ console.log(chalk.yellow(' ⚠ ') + `Package '${packageName}' is not installed in settings.json.`);
32
+ }
33
+
34
+ let type = 'skill';
35
+ let name = packageName;
36
+ if (settings && settings.dependencies && settings.dependencies[packageName]) {
37
+ type = settings.dependencies[packageName].type || 'skill';
38
+ name = settings.dependencies[packageName].name || packageName;
39
+ }
40
+
41
+ const folderName = type.endsWith('s') ? type : `${type}s`;
42
+ const targetFile = path.join(agentsDir, folderName, `${packageName}.md`);
43
+
44
+ try {
45
+ await fs.rm(targetFile, { force: true });
46
+ const removedFromSettings = await removeDependency(agentsDir, packageName);
47
+ await unlinkAgentFile(cwd, `.agents/${folderName}/${packageName}.md`, `[${type.toUpperCase()}] ${name}`);
48
+
49
+ if (removedFromSettings) {
50
+ console.log(chalk.green(' ✔ ') + `Successfully removed ${chalk.cyan(packageName)}`);
51
+ } else {
52
+ console.log(chalk.green(' ✔ ') + `Deleted file for ${chalk.cyan(packageName)}`);
53
+ }
54
+ } catch (err) {
55
+ console.log(chalk.red(' ✖ ') + `Failed to remove: ${err.message}`);
56
+ }
57
+ console.log('');
58
+ }
59
+
60
+ module.exports = { runRemove };
@@ -19,6 +19,11 @@ All agent configuration lives inside the `.agents/` directory:
19
19
  | `.agents/skills/` | Skill definitions that teach agents how to behave |
20
20
  | `.agents/rules/` | Project-wide rules all agents must follow |
21
21
 
22
+ ## Context Management
23
+
24
+ When exploring, reading, or searching this repository, you **MUST** respect the exclusion rules defined in `.aiignore`.
25
+ Additionally, you must also read and respect any rules defined in `.gitignore`, `.cursorignore`, or `.aiexclude` if they exist in the repository to prevent context pollution and save token usage.
26
+
22
27
  ## Getting Started
23
28
 
24
29
  1. Edit the markdown files inside `.agents/` to customise agent behaviour
@@ -0,0 +1,48 @@
1
+ # Standard AI Ignore file - prevents context pollution & saves tokens
2
+ # Learn more: https://github.com/TiPS0/agentive
3
+
4
+ # Dependencies
5
+ node_modules/
6
+ .pnp
7
+ .pnp.js
8
+ vendor/
9
+
10
+ # Environment variables
11
+ .env
12
+ .env.local
13
+ .env.development.local
14
+ .env.test.local
15
+ .env.production.local
16
+
17
+ # Build outputs & generated files
18
+ dist/
19
+ build/
20
+ out/
21
+ coverage/
22
+ .next/
23
+ .nuxt/
24
+ .vuepress/
25
+ .cache/
26
+
27
+ # Logs
28
+ logs
29
+ *.log
30
+ npm-debug.log*
31
+ yarn-debug.log*
32
+ yarn-error.log*
33
+ pnpm-debug.log*
34
+ lerna-debug.log*
35
+
36
+ # Version control & OS files
37
+ .git/
38
+ .DS_Store
39
+ *.pem
40
+
41
+ # Editor directories and files
42
+ .idea/
43
+ .vscode/
44
+ *.suo
45
+ *.ntvs*
46
+ *.njsproj
47
+ *.sln
48
+ *.sw?
@@ -136,6 +136,13 @@ async function copyTemplates(templatesDir, agentsDir, projectName, projectType =
136
136
  await fs.writeFile(path.join(cwd, 'AGENTS.md'), content, 'utf-8');
137
137
  } catch { /* template may not exist */ }
138
138
 
139
+ // 1.5 Copy aiignore from base to project root as .aiignore
140
+ const aiignoreSrc = path.join(templatesDir, 'base', 'aiignore');
141
+ try {
142
+ let content = await fs.readFile(aiignoreSrc, 'utf-8');
143
+ await fs.writeFile(path.join(cwd, '.aiignore'), content, 'utf-8');
144
+ } catch { /* template may not exist */ }
145
+
139
146
  const foldersToInclude = ['commands', 'skills', 'rules'];
140
147
 
141
148
  // Helper to copy a specific template layer
@@ -196,10 +203,104 @@ async function copyDirectoryRecursive(src, dest, projectName) {
196
203
  }
197
204
  }
198
205
  }
206
+ // ─── Package Manager Utilities ────────────────────────────────────────────────
207
+
208
+ /**
209
+ * Read the settings.json file.
210
+ */
211
+ async function readSettings(agentsDir) {
212
+ try {
213
+ const content = await fs.readFile(path.join(agentsDir, 'settings.json'), 'utf-8');
214
+ return JSON.parse(content);
215
+ } catch {
216
+ return null;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Update the settings.json file.
222
+ */
223
+ async function updateSettings(agentsDir, newSettings) {
224
+ await fs.writeFile(
225
+ path.join(agentsDir, 'settings.json'),
226
+ JSON.stringify(newSettings, null, 2) + '\n',
227
+ 'utf-8'
228
+ );
229
+ }
230
+
231
+ /**
232
+ * Add a dependency to settings.json
233
+ */
234
+ async function addDependency(agentsDir, packageName, version, type = 'skill') {
235
+ const settings = await readSettings(agentsDir);
236
+ if (!settings) return false;
237
+ if (!settings.dependencies) settings.dependencies = {};
238
+ settings.dependencies[packageName] = { version, type };
239
+ await updateSettings(agentsDir, settings);
240
+ return true;
241
+ }
242
+
243
+ /**
244
+ * Remove a dependency from settings.json
245
+ */
246
+ async function removeDependency(agentsDir, packageName) {
247
+ const settings = await readSettings(agentsDir);
248
+ if (!settings) return false;
249
+ if (settings.dependencies && settings.dependencies[packageName]) {
250
+ delete settings.dependencies[packageName];
251
+ await updateSettings(agentsDir, settings);
252
+ return true;
253
+ }
254
+ return false;
255
+ }
256
+
257
+ /**
258
+ * Link a downloaded agent file into AGENTS.md
259
+ */
260
+ async function linkAgentFile(cwd, relativePath, title) {
261
+ const agentMdPath = path.join(cwd, 'AGENTS.md');
262
+ try {
263
+ let content = await fs.readFile(agentMdPath, 'utf-8');
264
+ const linkStr = `- [${title}](${relativePath})`;
265
+ if (!content.includes(linkStr)) {
266
+ content += `\n${linkStr}\n`;
267
+ await fs.writeFile(agentMdPath, content, 'utf-8');
268
+ }
269
+ } catch (err) {
270
+ // AGENTS.md might not exist
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Unlink a removed agent file from AGENTS.md
276
+ */
277
+ async function unlinkAgentFile(cwd, relativePath, title) {
278
+ const agentMdPath = path.join(cwd, 'AGENTS.md');
279
+ try {
280
+ let content = await fs.readFile(agentMdPath, 'utf-8');
281
+ const linkStr = `- [${title}](${relativePath})`;
282
+ if (content.includes(linkStr)) {
283
+ content = content.replace(new RegExp(`\\n?${escapeRegex(linkStr)}\\n?`, 'g'), '\n');
284
+ await fs.writeFile(agentMdPath, content, 'utf-8');
285
+ }
286
+ } catch (err) {
287
+ // AGENTS.md might not exist
288
+ }
289
+ }
290
+
291
+ function escapeRegex(string) {
292
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
293
+ }
199
294
 
200
295
  module.exports = {
201
296
  agentDirectoryExists,
202
297
  createAgentDirectory,
203
298
  writeSettings,
204
299
  copyTemplates,
300
+ readSettings,
301
+ updateSettings,
302
+ addDependency,
303
+ removeDependency,
304
+ linkAgentFile,
305
+ unlinkAgentFile,
205
306
  };