@aimlsuperagent/agent 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/AGENTS.md +86 -0
- package/CONTRIBUTING.md +31 -0
- package/DEPLOYMENT_LOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +253 -0
- package/REPO_SOURCE_OF_TRUTH.json +77 -0
- package/SAFE_ENV_AUDIT.md +12 -0
- package/SECURITY.md +32 -0
- package/WORKING_NOTES.md +27 -0
- package/adapters/claude/CLAUDE.md +27 -0
- package/adapters/codex/AGENTS.md +24 -0
- package/adapters/cursor/rules.md +12 -0
- package/bin/aiml-superagent.js +477 -0
- package/docs/01-operating-model.md +95 -0
- package/docs/02-context-minimizer.md +113 -0
- package/docs/03-project-memory.md +83 -0
- package/docs/04-verification-loop.md +82 -0
- package/docs/05-secret-safe-operations.md +63 -0
- package/docs/06-deployment-discipline.md +50 -0
- package/docs/07-note-hygiene.md +51 -0
- package/docs/08-model-agnostic-use.md +53 -0
- package/docs/09-agent-evaluation.md +95 -0
- package/docs/10-adoption-playbook.md +62 -0
- package/docs/11-anti-patterns.md +85 -0
- package/docs/12-context-budget.md +52 -0
- package/docs/comparison-claude-md.md +56 -0
- package/docs/npm-private-publishing.md +89 -0
- package/docs/release-checklist.md +42 -0
- package/examples/nextjs-vercel-app/AGENTS.md +26 -0
- package/examples/nextjs-vercel-app/DEPLOYMENT_LOG.md +13 -0
- package/examples/nextjs-vercel-app/README.md +12 -0
- package/examples/nextjs-vercel-app/REPO_SOURCE_OF_TRUTH.json +65 -0
- package/examples/nextjs-vercel-app/SAFE_ENV_AUDIT.md +9 -0
- package/examples/nextjs-vercel-app/WORKING_NOTES.md +16 -0
- package/package.json +57 -0
- package/schemas/repo-source-of-truth.schema.json +122 -0
- package/templates/AGENTS.template.md +42 -0
- package/templates/DEPLOYMENT_LOG.template.md +11 -0
- package/templates/INCIDENT_REPORT.template.md +26 -0
- package/templates/PRODUCTION_CHECK.template.md +24 -0
- package/templates/REPO_SOURCE_OF_TRUTH.template.json +57 -0
- package/templates/SAFE_ENV_AUDIT.template.md +8 -0
- package/templates/TASK_BRIEF.template.md +22 -0
- package/templates/WORKING_NOTES.template.md +18 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
|
|
6
|
+
const REQUIRED_FILES = [
|
|
7
|
+
"AGENTS.md",
|
|
8
|
+
"REPO_SOURCE_OF_TRUTH.json",
|
|
9
|
+
"WORKING_NOTES.md"
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const RECOMMENDED_FILES = [
|
|
13
|
+
"DEPLOYMENT_LOG.md",
|
|
14
|
+
"SAFE_ENV_AUDIT.md"
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const TEMPLATE_MAP = new Map([
|
|
18
|
+
["AGENTS.template.md", "AGENTS.md"],
|
|
19
|
+
["REPO_SOURCE_OF_TRUTH.template.json", "REPO_SOURCE_OF_TRUTH.json"],
|
|
20
|
+
["WORKING_NOTES.template.md", "WORKING_NOTES.md"],
|
|
21
|
+
["DEPLOYMENT_LOG.template.md", "DEPLOYMENT_LOG.md"],
|
|
22
|
+
["INCIDENT_REPORT.template.md", "INCIDENT_REPORT.md"],
|
|
23
|
+
["SAFE_ENV_AUDIT.template.md", "SAFE_ENV_AUDIT.md"],
|
|
24
|
+
["PRODUCTION_CHECK.template.md", "PRODUCTION_CHECK.md"],
|
|
25
|
+
["TASK_BRIEF.template.md", "TASK_BRIEF.md"]
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const SECRET_PATTERNS = [
|
|
29
|
+
{
|
|
30
|
+
name: "private-key-block",
|
|
31
|
+
pattern: /-----BEGIN (RSA |EC |OPENSSH |DSA |PRIVATE )?PRIVATE KEY-----/
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "assignment-with-sensitive-name",
|
|
35
|
+
pattern: /\b(api[_-]?key|secret|token|password|passwd|auth[_-]?token|private[_-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_./+=-]{16,}/i
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "database-url-with-credentials",
|
|
39
|
+
pattern: /\b(postgres|postgresql|mysql|mongodb):\/\/[^:\s]+:[^@\s]+@/i
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const PLACEHOLDER_PATTERNS = [
|
|
44
|
+
{
|
|
45
|
+
name: "project-name-placeholder",
|
|
46
|
+
pattern: /\bPROJECT_NAME\b/
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "command-placeholder",
|
|
50
|
+
pattern: /\b(REPLACE_WITH_COMMAND|COMMANDS_HERE)\b/
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "date-placeholder",
|
|
54
|
+
pattern: /\bYYYY-MM-DD\b/
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "generic-template-instruction",
|
|
58
|
+
pattern: /Describe the project in one paragraph|One sentence describing what this project does|One sentence\./
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const CONTEXT_SIZE_LIMITS = [
|
|
63
|
+
{
|
|
64
|
+
file: "WORKING_NOTES.md",
|
|
65
|
+
maxBytes: 80 * 1024,
|
|
66
|
+
severity: "medium",
|
|
67
|
+
message: "WORKING_NOTES.md is large enough to risk context bloat. Archive or compress resolved material."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
file: "AGENTS.md",
|
|
71
|
+
maxBytes: 40 * 1024,
|
|
72
|
+
severity: "medium",
|
|
73
|
+
message: "AGENTS.md is large enough to weaken instruction focus. Move project history into notes."
|
|
74
|
+
}
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
function usage() {
|
|
78
|
+
console.log(`AiML SuperAgent
|
|
79
|
+
|
|
80
|
+
Usage:
|
|
81
|
+
aiml-superagent init [target-dir]
|
|
82
|
+
aiml-superagent check [target-dir] [--json] [--release] [--strict]
|
|
83
|
+
|
|
84
|
+
Examples:
|
|
85
|
+
node bin/aiml-superagent.js init ../my-app
|
|
86
|
+
node bin/aiml-superagent.js check ../my-app
|
|
87
|
+
node bin/aiml-superagent.js check ../my-app --release
|
|
88
|
+
node bin/aiml-superagent.js check ../my-app --strict
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function repoRootFromScript() {
|
|
93
|
+
return path.resolve(path.dirname(new URL(import.meta.url).pathname), "..");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function readJson(file) {
|
|
97
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function ensureDir(dir) {
|
|
101
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function copyTemplates(targetDir) {
|
|
105
|
+
const root = repoRootFromScript();
|
|
106
|
+
const templateDir = path.join(root, "templates");
|
|
107
|
+
const actions = [];
|
|
108
|
+
|
|
109
|
+
ensureDir(targetDir);
|
|
110
|
+
|
|
111
|
+
for (const [templateName, outputName] of TEMPLATE_MAP.entries()) {
|
|
112
|
+
const source = path.join(templateDir, templateName);
|
|
113
|
+
const target = path.join(targetDir, outputName);
|
|
114
|
+
|
|
115
|
+
if (!fs.existsSync(source)) {
|
|
116
|
+
actions.push({ type: "missing-template", file: templateName });
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(target)) {
|
|
121
|
+
actions.push({ type: "skipped-existing", file: outputName });
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fs.copyFileSync(source, target);
|
|
126
|
+
actions.push({ type: "created", file: outputName });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return actions;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function isIgnoredForPlaceholderScan(relPath) {
|
|
133
|
+
return (
|
|
134
|
+
relPath === "bin/aiml-superagent.js" ||
|
|
135
|
+
relPath.startsWith("templates/") ||
|
|
136
|
+
relPath.startsWith("schemas/")
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function listTextFiles(rootDir) {
|
|
141
|
+
const skip = new Set([".git", "node_modules", ".next", "dist", "build", "coverage"]);
|
|
142
|
+
const files = [];
|
|
143
|
+
|
|
144
|
+
function walk(dir) {
|
|
145
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
146
|
+
if (skip.has(entry.name)) continue;
|
|
147
|
+
const fullPath = path.join(dir, entry.name);
|
|
148
|
+
const relPath = path.relative(rootDir, fullPath);
|
|
149
|
+
|
|
150
|
+
if (entry.isDirectory()) {
|
|
151
|
+
walk(fullPath);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (entry.isFile() && /\.(md|json|js|ts|tsx|yml|yaml|env|txt)$/i.test(entry.name)) {
|
|
156
|
+
files.push(relPath);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
walk(rootDir);
|
|
162
|
+
return files;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function scanForSecrets(rootDir) {
|
|
166
|
+
const findings = [];
|
|
167
|
+
for (const relPath of listTextFiles(rootDir)) {
|
|
168
|
+
const fullPath = path.join(rootDir, relPath);
|
|
169
|
+
const content = fs.readFileSync(fullPath, "utf8");
|
|
170
|
+
|
|
171
|
+
for (const rule of SECRET_PATTERNS) {
|
|
172
|
+
if (rule.pattern.test(content)) {
|
|
173
|
+
findings.push({
|
|
174
|
+
severity: "high",
|
|
175
|
+
rule: rule.name,
|
|
176
|
+
file: relPath,
|
|
177
|
+
message: "Possible secret value found. Store names and roles only, never credential values."
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return findings;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function scanForPlaceholders(rootDir) {
|
|
187
|
+
const findings = [];
|
|
188
|
+
for (const relPath of listTextFiles(rootDir)) {
|
|
189
|
+
if (isIgnoredForPlaceholderScan(relPath)) continue;
|
|
190
|
+
|
|
191
|
+
const fullPath = path.join(rootDir, relPath);
|
|
192
|
+
const content = fs.readFileSync(fullPath, "utf8");
|
|
193
|
+
|
|
194
|
+
for (const rule of PLACEHOLDER_PATTERNS) {
|
|
195
|
+
if (rule.pattern.test(content)) {
|
|
196
|
+
findings.push({
|
|
197
|
+
severity: "medium",
|
|
198
|
+
rule: rule.name,
|
|
199
|
+
file: relPath,
|
|
200
|
+
message: "Template placeholder appears to be unresolved."
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return findings;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function validateContextSize(rootDir) {
|
|
211
|
+
const findings = [];
|
|
212
|
+
|
|
213
|
+
for (const rule of CONTEXT_SIZE_LIMITS) {
|
|
214
|
+
const file = path.join(rootDir, rule.file);
|
|
215
|
+
if (!fs.existsSync(file)) continue;
|
|
216
|
+
|
|
217
|
+
const size = fs.statSync(file).size;
|
|
218
|
+
if (size > rule.maxBytes) {
|
|
219
|
+
findings.push({
|
|
220
|
+
severity: rule.severity,
|
|
221
|
+
file: rule.file,
|
|
222
|
+
message: `${rule.message} Current size: ${size} bytes.`
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return findings;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function validateRelativePath(rootDir, relPath) {
|
|
231
|
+
if (typeof relPath !== "string" || relPath.trim() === "") return false;
|
|
232
|
+
if (/^https?:\/\//i.test(relPath)) return true;
|
|
233
|
+
return fs.existsSync(path.join(rootDir, relPath));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function validateSourceOfTruth(rootDir) {
|
|
237
|
+
const findings = [];
|
|
238
|
+
const file = path.join(rootDir, "REPO_SOURCE_OF_TRUTH.json");
|
|
239
|
+
|
|
240
|
+
if (!fs.existsSync(file)) {
|
|
241
|
+
findings.push({
|
|
242
|
+
severity: "high",
|
|
243
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
244
|
+
message: "Missing source-of-truth file."
|
|
245
|
+
});
|
|
246
|
+
return findings;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let data;
|
|
250
|
+
try {
|
|
251
|
+
data = readJson(file);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
findings.push({
|
|
254
|
+
severity: "high",
|
|
255
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
256
|
+
message: `Invalid JSON: ${error.message}`
|
|
257
|
+
});
|
|
258
|
+
return findings;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!data.project?.name) {
|
|
262
|
+
findings.push({
|
|
263
|
+
severity: "medium",
|
|
264
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
265
|
+
message: "Missing project.name."
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (data.sourceOfTruth && typeof data.sourceOfTruth === "object") {
|
|
270
|
+
for (const [key, relPath] of Object.entries(data.sourceOfTruth)) {
|
|
271
|
+
if (!validateRelativePath(rootDir, relPath)) {
|
|
272
|
+
findings.push({
|
|
273
|
+
severity: "medium",
|
|
274
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
275
|
+
message: `sourceOfTruth.${key} points to a missing path: ${relPath}`
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!Array.isArray(data.contextMinimizer?.readFirst) || data.contextMinimizer.readFirst.length === 0) {
|
|
282
|
+
findings.push({
|
|
283
|
+
severity: "medium",
|
|
284
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
285
|
+
message: "contextMinimizer.readFirst should list the files an agent reads first."
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (Array.isArray(data.contextMinimizer?.readFirst)) {
|
|
290
|
+
for (const relPath of data.contextMinimizer.readFirst) {
|
|
291
|
+
if (!validateRelativePath(rootDir, relPath)) {
|
|
292
|
+
findings.push({
|
|
293
|
+
severity: "high",
|
|
294
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
295
|
+
message: `contextMinimizer.readFirst points to a missing path: ${relPath}`
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!Array.isArray(data.contextMinimizer?.doNotLoadByDefault) || data.contextMinimizer.doNotLoadByDefault.length < 5) {
|
|
302
|
+
findings.push({
|
|
303
|
+
severity: "medium",
|
|
304
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
305
|
+
message: "contextMinimizer.doNotLoadByDefault should protect active context from generated or stale folders."
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!data.secrets?.policy) {
|
|
310
|
+
findings.push({
|
|
311
|
+
severity: "medium",
|
|
312
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
313
|
+
message: "Missing secrets.policy."
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!Array.isArray(data.verification?.defaultCommands) || data.verification.defaultCommands.length === 0) {
|
|
318
|
+
findings.push({
|
|
319
|
+
severity: "medium",
|
|
320
|
+
file: "REPO_SOURCE_OF_TRUTH.json",
|
|
321
|
+
message: "verification.defaultCommands should list the fastest meaningful proof command."
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return findings;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function checkProject(targetDir, options = {}) {
|
|
329
|
+
const rootDir = path.resolve(targetDir);
|
|
330
|
+
const findings = [];
|
|
331
|
+
|
|
332
|
+
if (!fs.existsSync(rootDir)) {
|
|
333
|
+
findings.push({
|
|
334
|
+
severity: "high",
|
|
335
|
+
file: rootDir,
|
|
336
|
+
message: "Target directory does not exist."
|
|
337
|
+
});
|
|
338
|
+
return { rootDir, findings };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
for (const file of REQUIRED_FILES) {
|
|
342
|
+
if (!fs.existsSync(path.join(rootDir, file))) {
|
|
343
|
+
findings.push({
|
|
344
|
+
severity: "high",
|
|
345
|
+
file,
|
|
346
|
+
message: "Required SuperAgent operating file is missing."
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
for (const file of RECOMMENDED_FILES) {
|
|
352
|
+
if (!fs.existsSync(path.join(rootDir, file))) {
|
|
353
|
+
findings.push({
|
|
354
|
+
severity: "low",
|
|
355
|
+
file,
|
|
356
|
+
message: "Recommended for production projects."
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
findings.push(...validateSourceOfTruth(rootDir));
|
|
362
|
+
findings.push(...scanForSecrets(rootDir));
|
|
363
|
+
findings.push(...scanForPlaceholders(rootDir));
|
|
364
|
+
findings.push(...validateContextSize(rootDir));
|
|
365
|
+
|
|
366
|
+
if (options.release) {
|
|
367
|
+
if (!fs.existsSync(path.join(rootDir, "SECURITY.md"))) {
|
|
368
|
+
findings.push({
|
|
369
|
+
severity: "medium",
|
|
370
|
+
file: "SECURITY.md",
|
|
371
|
+
message: "Recommended before making the repository public."
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (!fs.existsSync(path.join(rootDir, "CONTRIBUTING.md"))) {
|
|
376
|
+
findings.push({
|
|
377
|
+
severity: "low",
|
|
378
|
+
file: "CONTRIBUTING.md",
|
|
379
|
+
message: "Recommended before making the repository public."
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (!fs.existsSync(path.join(rootDir, "LICENSE"))) {
|
|
384
|
+
findings.push({
|
|
385
|
+
severity: "medium",
|
|
386
|
+
file: "LICENSE",
|
|
387
|
+
message: "No license file found. Choose public terms before release."
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return { rootDir, findings };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function score(findings) {
|
|
396
|
+
const high = findings.filter((item) => item.severity === "high").length;
|
|
397
|
+
const medium = findings.filter((item) => item.severity === "medium").length;
|
|
398
|
+
const low = findings.filter((item) => item.severity === "low").length;
|
|
399
|
+
|
|
400
|
+
if (high > 0) return { label: "not-ready", high, medium, low };
|
|
401
|
+
if (medium > 0) return { label: "needs-review", high, medium, low };
|
|
402
|
+
return { label: "ready", high, medium, low };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function printCheck(result, jsonMode) {
|
|
406
|
+
const readiness = score(result.findings);
|
|
407
|
+
|
|
408
|
+
if (jsonMode) {
|
|
409
|
+
console.log(JSON.stringify({ ...result, readiness }, null, 2));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
console.log(`AiML SuperAgent check: ${result.rootDir}`);
|
|
414
|
+
console.log(`Readiness: ${readiness.label}`);
|
|
415
|
+
console.log(`Findings: high=${readiness.high} medium=${readiness.medium} low=${readiness.low}`);
|
|
416
|
+
|
|
417
|
+
if (result.findings.length === 0) {
|
|
418
|
+
console.log("No findings.");
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
for (const item of result.findings) {
|
|
423
|
+
console.log(`- [${item.severity}] ${item.file}: ${item.message}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function parseArgs(argv) {
|
|
428
|
+
const args = [...argv];
|
|
429
|
+
const options = {
|
|
430
|
+
json: false,
|
|
431
|
+
release: false,
|
|
432
|
+
strict: false
|
|
433
|
+
};
|
|
434
|
+
const positionals = [];
|
|
435
|
+
|
|
436
|
+
for (const arg of args) {
|
|
437
|
+
if (arg === "--json") {
|
|
438
|
+
options.json = true;
|
|
439
|
+
} else if (arg === "--release") {
|
|
440
|
+
options.release = true;
|
|
441
|
+
} else if (arg === "--strict") {
|
|
442
|
+
options.strict = true;
|
|
443
|
+
} else {
|
|
444
|
+
positionals.push(arg);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return { command: positionals[0], targetArg: positionals[1], options };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const { command, targetArg, options } = parseArgs(process.argv.slice(2));
|
|
452
|
+
|
|
453
|
+
if (!command || command === "--help" || command === "-h") {
|
|
454
|
+
usage();
|
|
455
|
+
process.exit(0);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (command === "init") {
|
|
459
|
+
const targetDir = path.resolve(targetArg || ".");
|
|
460
|
+
const actions = copyTemplates(targetDir);
|
|
461
|
+
for (const action of actions) {
|
|
462
|
+
console.log(`${action.type}: ${action.file}`);
|
|
463
|
+
}
|
|
464
|
+
process.exit(0);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (command === "check") {
|
|
468
|
+
const targetDir = path.resolve(targetArg || ".");
|
|
469
|
+
const result = checkProject(targetDir, options);
|
|
470
|
+
const readiness = score(result.findings);
|
|
471
|
+
printCheck(result, options.json);
|
|
472
|
+
process.exit(readiness.high > 0 || (options.strict && readiness.medium > 0) ? 1 : 0);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
console.error(`Unknown command: ${command}`);
|
|
476
|
+
usage();
|
|
477
|
+
process.exit(1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Operating Model
|
|
2
|
+
|
|
3
|
+
AiML SuperAgent defines a project operating loop for AI coding assistants.
|
|
4
|
+
|
|
5
|
+
Behavior rules are necessary, but they are not enough. A coding assistant also needs a stable way to know what is true, what is stale, what is safe to read, what must be verified, and what should be remembered.
|
|
6
|
+
|
|
7
|
+
## The Six-Phase Loop
|
|
8
|
+
|
|
9
|
+
### 1. Orient
|
|
10
|
+
|
|
11
|
+
Read the minimum durable context:
|
|
12
|
+
|
|
13
|
+
- `AGENTS.md`
|
|
14
|
+
- `REPO_SOURCE_OF_TRUTH.json`
|
|
15
|
+
- `WORKING_NOTES.md`
|
|
16
|
+
- current task prompt
|
|
17
|
+
|
|
18
|
+
The assistant should not load the whole repository at this stage.
|
|
19
|
+
|
|
20
|
+
### 2. Verify
|
|
21
|
+
|
|
22
|
+
If the task depends on live reality, verify before changing code.
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
|
|
26
|
+
- current deployed URL
|
|
27
|
+
- active environment variable names
|
|
28
|
+
- current package version
|
|
29
|
+
- latest error log
|
|
30
|
+
- actual database schema
|
|
31
|
+
- currently selected build
|
|
32
|
+
|
|
33
|
+
Production reality beats memory.
|
|
34
|
+
|
|
35
|
+
### 3. Narrow
|
|
36
|
+
|
|
37
|
+
Use targeted search to find only the relevant files:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
rg --files
|
|
41
|
+
rg -n "keyword"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The assistant should explain why the files it opens are relevant.
|
|
45
|
+
|
|
46
|
+
### 4. Patch
|
|
47
|
+
|
|
48
|
+
Make the smallest safe diff.
|
|
49
|
+
|
|
50
|
+
Rules:
|
|
51
|
+
|
|
52
|
+
- no unrelated refactors
|
|
53
|
+
- no style churn
|
|
54
|
+
- no speculative abstractions
|
|
55
|
+
- no deleting user work
|
|
56
|
+
- no broad rewrites unless explicitly requested
|
|
57
|
+
|
|
58
|
+
### 5. Prove
|
|
59
|
+
|
|
60
|
+
Run the fastest meaningful verification.
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
|
|
64
|
+
- typecheck
|
|
65
|
+
- unit test
|
|
66
|
+
- endpoint probe
|
|
67
|
+
- build
|
|
68
|
+
- smoke test
|
|
69
|
+
- focused script
|
|
70
|
+
|
|
71
|
+
When tests cannot be run, state the missing prerequisite.
|
|
72
|
+
|
|
73
|
+
### 6. Record
|
|
74
|
+
|
|
75
|
+
Update durable memory only if reality changed.
|
|
76
|
+
|
|
77
|
+
Good notes:
|
|
78
|
+
|
|
79
|
+
- prevent repeating investigation
|
|
80
|
+
- identify production owners
|
|
81
|
+
- record exact deployment behavior
|
|
82
|
+
- explain why a surprising decision was made
|
|
83
|
+
|
|
84
|
+
Bad notes:
|
|
85
|
+
|
|
86
|
+
- paste giant logs
|
|
87
|
+
- store secrets
|
|
88
|
+
- preserve resolved noise
|
|
89
|
+
- duplicate README content
|
|
90
|
+
- record temporary guesses as facts
|
|
91
|
+
|
|
92
|
+
## Operating Principle
|
|
93
|
+
|
|
94
|
+
The assistant should leave the repo easier to operate than it found it.
|
|
95
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Context Minimizer
|
|
2
|
+
|
|
3
|
+
Context Minimizer is the central feature of AiML SuperAgent.
|
|
4
|
+
|
|
5
|
+
It reduces token waste by separating durable memory from active working context.
|
|
6
|
+
|
|
7
|
+
The goal is not smaller notes.
|
|
8
|
+
|
|
9
|
+
The goal is smaller active context.
|
|
10
|
+
|
|
11
|
+
## Problem
|
|
12
|
+
|
|
13
|
+
Large-context agents often fail because they read too much:
|
|
14
|
+
|
|
15
|
+
- stale notes
|
|
16
|
+
- resolved logs
|
|
17
|
+
- generated files
|
|
18
|
+
- unrelated code
|
|
19
|
+
- old screenshots
|
|
20
|
+
- duplicate repos
|
|
21
|
+
- package output
|
|
22
|
+
|
|
23
|
+
The model becomes slower, more expensive, and more confused.
|
|
24
|
+
|
|
25
|
+
## Strategy
|
|
26
|
+
|
|
27
|
+
AiML SuperAgent divides context into four layers.
|
|
28
|
+
|
|
29
|
+
### Layer 1: Durable Memory
|
|
30
|
+
|
|
31
|
+
Always small. Read first.
|
|
32
|
+
|
|
33
|
+
- `AGENTS.md`
|
|
34
|
+
- `REPO_SOURCE_OF_TRUTH.json`
|
|
35
|
+
- `WORKING_NOTES.md`
|
|
36
|
+
|
|
37
|
+
### Layer 2: Task Context
|
|
38
|
+
|
|
39
|
+
Provided by the user or issue.
|
|
40
|
+
|
|
41
|
+
- current request
|
|
42
|
+
- relevant error message
|
|
43
|
+
- direct file path
|
|
44
|
+
- screenshot or log excerpt
|
|
45
|
+
|
|
46
|
+
### Layer 3: Targeted Source Context
|
|
47
|
+
|
|
48
|
+
Found through search.
|
|
49
|
+
|
|
50
|
+
- files matching the symbol
|
|
51
|
+
- route handling the endpoint
|
|
52
|
+
- config controlling the behavior
|
|
53
|
+
- test covering the bug
|
|
54
|
+
|
|
55
|
+
### Layer 4: Proof Context
|
|
56
|
+
|
|
57
|
+
Generated during verification.
|
|
58
|
+
|
|
59
|
+
- focused test result
|
|
60
|
+
- build result
|
|
61
|
+
- endpoint response
|
|
62
|
+
- deployment status
|
|
63
|
+
|
|
64
|
+
Proof context should be summarized before being saved.
|
|
65
|
+
|
|
66
|
+
## Context Budget
|
|
67
|
+
|
|
68
|
+
Every task should answer:
|
|
69
|
+
|
|
70
|
+
1. What durable files are needed?
|
|
71
|
+
2. What exact source files are needed?
|
|
72
|
+
3. What logs or production checks are needed?
|
|
73
|
+
4. What can be skipped?
|
|
74
|
+
|
|
75
|
+
## Do Not Load By Default
|
|
76
|
+
|
|
77
|
+
- `node_modules`
|
|
78
|
+
- `.git`
|
|
79
|
+
- `.next`
|
|
80
|
+
- `dist`
|
|
81
|
+
- `build`
|
|
82
|
+
- `coverage`
|
|
83
|
+
- `DerivedData`
|
|
84
|
+
- huge logs
|
|
85
|
+
- old screenshots
|
|
86
|
+
- resolved incidents
|
|
87
|
+
- unrelated archives
|
|
88
|
+
|
|
89
|
+
## Search-First Commands
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
rg --files
|
|
93
|
+
rg -n "symbolOrKeyword"
|
|
94
|
+
rg -n "route|env|error|function"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Note Compression Rule
|
|
98
|
+
|
|
99
|
+
When saving a finding, write the smallest future-useful version:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
Bad:
|
|
103
|
+
Pasted 700 lines of build logs.
|
|
104
|
+
|
|
105
|
+
Good:
|
|
106
|
+
Build failed because Next.js could not resolve package X from route Y.
|
|
107
|
+
Fixed by pinning package version Z. Verified with npm run build on 2026-05-31.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Success Metric
|
|
111
|
+
|
|
112
|
+
Context Minimizer is working when future tasks need fewer broad searches and fewer repeated investigations.
|
|
113
|
+
|