@momentiq/dark-factory-cli 2.17.0 → 3.0.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 (136) hide show
  1. package/dist/adapters/codex-sdk.d.ts +30 -5
  2. package/dist/adapters/codex-sdk.d.ts.map +1 -1
  3. package/dist/adapters/codex-sdk.js +50 -15
  4. package/dist/adapters/codex-sdk.js.map +1 -1
  5. package/dist/adapters/cursor-sdk.d.ts.map +1 -1
  6. package/dist/adapters/cursor-sdk.js +14 -2
  7. package/dist/adapters/cursor-sdk.js.map +1 -1
  8. package/dist/adapters/index.d.ts +1 -0
  9. package/dist/adapters/index.d.ts.map +1 -1
  10. package/dist/adapters/index.js +6 -0
  11. package/dist/adapters/index.js.map +1 -1
  12. package/dist/adapters/mode-scope.d.ts +86 -0
  13. package/dist/adapters/mode-scope.d.ts.map +1 -0
  14. package/dist/adapters/mode-scope.js +456 -0
  15. package/dist/adapters/mode-scope.js.map +1 -0
  16. package/dist/branch-protection/audit_branch_protection.py +80 -2
  17. package/dist/cli.d.ts +5 -0
  18. package/dist/cli.d.ts.map +1 -1
  19. package/dist/cli.js +31 -1
  20. package/dist/cli.js.map +1 -1
  21. package/dist/commands/mode.d.ts +2 -0
  22. package/dist/commands/mode.d.ts.map +1 -0
  23. package/dist/commands/mode.js +235 -0
  24. package/dist/commands/mode.js.map +1 -0
  25. package/dist/commands/objectives.d.ts +27 -3
  26. package/dist/commands/objectives.d.ts.map +1 -1
  27. package/dist/commands/objectives.js +347 -53
  28. package/dist/commands/objectives.js.map +1 -1
  29. package/dist/commands/onboard.d.ts.map +1 -1
  30. package/dist/commands/onboard.js +3 -1
  31. package/dist/commands/onboard.js.map +1 -1
  32. package/dist/cycle-doc-validator/validate_cycle_doc.py +128 -11
  33. package/dist/doctor.d.ts.map +1 -1
  34. package/dist/doctor.js +15 -7
  35. package/dist/doctor.js.map +1 -1
  36. package/dist/evidence/index.d.ts +1 -0
  37. package/dist/evidence/index.d.ts.map +1 -1
  38. package/dist/evidence/index.js +5 -0
  39. package/dist/evidence/index.js.map +1 -1
  40. package/dist/evidence/ui-visual/embed.d.ts +65 -0
  41. package/dist/evidence/ui-visual/embed.d.ts.map +1 -0
  42. package/dist/evidence/ui-visual/embed.js +176 -0
  43. package/dist/evidence/ui-visual/embed.js.map +1 -0
  44. package/dist/evidence/ui-visual/index.d.ts +2 -0
  45. package/dist/evidence/ui-visual/index.d.ts.map +1 -0
  46. package/dist/evidence/ui-visual/index.js +6 -0
  47. package/dist/evidence/ui-visual/index.js.map +1 -0
  48. package/dist/index.d.ts +1 -1
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/mcp/cycle-doc/parser.d.ts +3 -3
  53. package/dist/mcp/cycle-doc/parser.d.ts.map +1 -1
  54. package/dist/mcp/cycle-doc/parser.js +2 -2
  55. package/dist/mcp/cycle-doc/parser.js.map +1 -1
  56. package/dist/mcp/tools/doctor.d.ts +5 -0
  57. package/dist/mcp/tools/doctor.d.ts.map +1 -1
  58. package/dist/mcp/tools/doctor.js +10 -1
  59. package/dist/mcp/tools/doctor.js.map +1 -1
  60. package/dist/mcp/tools/review-bypass.d.ts +5 -0
  61. package/dist/mcp/tools/review-bypass.d.ts.map +1 -1
  62. package/dist/mcp/tools/review-bypass.js +9 -1
  63. package/dist/mcp/tools/review-bypass.js.map +1 -1
  64. package/dist/mode/apply.d.ts +8 -0
  65. package/dist/mode/apply.d.ts.map +1 -0
  66. package/dist/mode/apply.js +118 -0
  67. package/dist/mode/apply.js.map +1 -0
  68. package/dist/mode/context.d.ts +50 -0
  69. package/dist/mode/context.d.ts.map +1 -0
  70. package/dist/mode/context.js +127 -0
  71. package/dist/mode/context.js.map +1 -0
  72. package/dist/mode/critic-registration.d.ts +66 -0
  73. package/dist/mode/critic-registration.d.ts.map +1 -0
  74. package/dist/mode/critic-registration.js +279 -0
  75. package/dist/mode/critic-registration.js.map +1 -0
  76. package/dist/mode/guard.d.ts +15 -0
  77. package/dist/mode/guard.d.ts.map +1 -0
  78. package/dist/mode/guard.js +160 -0
  79. package/dist/mode/guard.js.map +1 -0
  80. package/dist/mode/init.d.ts +17 -0
  81. package/dist/mode/init.d.ts.map +1 -0
  82. package/dist/mode/init.js +147 -0
  83. package/dist/mode/init.js.map +1 -0
  84. package/dist/mode/loadConfig.d.ts +34 -0
  85. package/dist/mode/loadConfig.d.ts.map +1 -0
  86. package/dist/mode/loadConfig.js +38 -0
  87. package/dist/mode/loadConfig.js.map +1 -0
  88. package/dist/mode/prompts/designer-baseline.md +47 -0
  89. package/dist/mode/resolve.d.ts +24 -0
  90. package/dist/mode/resolve.d.ts.map +1 -0
  91. package/dist/mode/resolve.js +65 -0
  92. package/dist/mode/resolve.js.map +1 -0
  93. package/dist/mode/scope-policy.d.ts +41 -0
  94. package/dist/mode/scope-policy.d.ts.map +1 -0
  95. package/dist/mode/scope-policy.js +97 -0
  96. package/dist/mode/scope-policy.js.map +1 -0
  97. package/dist/mode/status.d.ts +14 -0
  98. package/dist/mode/status.d.ts.map +1 -0
  99. package/dist/mode/status.js +13 -0
  100. package/dist/mode/status.js.map +1 -0
  101. package/dist/onboard/seeders/design-md.d.ts +3 -0
  102. package/dist/onboard/seeders/design-md.d.ts.map +1 -0
  103. package/dist/onboard/seeders/design-md.js +138 -0
  104. package/dist/onboard/seeders/design-md.js.map +1 -0
  105. package/dist/onboard/seeders/index.d.ts +8 -0
  106. package/dist/onboard/seeders/index.d.ts.map +1 -1
  107. package/dist/onboard/seeders/index.js +3 -0
  108. package/dist/onboard/seeders/index.js.map +1 -1
  109. package/dist/onboard/validate.d.ts +36 -1
  110. package/dist/onboard/validate.d.ts.map +1 -1
  111. package/dist/onboard/validate.js +136 -42
  112. package/dist/onboard/validate.js.map +1 -1
  113. package/dist/skills/config.d.ts +2 -110
  114. package/dist/skills/config.d.ts.map +1 -1
  115. package/dist/skills/config.js +20 -58
  116. package/dist/skills/config.js.map +1 -1
  117. package/dist/skills/index.d.ts +1 -1
  118. package/dist/skills/index.d.ts.map +1 -1
  119. package/dist/skills/index.js +1 -1
  120. package/dist/skills/index.js.map +1 -1
  121. package/dist/skills/install.d.ts.map +1 -1
  122. package/dist/skills/install.js +7 -0
  123. package/dist/skills/install.js.map +1 -1
  124. package/package.json +3 -3
  125. package/skills/designer-brief/SKILL.md.tmpl +74 -0
  126. package/skills/designer-brief/skill.json +17 -0
  127. package/skills/designer-build/SKILL.md.tmpl +63 -0
  128. package/skills/designer-build/skill.json +17 -0
  129. package/skills/designer-handoff/SKILL.md.tmpl +64 -0
  130. package/skills/designer-handoff/skill.json +17 -0
  131. package/skills/designer-ship/SKILL.md.tmpl +77 -0
  132. package/skills/designer-ship/skill.json +17 -0
  133. package/skills/objectives/SKILL.md.tmpl +34 -6
  134. package/skills/verify/producer/ui-visual-surfaces.ts +261 -0
  135. package/skills/verify/producer/ui-visual.config.ts +22 -0
  136. package/skills/verify/producer/ui-visual.producer.spec.ts +137 -0
