@p_tipso/agentive 1.0.3 → 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
@@ -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
 
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@p_tipso/agentive",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
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",
@@ -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 };
@@ -203,10 +203,104 @@ async function copyDirectoryRecursive(src, dest, projectName) {
203
203
  }
204
204
  }
205
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
+ }
206
294
 
207
295
  module.exports = {
208
296
  agentDirectoryExists,
209
297
  createAgentDirectory,
210
298
  writeSettings,
211
299
  copyTemplates,
300
+ readSettings,
301
+ updateSettings,
302
+ addDependency,
303
+ removeDependency,
304
+ linkAgentFile,
305
+ unlinkAgentFile,
212
306
  };