@meraki-digital/agent-init 1.0.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.
Files changed (3) hide show
  1. package/README.md +79 -0
  2. package/package.json +27 -0
  3. package/src/cli.js +372 -0
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # agent-init
2
+
3
+ Bootstrap repositories with Meraki agent infrastructure.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @meraki-digital/agent-init
9
+ ```
10
+
11
+ Or use directly without installing:
12
+
13
+ ```bash
14
+ npx @meraki-digital/agent-init
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Run in any repository you want to bootstrap:
20
+
21
+ ```bash
22
+ cd your-project
23
+ agent-init
24
+ ```
25
+
26
+ The tool will:
27
+
28
+ 1. Check for `meraki-standards` as a sibling directory
29
+ 2. Clone it if missing (with your permission)
30
+ 3. Ask a few questions about your setup preferences
31
+ 4. Create the agent infrastructure:
32
+ - `AGENTS.md` (symlinked from meraki-standards)
33
+ - `PROJECT.md` (project-specific, you fill this in)
34
+ - `.amp/skills/` or `.claude/skills/` (symlinked)
35
+ - `agentic/prd/` structure (optional)
36
+
37
+ ## Options
38
+
39
+ ```
40
+ --copy Copy files instead of symlinking (Windows fallback)
41
+ --update Pull latest meraki-standards and verify symlinks
42
+ --help Show help message
43
+ ```
44
+
45
+ ## Updating
46
+
47
+ After initial setup, run `--update` to pull the latest standards:
48
+
49
+ ```bash
50
+ agent-init --update
51
+ ```
52
+
53
+ This will:
54
+ 1. Pull latest changes to meraki-standards
55
+ 2. Verify all symlinks are intact
56
+ 3. Report any issues
57
+
58
+ ## Windows
59
+
60
+ On Windows, symlinks require Developer Mode enabled. The tool will detect this and provide instructions, or you can use `--copy` to copy files instead.
61
+
62
+ ## Project Structure After Init
63
+
64
+ ```
65
+ your-project/
66
+ AGENTS.md # Symlink → ../meraki-standards/AGENTS.md
67
+ PROJECT.md # Project-specific (edit this!)
68
+ .amp/skills/ # Symlink → ../meraki-standards/skills/
69
+ agentic/
70
+ prd/
71
+ proposed/{name}/ # PRDs in development
72
+ working/ # PRDs being implemented
73
+ completed/ # Archived PRDs
74
+ ```
75
+
76
+ ## Requirements
77
+
78
+ - Node.js 18+
79
+ - Git (for cloning meraki-standards)
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@meraki-digital/agent-init",
3
+ "version": "1.0.0",
4
+ "description": "Bootstrap repositories with Meraki agent infrastructure",
5
+ "type": "module",
6
+ "bin": {
7
+ "agent-init": "./src/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node --test"
11
+ },
12
+ "keywords": [
13
+ "ai",
14
+ "agent",
15
+ "bootstrap",
16
+ "cli"
17
+ ],
18
+ "author": "Meraki Digital",
19
+ "license": "UNLICENSED",
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/meraki-digital/agent-init.git"
26
+ }
27
+ }
package/src/cli.js ADDED
@@ -0,0 +1,372 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, symlinkSync, copyFileSync, cpSync, writeFileSync, lstatSync, readlinkSync } from 'fs';
4
+ import { join, dirname } from 'path';
5
+ import { execSync } from 'child_process';
6
+ import * as readline from 'readline';
7
+
8
+ const STANDARDS_REPO = 'https://github.com/meraki-digital/meraki-standards.git';
9
+ const STANDARDS_DIR = '../meraki-standards';
10
+
11
+ // Colors
12
+ const colors = {
13
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
14
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
15
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
16
+ bold: (s) => `\x1b[1m${s}\x1b[0m`,
17
+ };
18
+
19
+ // Readline interface
20
+ function createPrompt() {
21
+ return readline.createInterface({
22
+ input: process.stdin,
23
+ output: process.stdout,
24
+ });
25
+ }
26
+
27
+ async function ask(rl, question) {
28
+ return new Promise((resolve) => {
29
+ rl.question(question, (answer) => {
30
+ resolve(answer.trim());
31
+ });
32
+ });
33
+ }
34
+
35
+ // Check if symlinks work
36
+ function canSymlink() {
37
+ const testTarget = join(process.env.TMPDIR || '/tmp', `agent-init-target-${process.pid}`);
38
+ const testLink = join(process.env.TMPDIR || '/tmp', `agent-init-link-${process.pid}`);
39
+
40
+ try {
41
+ writeFileSync(testTarget, '');
42
+ symlinkSync(testTarget, testLink);
43
+ execSync(`rm -f "${testLink}" "${testTarget}"`, { stdio: 'ignore' });
44
+ return true;
45
+ } catch {
46
+ try {
47
+ execSync(`rm -f "${testTarget}"`, { stdio: 'ignore' });
48
+ } catch {}
49
+ return false;
50
+ }
51
+ }
52
+
53
+ // Check if Windows
54
+ function isWindows() {
55
+ return process.platform === 'win32';
56
+ }
57
+
58
+ // Check if path is a symlink pointing to expected target
59
+ function isValidSymlink(path, expectedTarget) {
60
+ try {
61
+ const stat = lstatSync(path);
62
+ if (!stat.isSymbolicLink()) return false;
63
+ const target = readlinkSync(path);
64
+ return target === expectedTarget;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ // Update mode
71
+ async function runUpdate() {
72
+ console.log('');
73
+ console.log(colors.bold('agent-init --update'));
74
+ console.log('===================');
75
+ console.log('');
76
+
77
+ // Check meraki-standards exists
78
+ if (!existsSync(STANDARDS_DIR)) {
79
+ console.log(`${colors.red('✗')} meraki-standards not found at ${STANDARDS_DIR}/`);
80
+ console.log(' Run agent-init first to set up this repo.');
81
+ process.exit(1);
82
+ }
83
+
84
+ // Pull latest
85
+ console.log('Updating meraki-standards...');
86
+ try {
87
+ const result = execSync('git pull', {
88
+ cwd: STANDARDS_DIR,
89
+ encoding: 'utf-8',
90
+ stdio: ['pipe', 'pipe', 'pipe']
91
+ });
92
+ if (result.includes('Already up to date')) {
93
+ console.log(` ${colors.green('✓')} Already up to date`);
94
+ } else {
95
+ const commits = result.match(/(\d+) files? changed/);
96
+ console.log(` ${colors.green('✓')} Pulled latest${commits ? ` (${commits[0]})` : ''}`);
97
+ }
98
+ } catch (err) {
99
+ console.log(` ${colors.yellow('!')} Could not pull: ${err.message}`);
100
+ }
101
+
102
+ // Check symlinks
103
+ console.log('');
104
+ console.log('Checking symlinks...');
105
+
106
+ const expectedLinks = [
107
+ { path: 'AGENTS.md', target: `${STANDARDS_DIR}/AGENTS.md` },
108
+ { path: 'instructions', target: `${STANDARDS_DIR}/instructions` },
109
+ ];
110
+
111
+ // Detect skills folder
112
+ if (existsSync('.amp/skills')) {
113
+ expectedLinks.push({ path: '.amp/skills', target: `${STANDARDS_DIR}/skills` });
114
+ } else if (existsSync('.claude/skills')) {
115
+ expectedLinks.push({ path: '.claude/skills', target: `${STANDARDS_DIR}/skills` });
116
+ }
117
+
118
+ let allGood = true;
119
+
120
+ for (const { path, target } of expectedLinks) {
121
+ if (!existsSync(path)) {
122
+ console.log(` ${colors.red('✗')} ${path} (missing)`);
123
+ allGood = false;
124
+ } else if (isValidSymlink(path, target)) {
125
+ console.log(` ${colors.green('✓')} ${path}`);
126
+ } else {
127
+ console.log(` ${colors.yellow('!')} ${path} (not a symlink or wrong target)`);
128
+ allGood = false;
129
+ }
130
+ }
131
+
132
+ // Check PROJECT.md exists
133
+ if (existsSync('PROJECT.md')) {
134
+ console.log(` · PROJECT.md (local file)`);
135
+ } else {
136
+ console.log(` ${colors.yellow('!')} PROJECT.md (missing - run agent-init to create)`);
137
+ }
138
+
139
+ console.log('');
140
+ if (allGood) {
141
+ console.log(colors.green('All up to date.'));
142
+ } else {
143
+ console.log(colors.yellow('Some issues found. Run agent-init to fix.'));
144
+ }
145
+ console.log('');
146
+ }
147
+
148
+ // Clone meraki-standards
149
+ function cloneStandards(targetDir) {
150
+ console.log('\nCloning meraki-standards...');
151
+ try {
152
+ execSync(`git clone ${STANDARDS_REPO} "${targetDir}"`, { stdio: 'inherit' });
153
+ console.log(` ${colors.green('✓')} Cloned to ${targetDir}/`);
154
+ return true;
155
+ } catch (err) {
156
+ console.log(` ${colors.red('✗')} Failed to clone: ${err.message}`);
157
+ return false;
158
+ }
159
+ }
160
+
161
+ // Create symlink or copy
162
+ function linkOrCopy(src, dest, name, useCopy) {
163
+ if (existsSync(dest)) {
164
+ console.log(` · ${name} (exists, skipped)`);
165
+ return;
166
+ }
167
+
168
+ try {
169
+ if (useCopy) {
170
+ try {
171
+ // Try directory copy first
172
+ cpSync(src, dest, { recursive: true });
173
+ } catch {
174
+ // Fall back to file copy
175
+ copyFileSync(src, dest);
176
+ }
177
+ console.log(` ${colors.green('✓')} ${name} (copied)`);
178
+ } else {
179
+ symlinkSync(src, dest);
180
+ console.log(` ${colors.green('✓')} ${name} (symlinked)`);
181
+ }
182
+ } catch (err) {
183
+ console.log(` ${colors.red('✗')} ${name} failed: ${err.message}`);
184
+ }
185
+ }
186
+
187
+ // Create PROJECT.md
188
+ function createProjectMd() {
189
+ if (existsSync('PROJECT.md')) {
190
+ console.log(' · PROJECT.md (exists, skipped)');
191
+ return;
192
+ }
193
+
194
+ const content = `# PROJECT.md
195
+
196
+ <!--
197
+ Project-specific instructions for AI agents.
198
+ AGENTS.md (symlinked from meraki-standards) provides the generic foundation.
199
+ This file adds context unique to this repository.
200
+
201
+ Suggested sections:
202
+ - Overview: What does this project do?
203
+ - Tech Stack: Languages, frameworks, key dependencies
204
+ - Commands: Build, test, lint, deploy
205
+ - Structure: Key directories and their purpose
206
+ - Conventions: Project-specific patterns
207
+ -->
208
+
209
+ ## Overview
210
+
211
+ ## Tech Stack
212
+
213
+ ## Commands
214
+
215
+ ## Structure
216
+
217
+ ## Conventions
218
+ `;
219
+
220
+ writeFileSync('PROJECT.md', content);
221
+ console.log(` ${colors.green('✓')} PROJECT.md (created)`);
222
+ }
223
+
224
+ // Create directory if it doesn't exist
225
+ function createDir(dir) {
226
+ if (existsSync(dir)) {
227
+ console.log(` · ${dir}/ (exists, skipped)`);
228
+ return;
229
+ }
230
+ mkdirSync(dir, { recursive: true });
231
+ console.log(` ${colors.green('✓')} ${dir}/`);
232
+ }
233
+
234
+ async function main() {
235
+ const args = process.argv.slice(2);
236
+ let useCopy = args.includes('--copy');
237
+ const showHelp = args.includes('--help') || args.includes('-h');
238
+ const runUpdateMode = args.includes('--update');
239
+
240
+ if (showHelp) {
241
+ console.log('Usage: agent-init [--copy] [--update] [--help]');
242
+ console.log('');
243
+ console.log('Bootstrap a repository with Meraki agent infrastructure.');
244
+ console.log('');
245
+ console.log('Options:');
246
+ console.log(' --copy Copy files instead of symlinking (Windows fallback)');
247
+ console.log(' --update Pull latest meraki-standards and check symlinks');
248
+ console.log(' --help Show this help message');
249
+ process.exit(0);
250
+ }
251
+
252
+ if (runUpdateMode) {
253
+ await runUpdate();
254
+ process.exit(0);
255
+ }
256
+
257
+ console.log('');
258
+ console.log(colors.bold('agent-init - Meraki Standards Bootstrap'));
259
+ console.log('========================================');
260
+ console.log('');
261
+
262
+ // Check for meraki-standards
263
+ console.log('Checking for meraki-standards repo...');
264
+
265
+ if (!existsSync(STANDARDS_DIR)) {
266
+ console.log(` ${colors.yellow('!')} Not found at ${STANDARDS_DIR}/`);
267
+
268
+ const rl = createPrompt();
269
+ const answer = await ask(rl, '\nClone meraki-standards now? [Y/n]: ');
270
+
271
+ if (answer.toLowerCase() === 'n') {
272
+ console.log('\nmeraki-standards is required. Clone it manually:');
273
+ console.log(` cd ${dirname(process.cwd())}`);
274
+ console.log(` git clone ${STANDARDS_REPO}`);
275
+ console.log('');
276
+ rl.close();
277
+ process.exit(1);
278
+ }
279
+
280
+ rl.close();
281
+
282
+ if (!cloneStandards(STANDARDS_DIR)) {
283
+ process.exit(1);
284
+ }
285
+ } else {
286
+ console.log(` ${colors.green('✓')} Found at ${STANDARDS_DIR}/`);
287
+ }
288
+
289
+ // Windows symlink check
290
+ if (isWindows() && !useCopy) {
291
+ if (!canSymlink()) {
292
+ console.log('');
293
+ console.log(colors.yellow('Windows detected. Symlinks not enabled.'));
294
+ console.log('');
295
+ console.log('To enable symlinks (recommended):');
296
+ console.log(' 1. Enable Developer Mode: Settings > System > For Developers');
297
+ console.log(' 2. Run: git config --global core.symlinks true');
298
+ console.log(' 3. Re-run this script');
299
+ console.log('');
300
+ console.log('Or run with --copy to copy files instead of symlinking:');
301
+ console.log(' agent-init --copy');
302
+ console.log('');
303
+ process.exit(1);
304
+ }
305
+ }
306
+
307
+ const rl = createPrompt();
308
+
309
+ // Interview
310
+ console.log('');
311
+ console.log('Skills folder');
312
+ console.log(' 1) .amp/skills (default)');
313
+ console.log(' 2) .claude/skills');
314
+ const skillsChoice = await ask(rl, 'Choice [1]: ');
315
+ const skillsFolder = skillsChoice === '2' ? '.claude/skills' : '.amp/skills';
316
+
317
+ console.log('');
318
+ const projectInput = await ask(rl, 'Project name for initial PRD folder [initial]: ');
319
+ const projectName = projectInput || 'initial';
320
+
321
+ console.log('');
322
+ const prdInput = await ask(rl, 'Create agentic/prd structure? [Y/n]: ');
323
+ const createPrd = prdInput.toLowerCase() !== 'n';
324
+
325
+ rl.close();
326
+
327
+ console.log('');
328
+ console.log('Creating structure...');
329
+
330
+ // AGENTS.md
331
+ linkOrCopy(`${STANDARDS_DIR}/AGENTS.md`, 'AGENTS.md', 'AGENTS.md', useCopy);
332
+
333
+ // PROJECT.md
334
+ createProjectMd();
335
+
336
+ // Skills folder
337
+ const skillsParent = dirname(skillsFolder);
338
+ if (skillsParent !== '.') {
339
+ mkdirSync(skillsParent, { recursive: true });
340
+ }
341
+ // Extra ../ because we're inside .amp/ or .claude/
342
+ linkOrCopy(`../${STANDARDS_DIR}/skills`, skillsFolder, `${skillsFolder}/`, useCopy);
343
+
344
+ // Instructions folder
345
+ linkOrCopy(`${STANDARDS_DIR}/instructions`, 'instructions', 'instructions/', useCopy);
346
+
347
+ // PRD structure
348
+ if (createPrd) {
349
+ createDir(`agentic/prd/proposed/${projectName}`);
350
+ createDir('agentic/prd/working');
351
+ createDir('agentic/prd/completed');
352
+ }
353
+
354
+ console.log('');
355
+ console.log('Done. Next steps:');
356
+ console.log(' - Edit PROJECT.md with project-specific details');
357
+ if (createPrd) {
358
+ console.log(` - Add seed.md to agentic/prd/proposed/${projectName}/`);
359
+ }
360
+
361
+ if (useCopy) {
362
+ console.log('');
363
+ console.log(colors.yellow('Note: Files were copied, not symlinked. They may drift from meraki-standards.'));
364
+ }
365
+
366
+ console.log('');
367
+ }
368
+
369
+ main().catch((err) => {
370
+ console.error(colors.red(`Error: ${err.message}`));
371
+ process.exit(1);
372
+ });