@@ -0,0 +1,118 @@
1
+ // Cycle 27 Phase B — `df mode apply <name>`: generate the per-user Claude
2
+ // Code overlay in `.claude/settings.local.json` (git-ignored — engineers on
3
+ // the same repo are untouched; the designer opts in on their own machine).
4
+ //
5
+ // Merge discipline: read-modify-write JSON that only ADDS what the mode
6
+ // needs — env.DF_MODE, the two df hooks, a destructive-git deny belt, and
7
+ // MCP enablement — and never disturbs unrelated user keys. Idempotent by
8
+ // containment checks, not by rewriting.
9
+ import { execFileSync } from "node:child_process";
10
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { join, resolve } from "node:path";
12
+ import { parse as parseYaml } from "yaml";
13
+ import { parseDarkFactoryConfig } from "@momentiq/dark-factory-schemas";
14
+ import { resolveModeWithConfig } from "./resolve.js";
15
+ const GUARD_COMMAND = '"$CLAUDE_PROJECT_DIR"/node_modules/.bin/df mode guard --hook';
16
+ const CONTEXT_COMMAND = '"$CLAUDE_PROJECT_DIR"/node_modules/.bin/df mode context --hook';
17
+ const DENY_BELT = [
18
+ "Bash(git push --force:*)",
19
+ "Bash(git push -f:*)",
20
+ "Bash(git reset --hard:*)",
21
+ "Bash(git clean:*)",
22
+ ];
23
+ function ensureHook(hooks, event, command, matcher) {
24
+ const entries = (hooks[event] ??= []);
25
+ const present = entries.some((e) => e.hooks.some((h) => h.command === command));
26
+ if (present)
27
+ return;
28
+ const entry = { hooks: [{ type: "command", command }] };
29
+ if (matcher !== undefined)
30
+ entry.matcher = matcher;
31
+ entries.push(entry);
32
+ }
33
+ export function applyMode(repoRoot, modeName) {
34
+ // Validate against the catalog first — applying an unknown mode would
35
+ // produce a session that immediately fail-closes every file tool.
36
+ const configPath = join(repoRoot, "darkfactory.yaml");
37
+ const raw = existsSync(configPath)
38
+ ? (parseYaml(readFileSync(configPath, "utf8")) ?? {})
39
+ : {};
40
+ resolveModeWithConfig(parseDarkFactoryConfig(raw), modeName);
41
+ const dir = join(repoRoot, ".claude");
42
+ const settingsPath = join(dir, "settings.local.json");
43
+ const created = !existsSync(settingsPath);
44
+ const settings = created
45
+ ? {}
46
+ : JSON.parse(readFileSync(settingsPath, "utf8"));
47
+ settings.env = { ...(settings.env ?? {}), DF_MODE: modeName };
48
+ const hooks = (settings.hooks ??= {});
49
+ ensureHook(hooks, "PreToolUse", GUARD_COMMAND, "Edit|Write|MultiEdit|NotebookEdit|Bash");
50
+ ensureHook(hooks, "SessionStart", CONTEXT_COMMAND);
51
+ const permissions = (settings.permissions ??= {});
52
+ const deny = new Set(permissions.deny ?? []);
53
+ for (const rule of DENY_BELT)
54
+ deny.add(rule);
55
+ permissions.deny = [...deny];
56
+ const servers = new Set(settings.enabledMcpjsonServers ?? []);
57
+ servers.add("dark-factory");
58
+ settings.enabledMcpjsonServers = [...servers];
59
+ mkdirSync(dir, { recursive: true });
60
+ writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
61
+ ensureLocallyIgnored(repoRoot);
62
+ return { status: "applied", modeName, settingsPath, created };
63
+ }
64
+ const OVERLAY_IGNORE_ENTRY = ".claude/settings.local.json";
65
+ /**
66
+ * Resolve the git **common** directory for `repoRoot` — the directory that
67
+ * owns `info/exclude`. In a normal clone this is `<repoRoot>/.git`, but in a
68
+ * linked worktree (or a submodule) `<repoRoot>/.git` is a POINTER FILE
69
+ * (`gitdir: …`), not a directory, and the per-clone exclude lives in the
70
+ * SHARED common dir, never under the worktree-private git dir. `git rev-parse
71
+ * --git-common-dir` reports the right path in every layout — absolute for a
72
+ * linked worktree, relative (`.git`) for a normal clone — and `resolve`
73
+ * returns an absolute segment as-is while joining the relative one onto
74
+ * `repoRoot`, so a single call covers both. Best-effort: returns null when git
75
+ * is unavailable or `repoRoot` is not a work tree, so apply skips ignoring
76
+ * rather than crashing.
77
+ */
78
+ function resolveGitCommonDir(repoRoot) {
79
+ try {
80
+ const out = execFileSync("git", ["rev-parse", "--git-common-dir"], {
81
+ cwd: repoRoot,
82
+ encoding: "utf8",
83
+ stdio: ["ignore", "pipe", "ignore"],
84
+ }).trim();
85
+ return out.length === 0 ? null : resolve(repoRoot, out);
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ /**
92
+ * The overlay is documented as per-user and git-ignored. Claude Code's own
93
+ * scaffolds usually ignore it, but nothing guarantees a given consumer repo
94
+ * does — so make it true structurally via the git common dir's `info/exclude`,
95
+ * which is per-clone (never committed), exactly matching the overlay's per-user
96
+ * semantics. Best-effort: a non-git directory — or a linked worktree whose git
97
+ * dir cannot be resolved — is left untouched, never crashing apply.
98
+ */
99
+ function ensureLocallyIgnored(repoRoot) {
100
+ // Fast local guard: only act when THIS directory roots a work tree (`.git`
101
+ // is a dir in a normal clone, a pointer FILE in a linked worktree/submodule).
102
+ // Skipping the git probe for a plain directory keeps apply best-effort and
103
+ // stops git's parent search from writing to an unrelated ancestor repo.
104
+ if (!existsSync(join(repoRoot, ".git")))
105
+ return;
106
+ const gitCommonDir = resolveGitCommonDir(repoRoot);
107
+ if (gitCommonDir === null)
108
+ return;
109
+ const infoDir = join(gitCommonDir, "info");
110
+ const excludePath = join(infoDir, "exclude");
111
+ const current = existsSync(excludePath) ? readFileSync(excludePath, "utf8") : "";
112
+ if (current.split(/\r?\n/).includes(OVERLAY_IGNORE_ENTRY))
113
+ return;
114
+ mkdirSync(infoDir, { recursive: true });
115
+ const separator = current.length === 0 || current.endsWith("\n") ? "" : "\n";
116
+ writeFileSync(excludePath, `${current}${separator}${OVERLAY_IGNORE_ENTRY}\n`);
117
+ }
118
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/mode/apply.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { ModeConfig } from "@momentiq/dark-factory-schemas";
2
+ /**
3
+ * Load the baseline persona asset. Lives alongside this module in both src
4
+ * (tests) and dist (runtime — the build's copy-assets step ships it), so an
5
+ * import.meta.url-relative read works in both.
6
+ */
7
+ export declare function loadBaselinePersona(): string;
8
+ export interface ResolvedModeLike {
9
+ modeName: string;
10
+ mode: ModeConfig;
11
+ }
12
+ /**
13
+ * The strict repo-relative path predicate used for the DISPLAY path only —
14
+ * pure lexical, no filesystem access. `buildModeContext` echoes the configured
15
+ * `designMd` into the LLM-consumed SessionStart note, so it gates on this and
16
+ * shows a neutral placeholder for anything that is not a genuine strict
17
+ * repo-relative path. True only for a non-empty, length-bounded value built
18
+ * from safe path characters (letters, digits, `.` `_` `-` `/`) that is NOT
19
+ * absolute and contains NO empty/current/parent (`.`/`..`) segments. The
20
+ * character grammar keeps the value free of spaces, newlines, backticks, and
21
+ * markdown metacharacters, so a hostile catalog value cannot express multi-word
22
+ * instructions or open a new markdown block in the injected context.
23
+ *
24
+ * This is deliberately STRICTER than what `safeDesignMdPath` accepts for reads:
25
+ * read-safety asks "is this a real file inside the repo?" (spaces are fine);
26
+ * display-safety asks "is this safe to echo into model context?" (they are not).
27
+ * Keeping them separate avoids narrowing the public `designSystem.designMd`
28
+ * string contract while still hardening the injected note.
29
+ */
30
+ export declare function isStrictRepoRelativePath(p: string): boolean;
31
+ /**
32
+ * Resolve the catalog's `designSystem.designMd` value safely: it is a
33
+ * REPO-RELATIVE path by contract, and this hook injects the file's contents
34
+ * into session context — so the check must hold against the REAL filesystem,
35
+ * not just lexically. Returns the resolved path only when:
36
+ * - the value is relative and its lexical resolution stays under repoRoot,
37
+ * - the file exists and is a regular file, and
38
+ * - its realpath (symlinks followed) still lives under the repo's realpath
39
+ * (a repo-local DESIGN.md symlinked to ~/.ssh/id_rsa returns null).
40
+ * A missing file also returns null — "unseeded" is a normal state the
41
+ * caller narrates; it must never be distinguishable from "blocked" in ways
42
+ * that leak what exists outside the repo.
43
+ *
44
+ * This accepts ANY real repo-relative file (paths with spaces included): it
45
+ * enforces filesystem safety, NOT the stricter display grammar, so it does not
46
+ * narrow the public `designSystem.designMd` string contract.
47
+ */
48
+ export declare function safeDesignMdPath(repoRoot: string, designMd: string): string | null;
49
+ export declare function buildModeContext(resolved: ResolvedModeLike, designMdContent: string | undefined): string;
50
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/mode/context.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAIjE;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;CAClB;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAQ3D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoBlF;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,gBAAgB,EAC1B,eAAe,EAAE,MAAM,GAAG,SAAS,GAClC,MAAM,CAqCR"}
@@ -0,0 +1,127 @@
1
+ // Cycle 27 Phase B — `df mode context --hook`: build the SessionStart
2
+ // additionalContext for an operator-mode session. Persona (baseline tier,
3
+ // shipped as a dist asset with the DF-PROFILE sentinel) + live mode summary
4
+ // + the repo's DESIGN.md as design doctrine.
5
+ import { readFileSync, realpathSync, statSync } from "node:fs";
6
+ import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ const HERE = dirname(fileURLToPath(import.meta.url));
9
+ /**
10
+ * Load the baseline persona asset. Lives alongside this module in both src
11
+ * (tests) and dist (runtime — the build's copy-assets step ships it), so an
12
+ * import.meta.url-relative read works in both.
13
+ */
14
+ export function loadBaselinePersona() {
15
+ return readFileSync(resolve(HERE, "prompts", "designer-baseline.md"), "utf8");
16
+ }
17
+ const STRICT_REL_PATH = /^[A-Za-z0-9._\/-]+$/;
18
+ /**
19
+ * The strict repo-relative path predicate used for the DISPLAY path only —
20
+ * pure lexical, no filesystem access. `buildModeContext` echoes the configured
21
+ * `designMd` into the LLM-consumed SessionStart note, so it gates on this and
22
+ * shows a neutral placeholder for anything that is not a genuine strict
23
+ * repo-relative path. True only for a non-empty, length-bounded value built
24
+ * from safe path characters (letters, digits, `.` `_` `-` `/`) that is NOT
25
+ * absolute and contains NO empty/current/parent (`.`/`..`) segments. The
26
+ * character grammar keeps the value free of spaces, newlines, backticks, and
27
+ * markdown metacharacters, so a hostile catalog value cannot express multi-word
28
+ * instructions or open a new markdown block in the injected context.
29
+ *
30
+ * This is deliberately STRICTER than what `safeDesignMdPath` accepts for reads:
31
+ * read-safety asks "is this a real file inside the repo?" (spaces are fine);
32
+ * display-safety asks "is this safe to echo into model context?" (they are not).
33
+ * Keeping them separate avoids narrowing the public `designSystem.designMd`
34
+ * string contract while still hardening the injected note.
35
+ */
36
+ export function isStrictRepoRelativePath(p) {
37
+ if (p.length === 0 || p.length > 200)
38
+ return false;
39
+ if (!STRICT_REL_PATH.test(p))
40
+ return false;
41
+ if (isAbsolute(p))
42
+ return false;
43
+ for (const segment of p.split("/")) {
44
+ if (segment === "" || segment === "." || segment === "..")
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ /**
50
+ * Resolve the catalog's `designSystem.designMd` value safely: it is a
51
+ * REPO-RELATIVE path by contract, and this hook injects the file's contents
52
+ * into session context — so the check must hold against the REAL filesystem,
53
+ * not just lexically. Returns the resolved path only when:
54
+ * - the value is relative and its lexical resolution stays under repoRoot,
55
+ * - the file exists and is a regular file, and
56
+ * - its realpath (symlinks followed) still lives under the repo's realpath
57
+ * (a repo-local DESIGN.md symlinked to ~/.ssh/id_rsa returns null).
58
+ * A missing file also returns null — "unseeded" is a normal state the
59
+ * caller narrates; it must never be distinguishable from "blocked" in ways
60
+ * that leak what exists outside the repo.
61
+ *
62
+ * This accepts ANY real repo-relative file (paths with spaces included): it
63
+ * enforces filesystem safety, NOT the stricter display grammar, so it does not
64
+ * narrow the public `designSystem.designMd` string contract.
65
+ */
66
+ export function safeDesignMdPath(repoRoot, designMd) {
67
+ if (isAbsolute(designMd))
68
+ return null;
69
+ const resolved = resolve(repoRoot, designMd);
70
+ const rel = relative(repoRoot, resolved);
71
+ // Reject only TRUE parent-traversal: the bare `..`, or a path whose first
72
+ // segment is `..` (`../x`). `rel.startsWith("..")` alone would also reject a
73
+ // legitimate repo-local path whose first segment merely BEGINS with `..`
74
+ // (e.g. `..docs/DESIGN.md`), so key on the separator — matching the realpath
75
+ // check below.
76
+ if (rel === "" || rel === ".." || rel.startsWith(`..${sep}`))
77
+ return null;
78
+ try {
79
+ if (!statSync(resolved).isFile())
80
+ return null;
81
+ const realTarget = realpathSync(resolved);
82
+ const realRoot = realpathSync(repoRoot);
83
+ const realRel = relative(realRoot, realTarget);
84
+ if (realRel === "" || realRel === ".." || realRel.startsWith(`..${sep}`))
85
+ return null;
86
+ return resolved;
87
+ }
88
+ catch {
89
+ return null; // missing, unreadable, or unresolvable — treat as unseeded
90
+ }
91
+ }
92
+ export function buildModeContext(resolved, designMdContent) {
93
+ const { modeName, mode } = resolved;
94
+ const persona = loadBaselinePersona();
95
+ const summary = [
96
+ `## Active operator mode: ${modeName}`,
97
+ "",
98
+ `- Editable areas (writeScopes): ${(mode.writeScopes ?? []).join(", ") || "(none configured)"}`,
99
+ `- Protected areas (always off-limits, they win on overlap): ${(mode.protectedPaths ?? []).join(", ") || "(none)"}`,
100
+ `- Dependency policy: ${mode.dependencyPolicy ?? "(unset)"} — dependency changes ${mode.dependencyPolicy === "none" ? "always escalate via handoff" : "may escalate"}`,
101
+ mode.devServer?.command
102
+ ? `- Dev server for visual verification: \`${mode.devServer.command}\`${mode.devServer.url ? ` → ${mode.devServer.url}` : ""}`
103
+ : `- Dev server: not configured — ask an engineer to set modes.${modeName}.devServer before visual work`,
104
+ `- Visual evidence: ${mode.evidence?.visual ?? "(unset)"}`,
105
+ ].join("\n");
106
+ // designSystem.designMd is a repo-relative path that may be nested (not
107
+ // necessarily the repo root), so the "unseeded" note names the configured
108
+ // location rather than hardcoding "the repo root". This value is echoed into
109
+ // the LLM-consumed SessionStart context, where markdown fencing is NOT a
110
+ // prompt-injection boundary — so echo it only when it is a strict
111
+ // repo-relative path (isStrictRepoRelativePath); anything else (absolute,
112
+ // `.`/`..` traversal, or containing spaces/newlines/markdown metachars) is
113
+ // shown as a neutral placeholder rather than echoed, so it can neither leak a
114
+ // non-repo-relative path nor inject text. This display gate is intentionally
115
+ // stricter than the read resolver, which accepts any real repo-relative file.
116
+ const rawDesignMd = mode.designSystem?.designMd ?? "DESIGN.md";
117
+ const designMdRel = isStrictRepoRelativePath(rawDesignMd)
118
+ ? rawDesignMd
119
+ : "(configured path omitted: unsafe characters)";
120
+ const designDoc = designMdContent
121
+ ? `## DESIGN.md (design source of truth)\n\n${designMdContent}`
122
+ : `## DESIGN.md\n\nNo DESIGN.md was found at \`${designMdRel}\` — it has not been seeded yet. ` +
123
+ `Follow the repo's existing visual patterns strictly, and suggest running ` +
124
+ `\`df onboard\` to an engineer so design doctrine gets a home.`;
125
+ return [persona, summary, designDoc].join("\n\n---\n\n");
126
+ }
127
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/mode/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /** The critic id used for the ModeScope critic in `.agent-review/config.json`. */
2
+ export declare const MODE_SCOPE_CRITIC_ID = "mode-scope-critic";
3
+ export interface ModeScopeRegistrationResult {
4
+ /** Whether the config was modified (false = already registered / idempotent). */
5
+ changed: boolean;
6
+ /** Human-readable descriptions of each mutation, for `df mode init` output. */
7
+ changes: string[];
8
+ }
9
+ /**
10
+ * The critic entry appended to `critics[]`. Deterministic critic: no auth,
11
+ * no vendor SDK. `required: true` — the scope critic is a HARD GATE, so its
12
+ * blockers must block under EVERY aggregation policy. Under `block-if-any`
13
+ * the gate demotes `required: false` critics' findings to warnings (report.ts
14
+ * / gate.ts key on `config.critics.filter(c => c.required)`), so a
15
+ * `required: false` scope critic would NOT block an out-of-scope `Mode:` PR
16
+ * there. `required: true` is also safe under `min-complete-quorum`: that path
17
+ * (`quorumAggregateVerdict`) never consults the `required` flag, and because
18
+ * the adapter ALWAYS completes it never trips the "required critic errored →
19
+ * block" rule — so it only ever adds the veto its blocker already carries, and
20
+ * the per-path quorum bumps below stay exact. `runtime: "local"` is metadata —
21
+ * the same deterministic entry is referenced by both the local and cloud
22
+ * profiles (mirrors how `cursor-*` is shared across profiles).
23
+ */
24
+ export declare function buildModeScopeCriticEntry(): Record<string, unknown>;
25
+ /**
26
+ * Register the ModeScope critic into a raw parsed `.agent-review/config.json`
27
+ * object. Mutates `raw` in place AND returns a change report. The caller
28
+ * validates the mutated object through `parseAgentReviewConfig` before
29
+ * persisting it.
30
+ *
31
+ * "Present" is NOT "safe". A pre-existing mode-scope entry can itself be
32
+ * UNSAFE — an older CLI (or a hand edit) may have added it `required: false`
33
+ * (its blockers are demoted to warnings under block-if-any) or left it out of a
34
+ * profile (it does not run at all under profile-filtered runs). So rather than
35
+ * no-op the instant an entry is present, we CONVERGE it to the required shape:
36
+ * promote `required: true`, repair a missing id, and ensure the id is in every
37
+ * profile. Only a FULLY compliant entry is a true no-op (`changed: false`).
38
+ *
39
+ * Quorum is preserved EXACTLY ONCE per scope, keyed on the absent→present
40
+ * transition (`bumpRootQuorum` for the root fleet, `ensureCriticInEveryProfile`
41
+ * per profile), so a converge re-run never double-counts.
42
+ */
43
+ export declare function registerModeScopeCritic(raw: unknown): ModeScopeRegistrationResult;
44
+ export interface CriticRegistrationOutcome {
45
+ /** Resolved path to `.agent-review/config.json`. */
46
+ configPath: string;
47
+ status: "registered" | "already-registered" | "absent" | "error";
48
+ /** Change descriptions when status === "registered". */
49
+ changes: string[];
50
+ /** Present when status === "error". */
51
+ error?: string;
52
+ }
53
+ /**
54
+ * Register the ModeScope critic into `<repoRoot>/.agent-review/config.json`.
55
+ *
56
+ * - `absent`: no config.json — the consumer hasn't run `df onboard`. Skipped
57
+ * (not an error): the catalog seed still stands and re-running init after
58
+ * onboarding completes registration.
59
+ * - `already-registered`: the critic is already present — idempotent no-op.
60
+ * - `registered`: patched + written.
61
+ * - `error`: the config is malformed OR the patched config fails canonical
62
+ * validation. The on-disk file is left UNTOUCHED (fail-closed); the error
63
+ * is reported, not thrown, so `df mode init` degrades gracefully.
64
+ */
65
+ export declare function applyModeScopeCriticRegistration(repoRoot: string): CriticRegistrationOutcome;
66
+ //# sourceMappingURL=critic-registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"critic-registration.d.ts","sourceRoot":"","sources":["../../src/mode/critic-registration.ts"],"names":[],"mappings":"AAwCA,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD,MAAM,WAAW,2BAA2B;IAC1C,iFAAiF;IACjF,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASnE;AAqED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,2BAA2B,CA4FjF;AAKD,MAAM,WAAW,yBAAyB;IACxC,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,YAAY,GAAG,oBAAoB,GAAG,QAAQ,GAAG,OAAO,CAAC;IACjE,wDAAwD;IACxD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,MAAM,GAAG,yBAAyB,CA4C5F"}