@theagilemonkeys/facility 0.11.4 → 0.12.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.
Files changed (67) hide show
  1. package/README.md +61 -47
  2. package/package.json +3 -4
  3. package/src/cli.mjs +27 -176
  4. package/src/detect.mjs +24 -94
  5. package/src/doctor.mjs +54 -559
  6. package/src/init.mjs +92 -535
  7. package/templates/agents/address-review.md +53 -0
  8. package/templates/agents/architect.md +50 -0
  9. package/templates/agents/builder.md +58 -0
  10. package/templates/agents/ci-doctor.md +55 -0
  11. package/templates/agents/pr-reviewer.md +52 -0
  12. package/templates/agents/security-audit.md +54 -0
  13. package/modules/README.md +0 -35
  14. package/modules/ai-queryability/agents/queryability-reviewer.md +0 -35
  15. package/modules/ai-queryability/module.json +0 -9
  16. package/modules/ai-queryability/standard-section.md +0 -22
  17. package/modules/analytics/agents/analytics-reviewer.md +0 -32
  18. package/modules/analytics/commands/add-telemetry.md +0 -23
  19. package/modules/analytics/module.json +0 -10
  20. package/modules/analytics/standard-section.md +0 -23
  21. package/modules/database/agents/data-security-reviewer.md +0 -38
  22. package/modules/database/commands/new-migration.md +0 -24
  23. package/modules/database/guards/migration-versions.mjs +0 -41
  24. package/modules/database/guards/migrations-immutable.mjs +0 -57
  25. package/modules/database/hooks/protect-migrations.fragment.mjs +0 -10
  26. package/modules/database/module.json +0 -25
  27. package/modules/database/standard-section.md +0 -20
  28. package/modules/design-system/agents/design-reviewer.md +0 -37
  29. package/modules/design-system/module.json +0 -9
  30. package/modules/design-system/standard-section.md +0 -15
  31. package/src/add.mjs +0 -77
  32. package/src/platform-admin.mjs +0 -1552
  33. package/src/platform-config.mjs +0 -39
  34. package/src/platform.mjs +0 -1759
  35. package/src/render.mjs +0 -66
  36. package/templates/claude/settings.json +0 -71
  37. package/templates/delivery/verify.mjs +0 -157
  38. package/templates/doctor/resolve.mjs +0 -572
  39. package/templates/guards/README.md +0 -30
  40. package/templates/guards/_kit.mjs +0 -81
  41. package/templates/guards/actions-pinned.mjs +0 -38
  42. package/templates/guards/run.mjs +0 -111
  43. package/templates/guards/watchtower-locked.mjs +0 -66
  44. package/templates/prompts/address-review.md +0 -14
  45. package/templates/prompts/architect.md +0 -63
  46. package/templates/prompts/builder.md +0 -79
  47. package/templates/prompts/doctor.md +0 -69
  48. package/templates/prompts/review.md +0 -14
  49. package/templates/prompts/sweep.md +0 -75
  50. package/templates/receipts/collect.mjs +0 -297
  51. package/templates/review/finalize.mjs +0 -38
  52. package/templates/scripts/move-board-status.sh +0 -155
  53. package/templates/security/sync-findings.mjs +0 -226
  54. package/templates/standard/STANDARD.md +0 -141
  55. package/templates/standard/agents-block.md +0 -25
  56. package/templates/watchtower/budgets.json +0 -12
  57. package/templates/watchtower/canary.mjs +0 -216
  58. package/templates/watchtower/health.mjs +0 -148
  59. package/templates/watchtower/outcomes.mjs +0 -188
  60. package/templates/workflows/facility-address-review.yml +0 -154
  61. package/templates/workflows/facility-canary.yml +0 -61
  62. package/templates/workflows/facility-codex.yml +0 -327
  63. package/templates/workflows/facility-crew.yml +0 -351
  64. package/templates/workflows/facility-doctor.yml +0 -174
  65. package/templates/workflows/facility-review.yml +0 -135
  66. package/templates/workflows/facility-security-sweep.yml +0 -204
  67. package/templates/workflows/facility-watchtower.yml +0 -87
