create-agonda 0.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/index.js +245 -0
- package/package.json +28 -0
package/index.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createInterface } from 'node:readline';
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync, rmSync } from 'node:fs';
|
|
5
|
+
import { join, dirname } from 'node:path';
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
|
|
10
|
+
const PACKAGE_NAME = '@alavida-ai/agonda-framework';
|
|
11
|
+
const REGISTRY = 'https://npm.pkg.github.com';
|
|
12
|
+
|
|
13
|
+
// --- Helpers ---
|
|
14
|
+
|
|
15
|
+
function hashFile(absPath) {
|
|
16
|
+
return createHash('sha256').update(readFileSync(absPath)).digest('hex');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function fetchFramework() {
|
|
20
|
+
const tmp = join(tmpdir(), `create-agonda-${Date.now()}`);
|
|
21
|
+
mkdirSync(tmp, { recursive: true });
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
execFileSync('npm', [
|
|
25
|
+
'install', '--prefix', tmp,
|
|
26
|
+
'--registry', REGISTRY,
|
|
27
|
+
PACKAGE_NAME,
|
|
28
|
+
], { encoding: 'utf-8', timeout: 60000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
29
|
+
} catch (err) {
|
|
30
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
31
|
+
const stderr = err.stderr || '';
|
|
32
|
+
if (stderr.includes('404') || stderr.includes('not found')) {
|
|
33
|
+
console.error('Error: Agonda framework package not found.');
|
|
34
|
+
console.error('Check your .npmrc has GitHub Packages auth configured.');
|
|
35
|
+
} else {
|
|
36
|
+
console.error(`Error fetching framework: ${stderr.trim() || err.message}`);
|
|
37
|
+
}
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const packageDir = join(tmp, 'node_modules', PACKAGE_NAME);
|
|
42
|
+
if (!existsSync(packageDir)) {
|
|
43
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
44
|
+
console.error('Error: Package installed but not found at expected path.');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { packageDir, tmp };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function generateClaudeMd(templatePath, companyName, description) {
|
|
52
|
+
let content = readFileSync(templatePath, 'utf-8');
|
|
53
|
+
content = content.replace('{Instance Name}', companyName);
|
|
54
|
+
content = content.replace('{description}', description);
|
|
55
|
+
content = content.replace(
|
|
56
|
+
/\| \*\{domain\}\* \| \*\{type\}\* \| \*\{owner\}\* \| \*\{routing hint\}\* \|/,
|
|
57
|
+
'| *(add domains via governance process)* | | | |'
|
|
58
|
+
);
|
|
59
|
+
return content;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Collect all lines from stdin, then serve them sequentially.
|
|
64
|
+
* Works with both piped input and interactive TTY.
|
|
65
|
+
*/
|
|
66
|
+
function createPrompter() {
|
|
67
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
68
|
+
const lines = [];
|
|
69
|
+
let lineResolve = null;
|
|
70
|
+
let closed = false;
|
|
71
|
+
|
|
72
|
+
rl.on('line', (line) => {
|
|
73
|
+
if (lineResolve) {
|
|
74
|
+
const resolve = lineResolve;
|
|
75
|
+
lineResolve = null;
|
|
76
|
+
resolve(line);
|
|
77
|
+
} else {
|
|
78
|
+
lines.push(line);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
rl.on('close', () => {
|
|
83
|
+
closed = true;
|
|
84
|
+
if (lineResolve) {
|
|
85
|
+
lineResolve(null);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
ask(prompt) {
|
|
91
|
+
process.stdout.write(prompt);
|
|
92
|
+
if (lines.length > 0) {
|
|
93
|
+
const line = lines.shift();
|
|
94
|
+
process.stdout.write(line + '\n');
|
|
95
|
+
return Promise.resolve(line);
|
|
96
|
+
}
|
|
97
|
+
if (closed) {
|
|
98
|
+
return Promise.resolve(null);
|
|
99
|
+
}
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
lineResolve = resolve;
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
close() {
|
|
105
|
+
rl.close();
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Main ---
|
|
111
|
+
|
|
112
|
+
async function main() {
|
|
113
|
+
const dirArg = process.argv[2];
|
|
114
|
+
|
|
115
|
+
if (dirArg === '--help' || dirArg === '-h') {
|
|
116
|
+
console.log('Usage: npx @alavida-ai/create-agonda [directory]\n');
|
|
117
|
+
console.log('Create a new Agonda instance.\n');
|
|
118
|
+
console.log('Options:');
|
|
119
|
+
console.log(' -h, --help Show this help message');
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const prompter = createPrompter();
|
|
124
|
+
|
|
125
|
+
let targetDir;
|
|
126
|
+
if (dirArg) {
|
|
127
|
+
targetDir = join(process.cwd(), dirArg);
|
|
128
|
+
} else {
|
|
129
|
+
const dir = await prompter.ask('Directory name: ');
|
|
130
|
+
if (!dir || !dir.trim()) {
|
|
131
|
+
console.error('Directory name is required.');
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
targetDir = join(process.cwd(), dir.trim());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (existsSync(targetDir)) {
|
|
138
|
+
prompter.close();
|
|
139
|
+
console.error(`Error: ${targetDir} already exists.`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const companyName = await prompter.ask('Company name: ');
|
|
144
|
+
if (!companyName || !companyName.trim()) {
|
|
145
|
+
prompter.close();
|
|
146
|
+
console.error('Company name is required.');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const description = await prompter.ask(`What does ${companyName.trim()} do? `);
|
|
151
|
+
prompter.close();
|
|
152
|
+
|
|
153
|
+
if (!description || !description.trim()) {
|
|
154
|
+
console.error('Description is required.');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(`\nCreating Agonda instance for ${companyName.trim()}...\n`);
|
|
159
|
+
|
|
160
|
+
// 1. Fetch framework package
|
|
161
|
+
process.stdout.write(' Fetching framework...');
|
|
162
|
+
const { packageDir, tmp } = fetchFramework();
|
|
163
|
+
const manifest = JSON.parse(readFileSync(join(packageDir, 'manifest.json'), 'utf-8'));
|
|
164
|
+
console.log(` v${manifest.version}`);
|
|
165
|
+
|
|
166
|
+
// 2. Create directory structure
|
|
167
|
+
mkdirSync(targetDir, { recursive: true });
|
|
168
|
+
const workspaceDirs = [
|
|
169
|
+
'workspace/inbox',
|
|
170
|
+
'workspace/active',
|
|
171
|
+
'workspace/reference',
|
|
172
|
+
'workspace/archive',
|
|
173
|
+
'domains',
|
|
174
|
+
'.agonda',
|
|
175
|
+
];
|
|
176
|
+
for (const dir of workspaceDirs) {
|
|
177
|
+
mkdirSync(join(targetDir, dir), { recursive: true });
|
|
178
|
+
}
|
|
179
|
+
console.log(' Created workspace directories');
|
|
180
|
+
|
|
181
|
+
// 3. Copy managed files from framework
|
|
182
|
+
const contentDir = join(packageDir, 'content');
|
|
183
|
+
const files = {};
|
|
184
|
+
for (const [filePath] of Object.entries(manifest.files)) {
|
|
185
|
+
const src = join(contentDir, filePath);
|
|
186
|
+
const dest = join(targetDir, filePath);
|
|
187
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
188
|
+
cpSync(src, dest);
|
|
189
|
+
files[filePath] = { sha256: hashFile(dest) };
|
|
190
|
+
}
|
|
191
|
+
console.log(` Installed ${Object.keys(files).length} framework files`);
|
|
192
|
+
|
|
193
|
+
// 4. Generate CLAUDE.md from template
|
|
194
|
+
const templatePath = join(packageDir, 'templates', 'CLAUDE.md');
|
|
195
|
+
const claudeMd = generateClaudeMd(templatePath, companyName.trim(), description.trim());
|
|
196
|
+
writeFileSync(join(targetDir, 'CLAUDE.md'), claudeMd);
|
|
197
|
+
console.log(' Generated CLAUDE.md');
|
|
198
|
+
|
|
199
|
+
// 5. Write .agonda/manifest.json
|
|
200
|
+
const instanceManifest = {
|
|
201
|
+
framework: {
|
|
202
|
+
package: PACKAGE_NAME,
|
|
203
|
+
version: manifest.version,
|
|
204
|
+
installed: new Date().toISOString(),
|
|
205
|
+
},
|
|
206
|
+
files,
|
|
207
|
+
};
|
|
208
|
+
writeFileSync(
|
|
209
|
+
join(targetDir, '.agonda', 'manifest.json'),
|
|
210
|
+
JSON.stringify(instanceManifest, null, 2) + '\n'
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// 6. Write .gitignore
|
|
214
|
+
writeFileSync(join(targetDir, '.gitignore'), [
|
|
215
|
+
'.agonda/status.json',
|
|
216
|
+
'node_modules/',
|
|
217
|
+
'.DS_Store',
|
|
218
|
+
'',
|
|
219
|
+
].join('\n'));
|
|
220
|
+
|
|
221
|
+
// 7. Git init + initial commit
|
|
222
|
+
try {
|
|
223
|
+
execFileSync('git', ['init'], { cwd: targetDir, stdio: 'pipe' });
|
|
224
|
+
execFileSync('git', ['add', '.'], { cwd: targetDir, stdio: 'pipe' });
|
|
225
|
+
execFileSync('git', ['commit', '-m', `Initialize Agonda instance for ${companyName.trim()}`], {
|
|
226
|
+
cwd: targetDir,
|
|
227
|
+
stdio: 'pipe',
|
|
228
|
+
});
|
|
229
|
+
console.log(' Initialized git repository');
|
|
230
|
+
} catch {
|
|
231
|
+
console.log(' Warning: git init failed — initialize manually');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 8. Clean up
|
|
235
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
236
|
+
|
|
237
|
+
// Done
|
|
238
|
+
const dirName = dirArg || targetDir.split('/').pop();
|
|
239
|
+
console.log(`\nDone. Next:\n\n cd ${dirName} && claude\n`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
main().catch((err) => {
|
|
243
|
+
console.error(err.message);
|
|
244
|
+
process.exit(1);
|
|
245
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-agonda",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create a new Agonda instance",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-agonda": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18.0.0"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/alavida-ai/create-agonda.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"agonda",
|
|
21
|
+
"governance",
|
|
22
|
+
"ai-agents",
|
|
23
|
+
"scaffolding"
|
|
24
|
+
],
|
|
25
|
+
"author": "Alavida AI",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"private": false
|
|
28
|
+
}
|