agentboot 0.1.0 → 0.3.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 +9 -8
- package/agentboot.config.json +4 -1
- package/package.json +2 -2
- package/scripts/cli.ts +465 -18
- package/scripts/compile.ts +724 -75
- package/scripts/dev-sync.ts +1 -1
- package/scripts/lib/config.ts +259 -1
- package/scripts/lib/frontmatter.ts +3 -1
- package/scripts/validate.ts +12 -7
- package/website/docusaurus.config.ts +117 -0
- package/website/package-lock.json +18448 -0
- package/website/package.json +47 -0
- package/website/sidebars.ts +53 -0
- package/website/src/css/custom.css +23 -0
- package/website/src/pages/index.module.css +23 -0
- package/website/src/pages/index.tsx +125 -0
- package/website/static/.nojekyll +0 -0
- package/website/static/CNAME +1 -0
- package/website/static/img/favicon.ico +0 -0
- package/website/static/img/logo.svg +1 -0
- package/.github/ISSUE_TEMPLATE/persona-request.md +0 -62
- package/.github/ISSUE_TEMPLATE/quality-feedback.md +0 -67
- package/.github/workflows/cla.yml +0 -25
- package/.github/workflows/validate.yml +0 -49
- package/.idea/agentboot.iml +0 -9
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/CLAUDE.md +0 -230
- package/CONTRIBUTING.md +0 -168
- package/PERSONAS.md +0 -156
- package/core/instructions/baseline.instructions.md +0 -133
- package/core/instructions/security.instructions.md +0 -186
- package/core/personas/code-reviewer/SKILL.md +0 -175
- package/core/personas/security-reviewer/SKILL.md +0 -233
- package/core/personas/test-data-expert/SKILL.md +0 -234
- package/core/personas/test-generator/SKILL.md +0 -262
- package/core/traits/audit-trail.md +0 -182
- package/core/traits/confidence-signaling.md +0 -172
- package/core/traits/critical-thinking.md +0 -129
- package/core/traits/schema-awareness.md +0 -132
- package/core/traits/source-citation.md +0 -174
- package/core/traits/structured-output.md +0 -199
- package/docs/ci-cd-automation.md +0 -548
- package/docs/claude-code-reference/README.md +0 -21
- package/docs/claude-code-reference/agentboot-coverage.md +0 -484
- package/docs/claude-code-reference/feature-inventory.md +0 -906
- package/docs/cli-commands-audit.md +0 -112
- package/docs/cli-design.md +0 -924
- package/docs/concepts.md +0 -1117
- package/docs/config-schema-audit.md +0 -121
- package/docs/configuration.md +0 -645
- package/docs/delivery-methods.md +0 -758
- package/docs/developer-onboarding.md +0 -342
- package/docs/extending.md +0 -448
- package/docs/getting-started.md +0 -298
- package/docs/knowledge-layer.md +0 -464
- package/docs/marketplace.md +0 -822
- package/docs/org-connection.md +0 -570
- package/docs/plans/architecture.md +0 -2429
- package/docs/plans/design.md +0 -2018
- package/docs/plans/prd.md +0 -1862
- package/docs/plans/stack-rank.md +0 -261
- package/docs/plans/technical-spec.md +0 -2755
- package/docs/privacy-and-safety.md +0 -807
- package/docs/prompt-optimization.md +0 -1071
- package/docs/test-plan.md +0 -972
- package/docs/third-party-ecosystem.md +0 -496
- package/domains/compliance-template/README.md +0 -173
- package/domains/compliance-template/traits/compliance-aware.md +0 -228
- package/examples/enterprise/agentboot.config.json +0 -184
- package/examples/minimal/agentboot.config.json +0 -46
- package/tests/REGRESSION-PLAN.md +0 -705
- package/tests/TEST-PLAN.md +0 -111
- package/tests/cli.test.ts +0 -705
- package/tests/pipeline.test.ts +0 -608
- package/tests/validate.test.ts +0 -278
- package/tsconfig.json +0 -62
package/tests/validate.test.ts
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for validation logic.
|
|
3
|
-
*
|
|
4
|
-
* Tests config loading (JSONC stripping), persona existence checks,
|
|
5
|
-
* trait reference checks, SKILL.md frontmatter parsing, and secret scanning.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect } from "vitest";
|
|
9
|
-
import fs from "node:fs";
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import { stripJsoncComments } from "../scripts/lib/config.js";
|
|
12
|
-
import {
|
|
13
|
-
parseFrontmatter,
|
|
14
|
-
scanForSecrets,
|
|
15
|
-
DEFAULT_SECRET_PATTERNS,
|
|
16
|
-
} from "../scripts/lib/frontmatter.js";
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// JSONC stripping
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
describe("stripJsoncComments", () => {
|
|
23
|
-
it("strips single-line comments", () => {
|
|
24
|
-
const input = `{
|
|
25
|
-
"key": "value" // this is a comment
|
|
26
|
-
}`;
|
|
27
|
-
const stripped = stripJsoncComments(input);
|
|
28
|
-
const parsed = JSON.parse(stripped);
|
|
29
|
-
expect(parsed.key).toBe("value");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("strips full-line comments", () => {
|
|
33
|
-
const input = `{
|
|
34
|
-
// this line is entirely a comment
|
|
35
|
-
"key": "value"
|
|
36
|
-
}`;
|
|
37
|
-
const stripped = stripJsoncComments(input);
|
|
38
|
-
const parsed = JSON.parse(stripped);
|
|
39
|
-
expect(parsed.key).toBe("value");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("preserves // inside string values", () => {
|
|
43
|
-
const input = `{
|
|
44
|
-
"url": "https://example.com/path"
|
|
45
|
-
}`;
|
|
46
|
-
const stripped = stripJsoncComments(input);
|
|
47
|
-
const parsed = JSON.parse(stripped);
|
|
48
|
-
expect(parsed.url).toBe("https://example.com/path");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("handles escaped quotes in strings", () => {
|
|
52
|
-
const input = `{
|
|
53
|
-
"msg": "say \\"hello\\"" // comment
|
|
54
|
-
}`;
|
|
55
|
-
const stripped = stripJsoncComments(input);
|
|
56
|
-
const parsed = JSON.parse(stripped);
|
|
57
|
-
expect(parsed.msg).toBe('say "hello"');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("parses real agentboot.config.json", () => {
|
|
61
|
-
const configPath = path.join(__dirname, "..", "agentboot.config.json");
|
|
62
|
-
const raw = fs.readFileSync(configPath, "utf-8");
|
|
63
|
-
const stripped = stripJsoncComments(raw);
|
|
64
|
-
const config = JSON.parse(stripped);
|
|
65
|
-
expect(config.org).toBe("your-org");
|
|
66
|
-
expect(config.personas.enabled).toContain("code-reviewer");
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
// Frontmatter parsing
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
|
|
74
|
-
describe("parseFrontmatter", () => {
|
|
75
|
-
it("extracts name and description", () => {
|
|
76
|
-
const content = `---
|
|
77
|
-
name: code-reviewer
|
|
78
|
-
description: Reviews code for bugs
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
# Code Reviewer`;
|
|
82
|
-
|
|
83
|
-
const fields = parseFrontmatter(content);
|
|
84
|
-
expect(fields).not.toBeNull();
|
|
85
|
-
expect(fields!.get("name")).toBe("code-reviewer");
|
|
86
|
-
expect(fields!.get("description")).toBe("Reviews code for bugs");
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("returns null when no frontmatter block", () => {
|
|
90
|
-
const content = `# Just a heading\n\nSome content.`;
|
|
91
|
-
expect(parseFrontmatter(content)).toBeNull();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("handles multi-word values", () => {
|
|
95
|
-
const content = `---
|
|
96
|
-
name: test-generator
|
|
97
|
-
description: Writes unit and integration tests for any codebase
|
|
98
|
-
---`;
|
|
99
|
-
const fields = parseFrontmatter(content);
|
|
100
|
-
expect(fields!.get("description")).toBe(
|
|
101
|
-
"Writes unit and integration tests for any codebase"
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("parses all real SKILL.md files", () => {
|
|
106
|
-
const personasDir = path.join(__dirname, "..", "core", "personas");
|
|
107
|
-
const personas = fs.readdirSync(personasDir).filter((entry) =>
|
|
108
|
-
fs.statSync(path.join(personasDir, entry)).isDirectory()
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
expect(personas.length).toBeGreaterThanOrEqual(4);
|
|
112
|
-
|
|
113
|
-
for (const persona of personas) {
|
|
114
|
-
const skillPath = path.join(personasDir, persona, "SKILL.md");
|
|
115
|
-
expect(fs.existsSync(skillPath), `${persona}/SKILL.md should exist`).toBe(true);
|
|
116
|
-
|
|
117
|
-
const content = fs.readFileSync(skillPath, "utf-8");
|
|
118
|
-
const fields = parseFrontmatter(content);
|
|
119
|
-
expect(fields, `${persona}/SKILL.md should have frontmatter`).not.toBeNull();
|
|
120
|
-
expect(fields!.has("name"), `${persona} frontmatter should have name`).toBe(true);
|
|
121
|
-
expect(fields!.has("description"), `${persona} frontmatter should have description`).toBe(true);
|
|
122
|
-
expect(fields!.get("name"), `${persona} name should not be empty`).not.toBe("");
|
|
123
|
-
expect(fields!.get("description"), `${persona} description should not be empty`).not.toBe("");
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// ---------------------------------------------------------------------------
|
|
129
|
-
// Secret scanning
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
|
-
|
|
132
|
-
describe("scanForSecrets", () => {
|
|
133
|
-
it("detects password assignments", () => {
|
|
134
|
-
const content = `const password = "hunter2";`;
|
|
135
|
-
const hits = scanForSecrets(content);
|
|
136
|
-
expect(hits.length).toBeGreaterThan(0);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("detects API keys", () => {
|
|
140
|
-
const content = `api_key = "sk-1234567890abcdef"`;
|
|
141
|
-
const hits = scanForSecrets(content);
|
|
142
|
-
expect(hits.length).toBeGreaterThan(0);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("detects AWS keys", () => {
|
|
146
|
-
const content = `aws_access_key_id = AKIAIOSFODNN7EXAMPLE`;
|
|
147
|
-
const hits = scanForSecrets(content);
|
|
148
|
-
expect(hits.length).toBeGreaterThan(0);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("detects private keys", () => {
|
|
152
|
-
const content = `-----BEGIN RSA PRIVATE KEY-----\nMIIEpA...`;
|
|
153
|
-
const hits = scanForSecrets(content);
|
|
154
|
-
expect(hits.length).toBeGreaterThan(0);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("detects GitHub tokens", () => {
|
|
158
|
-
const content = `ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij`;
|
|
159
|
-
const hits = scanForSecrets(content);
|
|
160
|
-
expect(hits.length).toBeGreaterThan(0);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("does not flag safe content", () => {
|
|
164
|
-
const content = `Use environment variables for credentials.\nNever hardcode passwords.`;
|
|
165
|
-
const hits = scanForSecrets(content);
|
|
166
|
-
expect(hits.length).toBe(0);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("does not flag password references in instructions", () => {
|
|
170
|
-
// Our security instructions mention passwords conceptually — that's fine
|
|
171
|
-
const content = `- Never hardcode (not even for local dev)\n- Flag secrets in wrong places`;
|
|
172
|
-
const hits = scanForSecrets(content);
|
|
173
|
-
expect(hits.length).toBe(0);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("scans all real trait and persona files without false positives", () => {
|
|
177
|
-
const dirs = [
|
|
178
|
-
path.join(__dirname, "..", "core", "traits"),
|
|
179
|
-
path.join(__dirname, "..", "core", "personas"),
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
for (const dir of dirs) {
|
|
183
|
-
if (!fs.existsSync(dir)) continue;
|
|
184
|
-
const files = walkDir(dir, [".md", ".json"]);
|
|
185
|
-
for (const file of files) {
|
|
186
|
-
const content = fs.readFileSync(file, "utf-8");
|
|
187
|
-
const hits = scanForSecrets(content);
|
|
188
|
-
const relPath = path.relative(path.join(__dirname, ".."), file);
|
|
189
|
-
expect(hits, `Secret found in ${relPath} at line ${hits[0]?.line}`).toHaveLength(0);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// ---------------------------------------------------------------------------
|
|
196
|
-
// persona.config.json validation
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
|
|
199
|
-
describe("persona.config.json", () => {
|
|
200
|
-
it("exists for all enabled personas", () => {
|
|
201
|
-
const configPath = path.join(__dirname, "..", "agentboot.config.json");
|
|
202
|
-
const raw = fs.readFileSync(configPath, "utf-8");
|
|
203
|
-
const config = JSON.parse(stripJsoncComments(raw));
|
|
204
|
-
const enabled: string[] = config.personas.enabled;
|
|
205
|
-
|
|
206
|
-
for (const persona of enabled) {
|
|
207
|
-
const pcPath = path.join(__dirname, "..", "core", "personas", persona, "persona.config.json");
|
|
208
|
-
expect(fs.existsSync(pcPath), `${persona}/persona.config.json should exist`).toBe(true);
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("has required fields (name, description, invocation, traits)", () => {
|
|
213
|
-
const personasDir = path.join(__dirname, "..", "core", "personas");
|
|
214
|
-
const personas = fs.readdirSync(personasDir).filter((entry) =>
|
|
215
|
-
fs.statSync(path.join(personasDir, entry)).isDirectory()
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
for (const persona of personas) {
|
|
219
|
-
const pcPath = path.join(personasDir, persona, "persona.config.json");
|
|
220
|
-
if (!fs.existsSync(pcPath)) continue;
|
|
221
|
-
|
|
222
|
-
const config = JSON.parse(fs.readFileSync(pcPath, "utf-8"));
|
|
223
|
-
expect(config.name, `${persona} should have name`).toBeTruthy();
|
|
224
|
-
expect(config.description, `${persona} should have description`).toBeTruthy();
|
|
225
|
-
expect(config.invocation, `${persona} should have invocation`).toBeTruthy();
|
|
226
|
-
expect(Array.isArray(config.traits), `${persona} traits should be array`).toBe(true);
|
|
227
|
-
expect(config.traits.length, `${persona} should have at least one trait`).toBeGreaterThan(0);
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it("only references traits that exist in core/traits/", () => {
|
|
232
|
-
const traitsDir = path.join(__dirname, "..", "core", "traits");
|
|
233
|
-
const availableTraits = new Set(
|
|
234
|
-
fs.readdirSync(traitsDir)
|
|
235
|
-
.filter((f) => f.endsWith(".md"))
|
|
236
|
-
.map((f) => path.basename(f, ".md"))
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
const personasDir = path.join(__dirname, "..", "core", "personas");
|
|
240
|
-
const personas = fs.readdirSync(personasDir).filter((entry) =>
|
|
241
|
-
fs.statSync(path.join(personasDir, entry)).isDirectory()
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
for (const persona of personas) {
|
|
245
|
-
const pcPath = path.join(personasDir, persona, "persona.config.json");
|
|
246
|
-
if (!fs.existsSync(pcPath)) continue;
|
|
247
|
-
|
|
248
|
-
const config = JSON.parse(fs.readFileSync(pcPath, "utf-8"));
|
|
249
|
-
for (const trait of config.traits ?? []) {
|
|
250
|
-
expect(
|
|
251
|
-
availableTraits.has(trait),
|
|
252
|
-
`${persona} references trait "${trait}" which doesn't exist`
|
|
253
|
-
).toBe(true);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// ---------------------------------------------------------------------------
|
|
260
|
-
// Helpers
|
|
261
|
-
// ---------------------------------------------------------------------------
|
|
262
|
-
|
|
263
|
-
function walkDir(dir: string, extensions: string[]): string[] {
|
|
264
|
-
const results: string[] = [];
|
|
265
|
-
if (!fs.existsSync(dir)) return results;
|
|
266
|
-
|
|
267
|
-
for (const entry of fs.readdirSync(dir)) {
|
|
268
|
-
const full = path.join(dir, entry);
|
|
269
|
-
const stat = fs.statSync(full);
|
|
270
|
-
if (stat.isDirectory()) {
|
|
271
|
-
results.push(...walkDir(full, extensions));
|
|
272
|
-
} else if (extensions.some((ext) => full.endsWith(ext))) {
|
|
273
|
-
results.push(full);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return results;
|
|
278
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// Language and environment
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
"target": "ES2022",
|
|
7
|
-
"lib": ["ES2022"],
|
|
8
|
-
"module": "NodeNext",
|
|
9
|
-
"moduleResolution": "NodeNext",
|
|
10
|
-
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Strict mode — all checks on
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
"strict": true,
|
|
15
|
-
"noUncheckedIndexedAccess": true, // array[i] is T | undefined
|
|
16
|
-
"noImplicitOverride": true, // explicit override keyword required
|
|
17
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
18
|
-
"exactOptionalPropertyTypes": true, // don't allow undefined where optional
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Additional checks
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
"noUnusedLocals": true,
|
|
24
|
-
"noUnusedParameters": true,
|
|
25
|
-
"noFallthroughCasesInSwitch": true,
|
|
26
|
-
"noImplicitReturns": true,
|
|
27
|
-
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
// Emit
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
"outDir": "./dist",
|
|
32
|
-
"rootDir": ".",
|
|
33
|
-
"declaration": true,
|
|
34
|
-
"declarationMap": true,
|
|
35
|
-
"sourceMap": true,
|
|
36
|
-
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// Interop
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
"esModuleInterop": true,
|
|
41
|
-
"allowSyntheticDefaultImports": true,
|
|
42
|
-
"resolveJsonModule": true,
|
|
43
|
-
"isolatedModules": true,
|
|
44
|
-
|
|
45
|
-
// ---------------------------------------------------------------------------
|
|
46
|
-
// Path aliases
|
|
47
|
-
// ---------------------------------------------------------------------------
|
|
48
|
-
"baseUrl": ".",
|
|
49
|
-
"paths": {
|
|
50
|
-
"@agentboot/*": ["src/*"]
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
"include": [
|
|
54
|
-
"scripts/**/*.ts",
|
|
55
|
-
"src/**/*.ts"
|
|
56
|
-
],
|
|
57
|
-
"exclude": [
|
|
58
|
-
"node_modules",
|
|
59
|
-
"dist",
|
|
60
|
-
"**/*.test.ts"
|
|
61
|
-
]
|
|
62
|
-
}
|