create-agent-room 1.2.1
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 +229 -0
- package/bin/cli.js +186 -0
- package/examples/python-project/.agent-room.json +14 -0
- package/examples/python-project/AGENTS.md +32 -0
- package/examples/rust-project/.agent-room.json +12 -0
- package/examples/rust-project/AGENTS.md +32 -0
- package/lib/color.js +31 -0
- package/lib/fsutil.js +218 -0
- package/lib/init.js +660 -0
- package/lib/lint-sessions.js +278 -0
- package/lib/metrics.js +190 -0
- package/lib/pr.js +176 -0
- package/lib/prompt.js +20 -0
- package/lib/session-utils.js +213 -0
- package/lib/sync.js +138 -0
- package/lib/validate.js +179 -0
- package/package.json +48 -0
- package/templates/.agent-room/anti-patterns.md +22 -0
- package/templates/.agent-room/coordination/handoff-protocol.md +60 -0
- package/templates/.agent-room/coordination/scope-boundaries.md +57 -0
- package/templates/.agent-room/coordination/session-log-format.md +62 -0
- package/templates/.agent-room/decisions.md +17 -0
- package/templates/.agent-room/guardrails.json +23 -0
- package/templates/.agent-room/guardrails.md +56 -0
- package/templates/.agent-room/principles.md +306 -0
- package/templates/.agent-room/sessions/.gitkeep +4 -0
- package/templates/.agent-room/skills/brainstorming.md +64 -0
- package/templates/.agent-room/skills/closing-the-loop.md +67 -0
- package/templates/.agent-room/skills/systematic-debugging.md +85 -0
- package/templates/.agent-room/skills/test-driven-development.md +100 -0
- package/templates/.agent-room/skills/verification-before-completion.md +56 -0
- package/templates/.agent-room/skills/writing-plans.md +87 -0
- package/templates/.agent-room/workflow-classifier.md +127 -0
- package/templates/AGENTS.md.tmpl +85 -0
- package/templates/adapters/CLAUDE.md.tmpl +38 -0
- package/templates/adapters/claude-hooks/close-the-loop-check.js +96 -0
- package/templates/adapters/clinerules.tmpl +14 -0
- package/templates/adapters/codexrules.tmpl +45 -0
- package/templates/adapters/cursorrules.tmpl +14 -0
- package/templates/adapters/git-hooks/guardrails-check.js +140 -0
- package/templates/adapters/git-hooks/pre-commit.tmpl +43 -0
- package/templates/adapters/windsurfrules.tmpl +14 -0
- package/templates/docs/plans/.gitkeep +0 -0
- package/templates/skill-packs/api-design/api-design.md +152 -0
- package/templates/skill-packs/code-review/code-review.md +113 -0
- package/templates/skill-packs/database/database-migrations.md +123 -0
- package/templates/skill-packs/documentation/documentation.md +155 -0
- package/templates/skill-packs/observability/observability.md +128 -0
- package/templates/skill-packs/performance/performance-optimization.md +150 -0
- package/templates/skill-packs/release/release-management.md +145 -0
- package/templates/skill-packs/security/security-principles.md +127 -0
- package/templates/skill-packs/testing/integration-testing.md +127 -0
- package/templates/stacks/python/.agent-room/skills/python-testing.md +59 -0
- package/templates/stacks/python/AGENTS.md.tmpl +35 -0
- package/templates/stacks/react/.agent-room/skills/react-component-testing.md +76 -0
- package/templates/stacks/react/AGENTS.md.tmpl +37 -0
- package/templates/stacks/typescript/.agent-room/skills/typescript-testing.md +63 -0
- package/templates/stacks/typescript/AGENTS.md.tmpl +36 -0
package/lib/fsutil.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
function ensureDir(dir) {
|
|
8
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function renderTemplate(content, vars) {
|
|
12
|
+
return content.replace(/\{\{([\w-]+)\}\}/g, (match, key) =>
|
|
13
|
+
Object.prototype.hasOwnProperty.call(vars, key) ? vars[key] : match
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveTemplateSource(customPath) {
|
|
18
|
+
if (customPath) {
|
|
19
|
+
const resolved = path.resolve(customPath);
|
|
20
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
21
|
+
return resolved;
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`Error: Specified template source directory does not exist or is not a directory: ${customPath}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 1. Check local directory .agent-room-templates/
|
|
27
|
+
const localTemplates = path.resolve('.agent-room-templates');
|
|
28
|
+
if (fs.existsSync(localTemplates) && fs.statSync(localTemplates).isDirectory()) {
|
|
29
|
+
return localTemplates;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 2. Check home directory ~/.agent-room-templates/
|
|
33
|
+
const homeTemplates = path.join(os.homedir(), '.agent-room-templates');
|
|
34
|
+
if (fs.existsSync(homeTemplates) && fs.statSync(homeTemplates).isDirectory()) {
|
|
35
|
+
return homeTemplates;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 3. Fallback to default internal package templates directory
|
|
39
|
+
return path.join(__dirname, '..', 'templates');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function stripTmplExt(name) {
|
|
43
|
+
return name.endsWith('.tmpl') ? name.slice(0, -'.tmpl'.length) : name;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function copyFile(src, dest, vars, opts) {
|
|
47
|
+
const force = !!(opts && opts.force);
|
|
48
|
+
if (fs.existsSync(dest) && !force) {
|
|
49
|
+
return { written: false, reason: 'exists' };
|
|
50
|
+
}
|
|
51
|
+
ensureDir(path.dirname(dest));
|
|
52
|
+
const content = renderTemplate(fs.readFileSync(src, 'utf8'), vars);
|
|
53
|
+
const exists = fs.existsSync(dest);
|
|
54
|
+
fs.writeFileSync(dest, content);
|
|
55
|
+
if (!exists && opts && typeof opts.onWrite === 'function') {
|
|
56
|
+
opts.onWrite(dest);
|
|
57
|
+
}
|
|
58
|
+
return { written: true };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function copyDir(srcDir, destDir, vars, opts) {
|
|
62
|
+
const root = (opts && opts.root) || destDir;
|
|
63
|
+
const results = [];
|
|
64
|
+
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
65
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
66
|
+
const destPath = path.join(destDir, stripTmplExt(entry.name));
|
|
67
|
+
if (entry.isDirectory()) {
|
|
68
|
+
results.push(...copyDir(srcPath, destPath, vars, opts));
|
|
69
|
+
} else {
|
|
70
|
+
const res = copyFile(srcPath, destPath, vars, opts);
|
|
71
|
+
results.push(Object.assign({ path: path.relative(root, destPath) }, res));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getLayers(R, language, pm, org) {
|
|
78
|
+
if (!fs.existsSync(R) || !fs.statSync(R).isDirectory()) return [];
|
|
79
|
+
|
|
80
|
+
// A directory is considered structured if it has a 'base' subdirectory
|
|
81
|
+
const baseDir = path.join(R, 'base');
|
|
82
|
+
const isStructured = fs.existsSync(baseDir) && fs.statSync(baseDir).isDirectory();
|
|
83
|
+
|
|
84
|
+
if (!isStructured) {
|
|
85
|
+
return [R];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const layers = [];
|
|
89
|
+
layers.push(baseDir);
|
|
90
|
+
|
|
91
|
+
if (language) {
|
|
92
|
+
const langLower = language.toLowerCase();
|
|
93
|
+
if (pm) {
|
|
94
|
+
const pmLower = pm.toLowerCase();
|
|
95
|
+
const langPmPath = path.join(R, 'stacks', `${langLower}-${pmLower}`);
|
|
96
|
+
if (fs.existsSync(langPmPath) && fs.statSync(langPmPath).isDirectory()) {
|
|
97
|
+
layers.push(langPmPath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const langPath = path.join(R, 'stacks', langLower);
|
|
101
|
+
if (fs.existsSync(langPath) && fs.statSync(langPath).isDirectory()) {
|
|
102
|
+
layers.push(langPath);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (org) {
|
|
107
|
+
const orgSpecificPath = path.join(R, 'org', org);
|
|
108
|
+
if (fs.existsSync(orgSpecificPath) && fs.statSync(orgSpecificPath).isDirectory()) {
|
|
109
|
+
layers.push(orgSpecificPath);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
const orgPath = path.join(R, 'org');
|
|
113
|
+
if (fs.existsSync(orgPath) && fs.statSync(orgPath).isDirectory()) {
|
|
114
|
+
layers.push(orgPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return layers;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function resolveTemplateSources(target, args, language, pm) {
|
|
122
|
+
const srcDirs = [];
|
|
123
|
+
const org = args.org || null;
|
|
124
|
+
|
|
125
|
+
// 1. Package templates (lowest priority)
|
|
126
|
+
const pkgTemplates = path.join(__dirname, '..', 'templates');
|
|
127
|
+
srcDirs.push(...getLayers(pkgTemplates, language, pm, org));
|
|
128
|
+
|
|
129
|
+
// 2. Global templates (~/.agent-room-templates)
|
|
130
|
+
const homeTemplates = path.join(os.homedir(), '.agent-room-templates');
|
|
131
|
+
srcDirs.push(...getLayers(homeTemplates, language, pm, org));
|
|
132
|
+
|
|
133
|
+
// 3. Local/Custom templates (highest priority)
|
|
134
|
+
const localTemplates = args['template-source']
|
|
135
|
+
? path.resolve(args['template-source'])
|
|
136
|
+
: path.resolve(target, '.agent-room-templates');
|
|
137
|
+
srcDirs.push(...getLayers(localTemplates, language, pm, org));
|
|
138
|
+
|
|
139
|
+
// De-duplicate directories to avoid redundant reads/scans
|
|
140
|
+
const uniqueDirs = [];
|
|
141
|
+
for (const d of srcDirs) {
|
|
142
|
+
if (!uniqueDirs.includes(d) && fs.existsSync(d) && fs.statSync(d).isDirectory()) {
|
|
143
|
+
uniqueDirs.push(d);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return uniqueDirs;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function copyFileInherited(srcDirs, relativePath, dest, vars, opts) {
|
|
150
|
+
for (let i = srcDirs.length - 1; i >= 0; i--) {
|
|
151
|
+
const srcPath = path.join(srcDirs[i], relativePath);
|
|
152
|
+
if (fs.existsSync(srcPath) && !fs.statSync(srcPath).isDirectory()) {
|
|
153
|
+
return copyFile(srcPath, dest, vars, opts);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`Error: Template file not found in any source: ${relativePath}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getRelativeFiles(dir, baseDir = dir) {
|
|
160
|
+
const files = [];
|
|
161
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return files;
|
|
162
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
163
|
+
const fullPath = path.join(dir, entry.name);
|
|
164
|
+
if (entry.isDirectory()) {
|
|
165
|
+
files.push(...getRelativeFiles(fullPath, baseDir));
|
|
166
|
+
} else {
|
|
167
|
+
files.push(path.relative(baseDir, fullPath));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return files;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function copyDirInherited(srcDirs, relativeSubdir, destDir, vars, opts) {
|
|
174
|
+
const uniqueRelativeFiles = new Set();
|
|
175
|
+
for (const srcDir of srcDirs) {
|
|
176
|
+
const targetDir = path.join(srcDir, relativeSubdir);
|
|
177
|
+
const files = getRelativeFiles(targetDir);
|
|
178
|
+
for (const f of files) {
|
|
179
|
+
uniqueRelativeFiles.add(f);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const results = [];
|
|
184
|
+
const root = (opts && opts.root) || destDir;
|
|
185
|
+
|
|
186
|
+
for (const relFile of uniqueRelativeFiles) {
|
|
187
|
+
let foundSrcPath = null;
|
|
188
|
+
for (let i = srcDirs.length - 1; i >= 0; i--) {
|
|
189
|
+
const p = path.join(srcDirs[i], relativeSubdir, relFile);
|
|
190
|
+
if (fs.existsSync(p) && !fs.statSync(p).isDirectory()) {
|
|
191
|
+
foundSrcPath = p;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (foundSrcPath) {
|
|
197
|
+
const destRelPath = stripTmplExt(relFile);
|
|
198
|
+
const destPath = path.join(destDir, destRelPath);
|
|
199
|
+
const res = copyFile(foundSrcPath, destPath, vars, opts);
|
|
200
|
+
results.push(Object.assign({ path: path.relative(root, destPath) }, res));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return results;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = {
|
|
208
|
+
ensureDir,
|
|
209
|
+
renderTemplate,
|
|
210
|
+
stripTmplExt,
|
|
211
|
+
copyFile,
|
|
212
|
+
copyDir,
|
|
213
|
+
resolveTemplateSource,
|
|
214
|
+
getLayers,
|
|
215
|
+
resolveTemplateSources,
|
|
216
|
+
copyFileInherited,
|
|
217
|
+
copyDirInherited
|
|
218
|
+
};
|