package/src/render.mjs DELETED
@@ -1,66 +0,0 @@
1
- // Template rendering and managed-content insertion.
2
- //
3
- // Placeholders are {{UPPER_SNAKE}} with no spaces, so GitHub Actions
4
- // expressions (`${{ secrets.X }}` — spaces, lowercase, dots) never match and
5
- // pass through untouched.
6
-
7
- /**
8
- * Substitute {{VARS}} in a template. A placeholder alone on its line is a
9
- * block variable: an empty value removes the whole line, a multi-line value
10
- * lands verbatim. Unknown placeholders are left as-is.
11
- */
12
- export function render(template, vars) {
13
- const withBlocks = template.replace(
14
- /^[ \t]*\{\{([A-Z0-9_]+)\}\}[ \t]*\r?\n/gm,
15
- (match, name) => {
16
- const value = vars[name];
17
- if (value === undefined) return match;
18
- if (value === "") return "";
19
- return value.endsWith("\n") ? value : `${value}\n`;
20
- }
21
- );
22
- return withBlocks.replace(/\{\{([A-Z0-9_]+)\}\}/g, (match, name) => vars[name] ?? match);
23
- }
24
-
25
- /** True when `content` already contains the facility managed-block marker. */
26
- export function hasManagedBlock(content) {
27
- return content.includes("<!-- facility:start");
28
- }
29
-
30
- /** Append a managed block to existing file content (with a blank line). */
31
- export function appendManagedBlock(content, block) {
32
- const base = content.replace(/\s*$/, "");
33
- return base ? `${base}\n\n${block}` : block;
34
- }
35
-
36
- const MODULES_START = "<!-- facility:modules:start -->";
37
- const MODULES_END = "<!-- facility:modules:end -->";
38
-
39
- /** Insert a module's standard section between the modules markers. */
40
- export function insertModuleSection(standard, section, moduleTitle) {
41
- if (standard.includes(`(facility module)`) && standard.includes(`### ${moduleTitle} (facility module)`)) {
42
- return { content: standard, inserted: false };
43
- }
44
- const end = standard.indexOf(MODULES_END);
45
- if (end === -1) {
46
- // No markers (user removed them): append under a Modules heading instead.
47
- const block = `\n## Modules\n\n${section.trim()}\n`;
48
- return { content: `${standard.replace(/\s*$/, "")}\n${block}`, inserted: true };
49
- }
50
- const before = standard.slice(0, end).replace(/[ \t]*$/, "");
51
- const after = standard.slice(end);
52
- return { content: `${before}${section.trim()}\n\n${after}`, inserted: true };
53
- }
54
-
55
- const HOOK_MARKER = "/* facility:module-rules */";
56
-
57
- /** Splice a module's hook rules at the marker in protect-files.mjs. */
58
- export function insertHookRules(hookSource, fragment, moduleName) {
59
- const sentinel = `facility module: ${moduleName}`;
60
- if (hookSource.includes(sentinel)) return { content: hookSource, inserted: false };
61
- if (!hookSource.includes(HOOK_MARKER)) return { content: hookSource, inserted: false };
62
- return {
63
- content: hookSource.replace(HOOK_MARKER, `${fragment.trim()}\n\n${HOOK_MARKER}`),
64
- inserted: true,
65
- };
66
- }
@@ -1,71 +0,0 @@
1
- {
2
- "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
- "includeCoAuthoredBy": false,
4
- "permissions": {
5
- "defaultMode": "default",
6
- "allow": [
7
- {{ALLOW_CHECKS_JSON}}
8
- "Bash(node guards/run.mjs)",
9
- "Bash(git status:*)",
10
- "Bash(git diff:*)",
11
- "Bash(git log:*)",
12
- "Bash(git show:*)",
13
- "Bash(git add:*)",
14
- "Bash(git restore:*)",
15
- "Bash(git branch:*)",
16
- "Bash(git switch:*)",
17
- "Bash(git checkout:*)",
18
- "Bash(git commit:*)",
19
- "Bash(git fetch:*)",
20
- "Bash(git rev-parse:*)",
21
- "Bash(gh pr view:*)",
22
- "Bash(gh pr list:*)",
23
- "Bash(gh pr diff:*)",
24
- "Bash(gh pr checks:*)",
25
- "Bash(gh issue view:*)",
26
- "Bash(gh issue list:*)",
27
- "Bash(gh run list:*)",
28
- "Bash(gh run view:*)"
29
- ],
30
- "ask": [
31
- "Bash(git push:*)",
32
- "Bash(gh pr create:*)",
33
- "Bash(gh pr merge:*)",
34
- "Bash(gh issue create:*)"
35
- ],
36
- "deny": [
37
- "Read(.env)",
38
- "Read(.env.local)",
39
- "Read(.env.*.local)",
40
- "Bash(sudo:*)",
41
- "Bash(rm -rf /)",
42
- "Bash(rm -rf ~)",
43
- "Bash(git push --force:*)",
44
- "Bash(git push -f:*)"
45
- ]
46
- },
47
- "hooks": {
48
- "PreToolUse": [
49
- {
50
- "matcher": "Bash",
51
- "hooks": [
52
- {
53
- "type": "command",
54
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/protect-branch.mjs\"",
55
- "timeout": 30
56
- }
57
- ]
58
- },
59
- {
60
- "matcher": "Edit|Write|MultiEdit",
61
- "hooks": [
62
- {
63
- "type": "command",
64
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/protect-files.mjs\"",
65
- "timeout": 30
66
- }
67
- ]
68
- }
69
- ]
70
- }
71
- }
@@ -1,157 +0,0 @@
1
- #!/usr/bin/env node
2
- // Generated by facility — deterministic /builder delivery acceptance.
3
- import { execFileSync } from "node:child_process";
4
- import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
- import { join } from "node:path";
6
-
7
- const mode = process.argv[2];
8
- const repo = required("GITHUB_REPOSITORY");
9
- const defaultBranch = required("DEFAULT_BRANCH");
10
- const receiptDir = join(required("RUNNER_TEMP"), "facility-delivery");
11
- const receiptPath = join(receiptDir, "receipt.json");
12
- const conventionalSubject =
13
- /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\((?=\S)[^()\r\n]*[^\s()\r\n]\))?!?: \S.*$/;
14
-
15
- if (mode === "discover") discover();
16
- else if (mode === "finalize") finalize();
17
- else throw new Error("Usage: verify.mjs discover|finalize");
18
-
19
- function discover() {
20
- const event = JSON.parse(readFileSync(required("GITHUB_EVENT_PATH"), "utf8"));
21
- const startedAt = required("FACILITY_STARTED_AT");
22
- const startSha = required("FACILITY_START_SHA");
23
- const existingPr =
24
- event.pull_request?.number ?? (event.issue?.pull_request ? event.issue.number : undefined);
25
- const pr = existingPr ? pull(existingPr) : newlyOpenedPull(startedAt);
26
-
27
- assert(pr.state === "open", `PR #${pr.number} is not open`);
28
- assert(!pr.draft, `PR #${pr.number} is still a draft; automated review would not run`);
29
- assert(
30
- pr.base?.ref === defaultBranch,
31
- `PR #${pr.number} targets ${pr.base?.ref}, not ${defaultBranch}`,
32
- );
33
- assert(pr.head?.repo?.full_name === repo, `PR #${pr.number} is not a same-repository PR`);
34
- assert(
35
- pr.user?.type === "Bot",
36
- `PR #${pr.number} is not bot-authored; address-review would be disabled`,
37
- );
38
- assert(
39
- /^(feature|fix|chore|ci|docs|refactor|perf|test|build|revert)\/[a-z0-9][a-z0-9._/-]*$/.test(
40
- pr.head.ref,
41
- ),
42
- `branch ${pr.head?.ref} is not semantic or still carries an agent/tool prefix`,
43
- );
44
- assert(pr.head?.sha !== startSha, "builder reported success without delivering a new commit");
45
-
46
- const commits = ghJson(["api", `repos/${repo}/pulls/${pr.number}/commits?per_page=100`]);
47
- const startIndex = commits.findIndex((commit) => commit.sha === startSha);
48
- if (existingPr) {
49
- assert(
50
- startIndex >= 0,
51
- `PR #${pr.number} no longer contains its pre-builder head; history was rewritten`,
52
- );
53
- }
54
- const delivered = startIndex >= 0 ? commits.slice(startIndex + 1) : commits;
55
- assert(delivered.length > 0, `PR #${pr.number} contains no builder-delivered commits`);
56
-
57
- for (const commit of delivered) {
58
- const message = commit.commit?.message ?? "";
59
- const subject = message.split(/\r?\n/, 1)[0];
60
- assert(
61
- !hasForbiddenSubjectCharacters(subject) && conventionalSubject.test(subject),
62
- `commit ${commit.sha} is not Conventional Commits: ${subject}`,
63
- );
64
- assert(
65
- !/^Co-authored-by:/im.test(message),
66
- `commit ${commit.sha} contains a Co-authored-by trailer`,
67
- );
68
- assert(
69
- commit.commit?.verification?.verified === true,
70
- `commit ${commit.sha} is not verified by GitHub`,
71
- );
72
- }
73
-
74
- mkdirSync(receiptDir, { recursive: true });
75
- const receipt = {
76
- schema: "facility.delivery.v1",
77
- repository: repo,
78
- pullRequest: { number: pr.number, url: pr.html_url, base: pr.base.ref },
79
- branch: pr.head.ref,
80
- headSha: pr.head.sha,
81
- author: pr.user.login,
82
- deliveredCommits: delivered.map((commit) => commit.sha),
83
- startedAt,
84
- verification: "pending",
85
- };
86
- writeFileSync(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`);
87
- output("pr_number", String(pr.number));
88
- output("head_ref", pr.head.ref);
89
- output("head_sha", pr.head.sha);
90
- console.log(`Validated delivery metadata for ${pr.html_url} at ${pr.head.sha}.`);
91
- }
92
-
93
- function finalize() {
94
- const prNumber = required("FACILITY_PR_NUMBER");
95
- const headRef = required("FACILITY_HEAD_REF");
96
- const headSha = required("FACILITY_HEAD_SHA");
97
- const pr = pull(prNumber);
98
- assert(pr.head?.ref === headRef, `PR #${prNumber} head branch changed during verification`);
99
- assert(pr.head?.sha === headSha, `PR #${prNumber} head commit changed during verification`);
100
-
101
- const receipt = JSON.parse(readFileSync(receiptPath, "utf8"));
102
- receipt.verification = "passed";
103
- receipt.verifiedAt = new Date().toISOString();
104
- writeFileSync(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`);
105
- console.log(JSON.stringify(receipt));
106
- }
107
-
108
- function newlyOpenedPull(startedAt) {
109
- const pulls = ghJson([
110
- "api",
111
- `repos/${repo}/pulls?state=open&base=${encodeURIComponent(defaultBranch)}&sort=created&direction=desc&per_page=50`,
112
- ]).filter(
113
- (pr) =>
114
- pr.head?.repo?.full_name === repo &&
115
- pr.created_at >= startedAt &&
116
- pr.head?.sha !== process.env.FACILITY_START_SHA,
117
- );
118
- assert(
119
- pulls.length === 1,
120
- `expected exactly one new PR from this builder run; found ${pulls.length}`,
121
- );
122
- return pulls[0];
123
- }
124
-
125
- function pull(number) {
126
- return ghJson(["api", `repos/${repo}/pulls/${number}`]);
127
- }
128
-
129
- function ghJson(args) {
130
- return JSON.parse(execFileSync("gh", args, { encoding: "utf8" }));
131
- }
132
-
133
- function output(name, value) {
134
- appendFileSync(required("GITHUB_OUTPUT"), `${name}=${value}\n`);
135
- }
136
-
137
- function hasForbiddenSubjectCharacters(subject) {
138
- return [...subject].some((character) => {
139
- const codePoint = character.codePointAt(0) ?? 0;
140
- return (
141
- codePoint <= 0x1f ||
142
- (codePoint >= 0x7f && codePoint <= 0x9f) ||
143
- codePoint === 0x2028 ||
144
- codePoint === 0x2029
145
- );
146
- });
147
- }
148
-
149
- function required(name) {
150
- const value = process.env[name];
151
- if (!value) throw new Error(`${name} is required`);
152
- return value;
153
- }
154
-
155
- function assert(condition, message) {
156
- if (!condition) throw new Error(message);
157
- }