agentboot 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/.github/ISSUE_TEMPLATE/persona-request.md +62 -0
- package/.github/ISSUE_TEMPLATE/quality-feedback.md +67 -0
- package/.github/workflows/cla.yml +25 -0
- package/.github/workflows/validate.yml +49 -0
- package/.idea/agentboot.iml +9 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/CLA.md +98 -0
- package/CLAUDE.md +230 -0
- package/CONTRIBUTING.md +168 -0
- package/LICENSE +191 -0
- package/NOTICE +4 -0
- package/PERSONAS.md +156 -0
- package/README.md +172 -0
- package/agentboot.config.json +207 -0
- package/bin/agentboot.js +17 -0
- package/core/gotchas/README.md +35 -0
- package/core/instructions/baseline.instructions.md +133 -0
- package/core/instructions/security.instructions.md +186 -0
- package/core/personas/code-reviewer/SKILL.md +175 -0
- package/core/personas/code-reviewer/persona.config.json +11 -0
- package/core/personas/security-reviewer/SKILL.md +233 -0
- package/core/personas/security-reviewer/persona.config.json +11 -0
- package/core/personas/test-data-expert/SKILL.md +234 -0
- package/core/personas/test-data-expert/persona.config.json +10 -0
- package/core/personas/test-generator/SKILL.md +262 -0
- package/core/personas/test-generator/persona.config.json +10 -0
- package/core/traits/audit-trail.md +182 -0
- package/core/traits/confidence-signaling.md +172 -0
- package/core/traits/critical-thinking.md +129 -0
- package/core/traits/schema-awareness.md +132 -0
- package/core/traits/source-citation.md +174 -0
- package/core/traits/structured-output.md +199 -0
- package/docs/ci-cd-automation.md +548 -0
- package/docs/claude-code-reference/README.md +21 -0
- package/docs/claude-code-reference/agentboot-coverage.md +484 -0
- package/docs/claude-code-reference/feature-inventory.md +906 -0
- package/docs/cli-commands-audit.md +112 -0
- package/docs/cli-design.md +924 -0
- package/docs/concepts.md +1117 -0
- package/docs/config-schema-audit.md +121 -0
- package/docs/configuration.md +645 -0
- package/docs/delivery-methods.md +758 -0
- package/docs/developer-onboarding.md +342 -0
- package/docs/extending.md +448 -0
- package/docs/getting-started.md +298 -0
- package/docs/knowledge-layer.md +464 -0
- package/docs/marketplace.md +822 -0
- package/docs/org-connection.md +570 -0
- package/docs/plans/architecture.md +2429 -0
- package/docs/plans/design.md +2018 -0
- package/docs/plans/prd.md +1862 -0
- package/docs/plans/stack-rank.md +261 -0
- package/docs/plans/technical-spec.md +2755 -0
- package/docs/privacy-and-safety.md +807 -0
- package/docs/prompt-optimization.md +1071 -0
- package/docs/test-plan.md +972 -0
- package/docs/third-party-ecosystem.md +496 -0
- package/domains/compliance-template/README.md +173 -0
- package/domains/compliance-template/traits/compliance-aware.md +228 -0
- package/examples/enterprise/agentboot.config.json +184 -0
- package/examples/minimal/agentboot.config.json +46 -0
- package/package.json +63 -0
- package/repos.json +1 -0
- package/scripts/cli.ts +1069 -0
- package/scripts/compile.ts +1000 -0
- package/scripts/dev-sync.ts +149 -0
- package/scripts/lib/config.ts +137 -0
- package/scripts/lib/frontmatter.ts +61 -0
- package/scripts/sync.ts +687 -0
- package/scripts/validate.ts +421 -0
- package/tests/REGRESSION-PLAN.md +705 -0
- package/tests/TEST-PLAN.md +111 -0
- package/tests/cli.test.ts +705 -0
- package/tests/pipeline.test.ts +608 -0
- package/tests/validate.test.ts +278 -0
- package/tsconfig.json +62 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-only: syncs all platform distributions to their native locations
|
|
3
|
+
* in the current repo for local dogfooding. Gitignored output only.
|
|
4
|
+
*
|
|
5
|
+
* This is NOT the production sync script (sync.ts). This is a convenience
|
|
6
|
+
* for developing AgentBoot itself with its own personas loaded.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npm run dev-sync (after npm run build)
|
|
10
|
+
* npm run full-build && npm run dev-sync
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const ROOT = path.resolve(__dirname, "..");
|
|
20
|
+
const DIST = path.join(ROOT, "dist");
|
|
21
|
+
|
|
22
|
+
function copyRecursive(src: string, dest: string): number {
|
|
23
|
+
let count = 0;
|
|
24
|
+
if (!fs.existsSync(src)) return count;
|
|
25
|
+
|
|
26
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
const srcPath = path.join(src, entry.name);
|
|
29
|
+
const destPath = path.join(dest, entry.name);
|
|
30
|
+
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
count += copyRecursive(srcPath, destPath);
|
|
33
|
+
} else {
|
|
34
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
35
|
+
fs.copyFileSync(srcPath, destPath);
|
|
36
|
+
count++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return count;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Remove only files that exist in the source tree from the destination.
|
|
44
|
+
* This avoids destroying manually-created files (e.g., .claude/rules/ or
|
|
45
|
+
* .claude/settings.json that are checked into the repo).
|
|
46
|
+
*/
|
|
47
|
+
function cleanMatchingFiles(src: string, dest: string): void {
|
|
48
|
+
if (!fs.existsSync(src) || !fs.existsSync(dest)) return;
|
|
49
|
+
|
|
50
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const destPath = path.join(dest, entry.name);
|
|
53
|
+
if (!fs.existsSync(destPath)) continue;
|
|
54
|
+
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
cleanMatchingFiles(path.join(src, entry.name), destPath);
|
|
57
|
+
// Remove directory only if now empty
|
|
58
|
+
try {
|
|
59
|
+
const remaining = fs.readdirSync(destPath);
|
|
60
|
+
if (remaining.length === 0) fs.rmdirSync(destPath);
|
|
61
|
+
} catch { /* ignore */ }
|
|
62
|
+
} else {
|
|
63
|
+
fs.unlinkSync(destPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Platform sync definitions
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
const platforms: Array<{
|
|
73
|
+
name: string;
|
|
74
|
+
distSubdir: string;
|
|
75
|
+
repoTarget: string;
|
|
76
|
+
available: boolean;
|
|
77
|
+
}> = [
|
|
78
|
+
{
|
|
79
|
+
name: "claude",
|
|
80
|
+
distSubdir: "claude/core",
|
|
81
|
+
repoTarget: ".claude",
|
|
82
|
+
available: false,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "copilot",
|
|
86
|
+
distSubdir: "copilot/core",
|
|
87
|
+
repoTarget: ".github/copilot",
|
|
88
|
+
available: false,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "cursor",
|
|
92
|
+
distSubdir: "cursor/core",
|
|
93
|
+
repoTarget: ".cursor/agentboot",
|
|
94
|
+
available: false,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "skill",
|
|
98
|
+
distSubdir: "skill/core",
|
|
99
|
+
repoTarget: ".agentboot/skill",
|
|
100
|
+
available: false,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "gemini",
|
|
104
|
+
distSubdir: "gemini/core",
|
|
105
|
+
repoTarget: ".gemini/agentboot",
|
|
106
|
+
available: false,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Main
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
console.log(chalk.bold("\nAgentBoot — dev-sync"));
|
|
115
|
+
|
|
116
|
+
if (!fs.existsSync(DIST)) {
|
|
117
|
+
console.error(chalk.red("✗ dist/ not found. Run `npm run build` first."));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let totalFiles = 0;
|
|
122
|
+
|
|
123
|
+
for (const platform of platforms) {
|
|
124
|
+
const src = path.join(DIST, platform.distSubdir);
|
|
125
|
+
if (!fs.existsSync(src)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
platform.available = true;
|
|
130
|
+
const dest = path.join(ROOT, platform.repoTarget);
|
|
131
|
+
cleanMatchingFiles(src, dest);
|
|
132
|
+
const count = copyRecursive(src, dest);
|
|
133
|
+
totalFiles += count;
|
|
134
|
+
|
|
135
|
+
console.log(` ${chalk.green("✓")} ${platform.name} → ${platform.repoTarget}/ (${count} files)`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const skipped = platforms.filter((p) => !p.available);
|
|
139
|
+
if (skipped.length > 0) {
|
|
140
|
+
console.log(chalk.gray(` (not built: ${skipped.map((p) => p.name).join(", ")})`));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log(
|
|
144
|
+
chalk.bold(`\n${chalk.green("✓")} Dev-synced ${totalFiles} files across ${platforms.filter((p) => p.available).length} platforms (gitignored)`)
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
if (totalFiles > 0) {
|
|
148
|
+
console.log(chalk.yellow("\n ⚠ Restart Claude Code to pick up persona changes\n"));
|
|
149
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared configuration types and utilities used by validate, compile, and sync scripts.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Types
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export interface AgentBootConfig {
|
|
13
|
+
org: string;
|
|
14
|
+
orgDisplayName?: string;
|
|
15
|
+
groups?: Record<string, GroupConfig>;
|
|
16
|
+
personas?: {
|
|
17
|
+
enabled?: string[];
|
|
18
|
+
customDir?: string;
|
|
19
|
+
outputFormats?: string[];
|
|
20
|
+
};
|
|
21
|
+
traits?: {
|
|
22
|
+
enabled?: string[];
|
|
23
|
+
};
|
|
24
|
+
instructions?: {
|
|
25
|
+
enabled?: string[];
|
|
26
|
+
};
|
|
27
|
+
output?: {
|
|
28
|
+
distPath?: string;
|
|
29
|
+
provenanceHeaders?: boolean;
|
|
30
|
+
failOnDirtyDist?: boolean;
|
|
31
|
+
tokenBudget?: { warnAt?: number };
|
|
32
|
+
};
|
|
33
|
+
sync?: {
|
|
34
|
+
repos?: string;
|
|
35
|
+
targetDir?: string;
|
|
36
|
+
writePersonasIndex?: boolean;
|
|
37
|
+
dryRun?: boolean;
|
|
38
|
+
pr?: {
|
|
39
|
+
enabled?: boolean;
|
|
40
|
+
branchPrefix?: string;
|
|
41
|
+
titleTemplate?: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
claude?: {
|
|
45
|
+
hooks?: Record<string, unknown>;
|
|
46
|
+
permissions?: { allow?: string[]; deny?: string[] };
|
|
47
|
+
mcpServers?: Record<string, unknown>;
|
|
48
|
+
};
|
|
49
|
+
validation?: {
|
|
50
|
+
secretPatterns?: string[];
|
|
51
|
+
strictMode?: boolean;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface GroupConfig {
|
|
56
|
+
teams?: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PersonaConfig {
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
invocation?: string;
|
|
63
|
+
model?: string;
|
|
64
|
+
permissionMode?: string;
|
|
65
|
+
traits?: string[];
|
|
66
|
+
groups?: Record<string, { traits?: string[] }>;
|
|
67
|
+
teams?: Record<string, { traits?: string[] }>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// JSONC stripping
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Strip single-line // comments from a JSONC string, respecting string literals.
|
|
76
|
+
* Tracks whether we are inside a quoted string (handling escaped quotes) before
|
|
77
|
+
* deciding to truncate a line at a // comment.
|
|
78
|
+
*/
|
|
79
|
+
export function stripJsoncComments(raw: string): string {
|
|
80
|
+
const lines = raw.split("\n");
|
|
81
|
+
const result: string[] = [];
|
|
82
|
+
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
let inString = false;
|
|
85
|
+
let i = 0;
|
|
86
|
+
let out = "";
|
|
87
|
+
|
|
88
|
+
while (i < line.length) {
|
|
89
|
+
const ch = line[i]!;
|
|
90
|
+
|
|
91
|
+
if (inString) {
|
|
92
|
+
out += ch;
|
|
93
|
+
if (ch === "\\" && i + 1 < line.length) {
|
|
94
|
+
i++;
|
|
95
|
+
out += line[i]!;
|
|
96
|
+
} else if (ch === '"') {
|
|
97
|
+
inString = false;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
if (ch === '"') {
|
|
101
|
+
inString = true;
|
|
102
|
+
out += ch;
|
|
103
|
+
} else if (ch === "/" && line[i + 1] === "/") {
|
|
104
|
+
break;
|
|
105
|
+
} else {
|
|
106
|
+
out += ch;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
i++;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
result.push(out.trimEnd());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result.join("\n");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Config loading
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
export function resolveConfigPath(argv: string[], root: string): string {
|
|
123
|
+
const idx = argv.indexOf("--config");
|
|
124
|
+
if (idx !== -1 && argv[idx + 1]) {
|
|
125
|
+
return path.resolve(argv[idx + 1]!);
|
|
126
|
+
}
|
|
127
|
+
return path.join(root, "agentboot.config.json");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function loadConfig(configPath: string): AgentBootConfig {
|
|
131
|
+
if (!fs.existsSync(configPath)) {
|
|
132
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
133
|
+
}
|
|
134
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
135
|
+
const stripped = stripJsoncComments(raw);
|
|
136
|
+
return JSON.parse(stripped) as AgentBootConfig;
|
|
137
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared frontmatter and secret-scanning utilities.
|
|
3
|
+
*
|
|
4
|
+
* Used by both the validate script and the test suite.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Frontmatter parsing
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const FRONTMATTER_RE = /^---\n([\s\S]+?)\n---/;
|
|
12
|
+
|
|
13
|
+
export function parseFrontmatter(content: string): Map<string, string> | null {
|
|
14
|
+
const match = FRONTMATTER_RE.exec(content);
|
|
15
|
+
if (!match) return null;
|
|
16
|
+
|
|
17
|
+
const lines = (match[1] ?? "").split("\n");
|
|
18
|
+
const fields = new Map<string, string>();
|
|
19
|
+
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
const colonIdx = line.indexOf(":");
|
|
22
|
+
if (colonIdx === -1) continue;
|
|
23
|
+
const key = line.slice(0, colonIdx).trim();
|
|
24
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
25
|
+
fields.set(key, value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return fields;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Secret scanning
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
export const DEFAULT_SECRET_PATTERNS: RegExp[] = [
|
|
36
|
+
/(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]+['"]/i,
|
|
37
|
+
/(?:api[_-]?key|apikey)\s*[:=]\s*['"][^'"]+['"]/i,
|
|
38
|
+
/(?:secret|token)\s*[:=]\s*['"][^'"]+['"]/i,
|
|
39
|
+
/aws[_-]?(?:access[_-]?key|secret[_-]?key)/i,
|
|
40
|
+
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
|
|
41
|
+
/(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36}/, // GitHub tokens
|
|
42
|
+
/xox[baprs]-[0-9A-Za-z-]+/, // Slack tokens
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export function scanForSecrets(
|
|
46
|
+
content: string,
|
|
47
|
+
patterns: RegExp[] = DEFAULT_SECRET_PATTERNS
|
|
48
|
+
): Array<{ line: number; pattern: string }> {
|
|
49
|
+
const hits: Array<{ line: number; pattern: string }> = [];
|
|
50
|
+
const lines = content.split("\n");
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < lines.length; i++) {
|
|
53
|
+
for (const pattern of patterns) {
|
|
54
|
+
if (pattern.test(lines[i]!)) {
|
|
55
|
+
hits.push({ line: i + 1, pattern: pattern.source });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return hits;
|
|
61
|
+
}
|