@theagilemonkeys/facility 0.11.4 → 0.12.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.
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
@@ -1,57 +0,0 @@
1
- // facility module: database
2
- //
3
- // Applied migrations are immutable. This guard fails when a commit range
4
- // modifies or deletes an existing migration file instead of adding a new one.
5
- // In CI it diffs against the PR base; locally it diffs against the merge-base
6
- // with the default branch.
7
- import { execFileSync } from "node:child_process";
8
-
9
- // Directories that hold ordered migration files. Adjust to your stack.
10
- const MIGRATION_DIRS = [
11
- "migrations/",
12
- "supabase/migrations/",
13
- "db/migrations/",
14
- "prisma/migrations/",
15
- ];
16
-
17
- // key: "<path>", value: written reason (e.g. "squashed baseline, 2026-01").
18
- const ALLOWLIST = {};
19
-
20
- function git(args) {
21
- try {
22
- return execFileSync("git", args, {
23
- encoding: "utf8",
24
- stdio: ["ignore", "pipe", "ignore"],
25
- }).trim();
26
- } catch {
27
- return "";
28
- }
29
- }
30
-
31
- export default {
32
- name: "migrations-immutable",
33
- description: "existing migration files are never edited or deleted, only added",
34
- run() {
35
- const base =
36
- process.env.GITHUB_BASE_SHA ||
37
- git(["merge-base", "HEAD", `origin/${process.env.DEFAULT_BRANCH || "main"}`]) ||
38
- git(["merge-base", "HEAD", "main"]);
39
- if (!base) return []; // shallow clone or fresh repo — nothing to compare against
40
-
41
- const out = git(["diff", "--name-status", base, "HEAD"]);
42
- const violations = [];
43
- for (const row of out.split("\n").filter(Boolean)) {
44
- const [status, ...paths] = row.split("\t");
45
- const path = paths[paths.length - 1];
46
- if (!MIGRATION_DIRS.some((dir) => path.startsWith(dir))) continue;
47
- if (status.startsWith("M") || status.startsWith("D") || status.startsWith("R")) {
48
- if (ALLOWLIST[path]) continue;
49
- violations.push({
50
- file: path,
51
- message: `migration ${status.startsWith("D") ? "deleted" : "modified"} — migrations are append-only; write a new one`,
52
- });
53
- }
54
- }
55
- return violations;
56
- },
57
- };
@@ -1,10 +0,0 @@
1
- // facility module: database — migration immutability (PreToolUse)
2
- if (/(^|\/)(supabase\/|db\/|prisma\/)?migrations\/[^/]+\.(sql|js|ts|rb|py)$/.test(filePath)) {
3
- const tool = payload?.tool_name ?? "";
4
- const isEdit = tool === "Edit" || tool === "MultiEdit";
5
- if (isEdit) {
6
- block(
7
- "Migration files are immutable once written. Create a NEW timestamped migration instead.",
8
- );
9
- }
10
- }
@@ -1,25 +0,0 @@
1
- {
2
- "name": "database",
3
- "title": "Database",
4
- "description": "Migrations are immutable and append-only; access control by default; privileged credentials stay out of user-facing paths.",
5
- "standardSection": "standard-section.md",
6
- "files": [
7
- {
8
- "from": "agents/data-security-reviewer.md",
9
- "to": ".claude/agents/data-security-reviewer.md"
10
- },
11
- {
12
- "from": "guards/migrations-immutable.mjs",
13
- "to": "guards/migrations-immutable.mjs"
14
- },
15
- {
16
- "from": "guards/migration-versions.mjs",
17
- "to": "guards/migration-versions.mjs"
18
- },
19
- {
20
- "from": "commands/new-migration.md",
21
- "to": ".claude/commands/new-migration.md"
22
- }
23
- ],
24
- "hookRules": "hooks/protect-migrations.fragment.mjs"
25
- }
@@ -1,20 +0,0 @@
1
- ### Database (facility module)
2
-
3
- Migrations are the durable state of the database. They are **append-only**:
4
- a change is a NEW migration, never an edit to one that may have been applied
5
- anywhere. The `migrations-immutable` guard and the `.claude/hooks` rules
6
- enforce this mechanically.
7
-
8
- - New tables and collections start closed: enable row/document-level access
9
- control where the platform supports it, deny by default, grant only the
10
- minimum required operations.
11
- - Authorization checks route through one shared helper layer — never inline
12
- token/claim parsing inside individual policies or queries.
13
- - Privileged credentials (service role, admin connections) stay out of
14
- user-facing and agent-facing read paths. Any privileged action lives in a
15
- reviewed application service with explicit permission checks and tests.
16
- - Update seed data whenever a feature needs realistic data to test locally.
17
- Seeds follow the domain: realistic names, states, relationships, and edge
18
- cases — not toy rows that only satisfy a test selector.
19
- - Empty results must be permission-safe: do not imply hidden data exists when
20
- access control returns nothing.
@@ -1,37 +0,0 @@
1
- ---
2
- name: design-reviewer
3
- description: Reviews UI changes against the repo's design system and checks that critical UI flows carry browser evidence. Use when a change adds or modifies UI, layout, components, or styling.
4
- tools: Read, Grep, Glob, Bash
5
- ---
6
-
7
- You review product UI against this repository's design system and confirm
8
- critical flows are verified in a browser. A UI that conflicts with the design
9
- system is a product-quality issue.
10
-
11
- ## Authoritative references
12
- - The design system source of truth declared in `STANDARD.md` (Design system
13
- section) — read it before judging.
14
- - The repo's design tokens / shared component layer — existing values come
15
- first.
16
-
17
- ## What to check
18
- 1. Existing design tokens and helpers are reused before any new value; no
19
- ad-hoc colors, spacing, or radii that bypass the token layer.
20
- 2. Components match the system's catalog: shapes, sizes, states, and
21
- hierarchy expressed the way the system prescribes.
22
- 3. **Evidence**: UI flow changes include a browser verification note
23
- (Playwright run, screenshots), or an explicit reason UI verification did
24
- not apply.
25
- 4. Responsive behavior holds at the product's breakpoints when layout, text
26
- fit, navigation, modals, or tables can be affected.
27
- 5. Empty, error, and loading states are designed, not accidental.
28
-
29
- ## How to verify
30
- - `grep` the diff for raw color/spacing literals that should be tokens.
31
- - Recommend the smallest browser run that covers the affected flows.
32
-
33
- ## Output contract
34
- List design-system violations and missing evidence by severity with file:line
35
- and the token/helper to use instead. Confirm which critical flows are covered
36
- by browser evidence. Flag only design-system/UX-correctness gaps, not
37
- subjective taste.
@@ -1,9 +0,0 @@
1
- {
2
- "name": "design-system",
3
- "title": "Design system",
4
- "description": "UI changes conform to your design system and critical flows carry browser evidence.",
5
- "standardSection": "standard-section.md",
6
- "files": [
7
- { "from": "agents/design-reviewer.md", "to": ".claude/agents/design-reviewer.md" }
8
- ]
9
- }
@@ -1,15 +0,0 @@
1
- ### Design system (facility module)
2
-
3
- A UI that conflicts with the design system is a product-quality issue, not a
4
- matter of taste. Declare your source of truth here (a spec doc, a tokens
5
- file, a published package) — the design reviewer reads it before judging.
6
-
7
- - Use existing design tokens and shared components before inventing values.
8
- Ad-hoc colors, spacing, and radii that bypass the token layer are defects.
9
- - UI flow changes include browser evidence — a Playwright run, screenshots,
10
- or a recorded check — or an explicit reason why UI verification did not
11
- apply. "It compiles" is not evidence for layout.
12
- - Verify responsive behavior at the breakpoints your product supports when a
13
- change can affect layout, text fit, navigation, modals, or tables.
14
- - Empty states, error states, and loading states are part of the surface —
15
- design them, don't let them fall out of the happy path.
package/src/add.mjs DELETED
@@ -1,77 +0,0 @@
1
- // `facility add <module>` — install a quality module: its STANDARD.md section,
2
- // its reviewer subagent, its guards, and its hook rules.
3
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
4
- import { dirname, isAbsolute, join, resolve } from "node:path";
5
- import { insertHookRules, insertModuleSection } from "./render.mjs";
6
- import { fail, heading, ok, skip, warn } from "./ui.mjs";
7
-
8
- export async function addModule(name, { dir, pkgRoot, banner = true }) {
9
- const moduleDir =
10
- name.startsWith(".") || isAbsolute(name) ? resolve(dir, name) : join(pkgRoot, "modules", name);
11
-
12
- const manifestPath = join(moduleDir, "module.json");
13
- if (!existsSync(manifestPath)) {
14
- const available = readdirSync(join(pkgRoot, "modules"), { withFileTypes: true })
15
- .filter((entry) => entry.isDirectory())
16
- .map((entry) => entry.name)
17
- .join(", ");
18
- fail(`Unknown module "${name}". Available: ${available} (or a local path).`);
19
- return 1;
20
- }
21
- const module = JSON.parse(readFileSync(manifestPath, "utf8"));
22
- if (banner) heading(`Adding module: ${module.title}`);
23
-
24
- // 1. Files (reviewer subagents, guards) — never overwrite.
25
- for (const file of module.files ?? []) {
26
- const target = join(dir, file.to);
27
- if (existsSync(target)) {
28
- skip(`${file.to} exists — left untouched`);
29
- continue;
30
- }
31
- mkdirSync(dirname(target), { recursive: true });
32
- writeFileSync(target, readFileSync(join(moduleDir, file.from), "utf8"));
33
- ok(file.to);
34
- }
35
-
36
- // 2. STANDARD.md section between the facility:modules markers.
37
- const standardPath = join(dir, "STANDARD.md");
38
- if (module.standardSection && existsSync(standardPath)) {
39
- const section = readFileSync(join(moduleDir, module.standardSection), "utf8");
40
- const { content, inserted } = insertModuleSection(readFileSync(standardPath, "utf8"), section, module.title);
41
- if (inserted) {
42
- writeFileSync(standardPath, content);
43
- ok(`STANDARD.md — "${module.title}" section`);
44
- } else {
45
- skip(`STANDARD.md already has the "${module.title}" section`);
46
- }
47
- } else if (module.standardSection) {
48
- warn("STANDARD.md not found — run `facility init` first.");
49
- }
50
-
51
- // 3. Hook rules spliced into protect-files.mjs at the module marker.
52
- if (module.hookRules) {
53
- const hookPath = join(dir, ".claude/hooks/protect-files.mjs");
54
- if (existsSync(hookPath)) {
55
- const fragment = readFileSync(join(moduleDir, module.hookRules), "utf8");
56
- const { content, inserted } = insertHookRules(readFileSync(hookPath, "utf8"), fragment, module.name);
57
- if (inserted) {
58
- writeFileSync(hookPath, content);
59
- ok(".claude/hooks/protect-files.mjs — rules spliced");
60
- } else {
61
- skip(".claude/hooks/protect-files.mjs already has this module's rules");
62
- }
63
- }
64
- }
65
-
66
- // 4. Record it in the manifest.
67
- const facilityManifestPath = join(dir, ".facility.json");
68
- if (existsSync(facilityManifestPath)) {
69
- const manifest = JSON.parse(readFileSync(facilityManifestPath, "utf8"));
70
- if (!manifest.modules?.includes(module.name)) {
71
- manifest.modules = [...(manifest.modules ?? []), module.name];
72
- writeFileSync(facilityManifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
73
- }
74
- }
75
-
76
- return 0;
77
- }