@danmoisan/drm-copilot-mcp 1.1.15 → 1.1.17

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 (71) hide show
  1. package/out/mcp-server.js +639 -27
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +12 -6
  4. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +4 -5
  5. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +3 -3
  6. package/resources/claude-customizations/.claude/hooks/enforce-batch-budget-route.ps1 +139 -0
  7. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +221 -0
  8. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +42 -66
  9. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +58 -10
  10. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +144 -0
  11. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +36 -47
  12. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +179 -27
  13. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -38
  14. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +4 -4
  15. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +58 -10
  16. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +59 -9
  17. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +114 -37
  18. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +95 -66
  19. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +99 -64
  20. package/resources/claude-customizations/.claude/lib/bash/abandon-parallel-item.sh +176 -0
  21. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +193 -14
  22. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusGlob.psm1 +51 -28
  23. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +5 -8
  24. package/resources/claude-customizations/.claude/lib/parallel-drift/Invoke-ParallelDriftDetection.ps1 +336 -0
  25. package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDrift.psm1 +467 -0
  26. package/resources/claude-customizations/.claude/lib/parallel-drift/ParallelDriftHalt.psm1 +388 -0
  27. package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +53 -32
  28. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +19 -4
  29. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +497 -0
  30. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +21 -0
  31. package/resources/claude-customizations/.claude/rules/powershell.md +3 -3
  32. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +2 -0
  33. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -0
  34. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +7 -4
  35. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +6 -3
  36. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +3 -1
  37. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +32 -18
  38. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +10 -10
  39. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +6 -6
  40. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +11 -35
  41. package/resources/claude-customizations/pack-manifests/core.json +8 -0
  42. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +1 -2
  43. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +2 -3
  44. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +1 -2
  45. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +10 -34
  46. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +2 -3
  47. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +2 -3
  48. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +2 -3
  49. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +2 -3
  50. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +2 -3
  51. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +2 -3
  52. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +2 -2
  53. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +3 -3
  54. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +3 -3
  55. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +3 -3
  56. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +3 -3
  57. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +3 -3
  58. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +3 -3
  59. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  60. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-batch-budget-route.ps1 +139 -0
  61. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +165 -80
  62. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +169 -79
  63. package/resources/codex-and-agents-customizations/pack-manifests/core.json +1 -0
  64. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +7 -7
  65. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +8 -10
  66. package/resources/customizations/.github/agents/python-orchestrator.agent.md +4 -4
  67. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +7 -8
  68. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +2 -2
  69. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +3 -3
  70. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +3 -3
  71. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +12 -0
package/out/mcp-server.js CHANGED
@@ -1146,9 +1146,9 @@ var require_util = __commonJS({
1146
1146
  }
1147
1147
  }
1148
1148
  exports2.eachItem = eachItem;
1149
- function makeMergeEvaluated({ mergeNames, mergeToName, mergeValues: mergeValues2, resultToName }) {
1149
+ function makeMergeEvaluated({ mergeNames, mergeToName, mergeValues: mergeValues3, resultToName }) {
1150
1150
  return (gen, from, to, toName) => {
1151
- const res = to === void 0 ? from : to instanceof codegen_1.Name ? (from instanceof codegen_1.Name ? mergeNames(gen, from, to) : mergeToName(gen, from, to), to) : from instanceof codegen_1.Name ? (mergeToName(gen, to, from), from) : mergeValues2(from, to);
1151
+ const res = to === void 0 ? from : to instanceof codegen_1.Name ? (from instanceof codegen_1.Name ? mergeNames(gen, from, to) : mergeToName(gen, from, to), to) : from instanceof codegen_1.Name ? (mergeToName(gen, to, from), from) : mergeValues3(from, to);
1152
1152
  return toName === codegen_1.Name && !(res instanceof codegen_1.Name) ? resultToName(gen, res) : res;
1153
1153
  };
1154
1154
  }
@@ -19984,6 +19984,237 @@ var BlastRadiusDeriveFileSystem = class {
19984
19984
  }
19985
19985
  };
19986
19986
 
19987
+ // ../../extensions/drm-copilot/src/lib/push-down/claude-blast-radius-overlay.ts
19988
+ var BLAST_RADIUS_OVERLAY_RELATIVE_PATH = "config/blast-radius.local.json";
19989
+ var STRING_LIST_KEYS = /* @__PURE__ */ new Set([
19990
+ "shared_surfaces",
19991
+ "shared_surface_globs",
19992
+ "mandate_reads",
19993
+ "mergeable_paths",
19994
+ "path_roots"
19995
+ ]);
19996
+ var MODULES_KEY = "modules";
19997
+ var VERSION_KEY = "version";
19998
+ var BlastRadiusOverlayError = class extends Error {
19999
+ /** Relative path of the document that could not be composed. */
20000
+ path;
20001
+ /**
20002
+ * @param path Relative path of the offending document.
20003
+ * @param detail Reason appended to the message.
20004
+ */
20005
+ constructor(path16, detail) {
20006
+ super(
20007
+ `Blast-radius overlay ${path16} was not applied; ${BLAST_RADIUS_RELATIVE_PATH} was not written: ${detail}`
20008
+ );
20009
+ this.name = "BlastRadiusOverlayError";
20010
+ this.path = path16;
20011
+ }
20012
+ };
20013
+ function isJsonObject(value) {
20014
+ return value !== null && typeof value === "object" && !Array.isArray(value);
20015
+ }
20016
+ function isStringList(value) {
20017
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
20018
+ }
20019
+ function parseObject(text2, path16) {
20020
+ let parsed;
20021
+ try {
20022
+ parsed = JSON.parse(text2);
20023
+ } catch (error2) {
20024
+ const detail = error2 instanceof Error ? error2.message : String(error2);
20025
+ throw new BlastRadiusOverlayError(path16, `not valid JSON (${detail})`);
20026
+ }
20027
+ if (!isJsonObject(parsed)) {
20028
+ throw new BlastRadiusOverlayError(
20029
+ path16,
20030
+ "document root is not a JSON object"
20031
+ );
20032
+ }
20033
+ return parsed;
20034
+ }
20035
+ function unionStrings(base, overlay) {
20036
+ const composed = [...base];
20037
+ const seen = new Set(base.filter((item) => typeof item === "string"));
20038
+ for (const item of overlay) {
20039
+ if (!seen.has(item)) {
20040
+ seen.add(item);
20041
+ composed.push(item);
20042
+ }
20043
+ }
20044
+ return composed;
20045
+ }
20046
+ function composeStringList(key, base, overlay, overlayPath) {
20047
+ if (!isStringList(overlay)) {
20048
+ throw new BlastRadiusOverlayError(
20049
+ overlayPath,
20050
+ `key "${key}" must be a list of strings`
20051
+ );
20052
+ }
20053
+ return unionStrings(Array.isArray(base) ? base : [], overlay);
20054
+ }
20055
+ function composeModules(base, overlay, overlayPath) {
20056
+ if (!isJsonObject(overlay) || !Object.values(overlay).every((globs) => isStringList(globs))) {
20057
+ throw new BlastRadiusOverlayError(
20058
+ overlayPath,
20059
+ `key "${MODULES_KEY}" must map module names to lists of strings`
20060
+ );
20061
+ }
20062
+ const merged = { ...isJsonObject(base) ? base : {} };
20063
+ for (const [name, globs] of Object.entries(overlay)) {
20064
+ merged[name] = globs;
20065
+ }
20066
+ const composed = {};
20067
+ for (const name of Object.keys(merged).sort(compareOrdinal)) {
20068
+ const globs = merged[name] ?? [];
20069
+ composed[name] = globs;
20070
+ for (const glob of Array.isArray(globs) ? globs : []) {
20071
+ if (typeof glob === "string" && FORBIDDEN_GLOBS.includes(glob)) {
20072
+ throw new BlastRadiusGuardError(name, glob);
20073
+ }
20074
+ }
20075
+ }
20076
+ return composed;
20077
+ }
20078
+ function mergeValues2(base, overlay, dottedKey, overlayPath) {
20079
+ if (isJsonObject(base) && isJsonObject(overlay)) {
20080
+ const merged = {};
20081
+ for (const [key, value] of Object.entries(base)) {
20082
+ const overlayValue = overlay[key];
20083
+ merged[key] = overlayValue === void 0 ? value : mergeValues2(
20084
+ value,
20085
+ overlayValue,
20086
+ `${dottedKey}.${key}`,
20087
+ overlayPath
20088
+ );
20089
+ }
20090
+ for (const [key, value] of Object.entries(overlay)) {
20091
+ if (!(key in merged)) {
20092
+ merged[key] = value;
20093
+ }
20094
+ }
20095
+ return merged;
20096
+ }
20097
+ if (Array.isArray(base) && Array.isArray(overlay)) {
20098
+ if (!isStringList(overlay)) {
20099
+ throw new BlastRadiusOverlayError(
20100
+ overlayPath,
20101
+ `key "${dottedKey}" must be a list of strings`
20102
+ );
20103
+ }
20104
+ return unionStrings(base, overlay);
20105
+ }
20106
+ return overlay;
20107
+ }
20108
+ function composeKey(key, base, overlay, overlayPath) {
20109
+ if (STRING_LIST_KEYS.has(key)) {
20110
+ return composeStringList(key, base, overlay, overlayPath);
20111
+ }
20112
+ if (key === MODULES_KEY) {
20113
+ return composeModules(base, overlay, overlayPath);
20114
+ }
20115
+ return base === void 0 ? overlay : mergeValues2(base, overlay, key, overlayPath);
20116
+ }
20117
+ function composeBlastRadiusOverlay(baseText, overlayText, overlayPath) {
20118
+ if (overlayText === null) {
20119
+ return baseText;
20120
+ }
20121
+ const overlay = parseObject(overlayText, overlayPath);
20122
+ const base = parseObject(baseText, BLAST_RADIUS_RELATIVE_PATH);
20123
+ if (VERSION_KEY in overlay && overlay[VERSION_KEY] !== base[VERSION_KEY]) {
20124
+ throw new BlastRadiusOverlayError(
20125
+ overlayPath,
20126
+ `key "${VERSION_KEY}" does not match the base version`
20127
+ );
20128
+ }
20129
+ const composed = {};
20130
+ for (const [key, value] of Object.entries(base)) {
20131
+ const overlayValue = overlay[key];
20132
+ composed[key] = key === VERSION_KEY || overlayValue === void 0 ? value : composeKey(key, value, overlayValue, overlayPath);
20133
+ }
20134
+ for (const [key, value] of Object.entries(overlay)) {
20135
+ if (!(key in composed)) {
20136
+ composed[key] = composeKey(key, void 0, value, overlayPath);
20137
+ }
20138
+ }
20139
+ return `${JSON.stringify(composed, null, 2)}
20140
+ `;
20141
+ }
20142
+ function normalizePosix6(value) {
20143
+ return value.replace(/\\/g, "/").replace(/\/+$/, "");
20144
+ }
20145
+ function relativeToPosix6(path16, root) {
20146
+ const normalizedPath = normalizePosix6(path16);
20147
+ const normalizedRoot = normalizePosix6(root);
20148
+ if (normalizedPath === normalizedRoot) {
20149
+ return "";
20150
+ }
20151
+ const prefix = `${normalizedRoot}/`;
20152
+ return normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : null;
20153
+ }
20154
+ var BlastRadiusOverlayFileSystem = class {
20155
+ inner;
20156
+ destinationRoot;
20157
+ overlayRelativePath;
20158
+ targetRelativePath;
20159
+ /**
20160
+ * @param inner The wrapped adapter performing real I/O.
20161
+ * @param destinationRoot Destination workspace root (POSIX path).
20162
+ * @param overlayRelativePath Destination-relative overlay path.
20163
+ * @param targetRelativePath Destination-relative path the overlay composes
20164
+ * onto.
20165
+ */
20166
+ constructor(inner, destinationRoot, overlayRelativePath = BLAST_RADIUS_OVERLAY_RELATIVE_PATH, targetRelativePath = BLAST_RADIUS_RELATIVE_PATH) {
20167
+ this.inner = inner;
20168
+ this.destinationRoot = normalizePosix6(destinationRoot);
20169
+ this.overlayRelativePath = overlayRelativePath;
20170
+ this.targetRelativePath = targetRelativePath;
20171
+ }
20172
+ /** @inheritdoc */
20173
+ listFiles(root) {
20174
+ return this.inner.listFiles(root);
20175
+ }
20176
+ /** @inheritdoc */
20177
+ isDir(path16) {
20178
+ return this.inner.isDir(path16);
20179
+ }
20180
+ /** @inheritdoc */
20181
+ isFile(path16) {
20182
+ return this.inner.isFile(path16);
20183
+ }
20184
+ /** @inheritdoc */
20185
+ readTextFile(path16) {
20186
+ return this.inner.readTextFile(path16);
20187
+ }
20188
+ /** @inheritdoc */
20189
+ ensureDir(path16) {
20190
+ this.inner.ensureDir(path16);
20191
+ }
20192
+ /**
20193
+ * Write a file, composing the destination overlay onto the target path.
20194
+ *
20195
+ * @param path Absolute destination POSIX path.
20196
+ * @param content Content the layers above want to publish.
20197
+ * @throws BlastRadiusOverlayError When the overlay cannot be composed; the
20198
+ * destination target keeps its prior bytes.
20199
+ * @throws BlastRadiusGuardError When a composed module carries a forbidden
20200
+ * glob; the destination target keeps its prior bytes.
20201
+ */
20202
+ writeTextFile(path16, content) {
20203
+ if (relativeToPosix6(path16, this.destinationRoot) !== this.targetRelativePath) {
20204
+ this.inner.writeTextFile(path16, content);
20205
+ return;
20206
+ }
20207
+ const overlayPath = `${this.destinationRoot}/${this.overlayRelativePath}`;
20208
+ const overlayText = this.inner.isFile(overlayPath) ? this.inner.readTextFile(overlayPath) : null;
20209
+ const composed = composeBlastRadiusOverlay(
20210
+ content,
20211
+ overlayText,
20212
+ this.overlayRelativePath
20213
+ );
20214
+ this.inner.writeTextFile(path16, composed);
20215
+ }
20216
+ };
20217
+
19987
20218
  // ../../extensions/drm-copilot/src/lib/push-down/claude-gitignore-merge.ts
19988
20219
  var CLAUDE_GITIGNORE_RELATIVE_PATH = ".gitignore";
19989
20220
  var CLAUDE_GITIGNORE_BEGIN_SENTINEL = "# BEGIN drm-copilot managed ignores";
@@ -20043,12 +20274,363 @@ function toDocument(lines) {
20043
20274
  return `${lines.join(LINE_SEPARATOR)}${LINE_SEPARATOR}`;
20044
20275
  }
20045
20276
 
20277
+ // ../../extensions/drm-copilot/src/lib/push-down/claude-exclusion-manifest.ts
20278
+ var EXCLUSION_MANIFEST_RELATIVE_PATH = ".push-down-exclusions";
20279
+ var ExclusionManifestError = class extends Error {
20280
+ /** Destination-relative manifest path. */
20281
+ path;
20282
+ /** 1-based offending line, or `undefined` when the whole file is at fault. */
20283
+ line;
20284
+ /**
20285
+ * @param path Destination-relative manifest path.
20286
+ * @param line Offending line, or `undefined` for a whole-file condition.
20287
+ * @param reason Human-readable reason.
20288
+ */
20289
+ constructor(path16, line, reason) {
20290
+ const location = line === void 0 ? path16 : `${path16} line ${String(line)}`;
20291
+ super(`Malformed push-down exclusion manifest ${location}: ${reason}`);
20292
+ this.name = "ExclusionManifestError";
20293
+ this.path = path16;
20294
+ this.line = line;
20295
+ }
20296
+ };
20297
+ var BYTE_ORDER_MARK = "\uFEFF";
20298
+ var REPLACEMENT_CHARACTER = "\uFFFD";
20299
+ var LINE_SPLIT = /\r?\n/;
20300
+ var REPEATED_SLASH = /\/{2,}/g;
20301
+ var DRIVE_PREFIX = /^[A-Za-z]:/;
20302
+ function hasWildcard(normalized) {
20303
+ return normalized.includes("*") || normalized.includes("?");
20304
+ }
20305
+ function escapeRegexCharacter(character) {
20306
+ return /[\\^$.*+?()[\]{}|/-]/.test(character) ? `\\${character}` : character;
20307
+ }
20308
+ function globToRegexText(pattern) {
20309
+ const parts = [];
20310
+ let index = 0;
20311
+ while (index < pattern.length) {
20312
+ if (pattern.startsWith("**", index)) {
20313
+ parts.push(".*");
20314
+ index += 2;
20315
+ continue;
20316
+ }
20317
+ const character = pattern.charAt(index);
20318
+ if (character === "*") {
20319
+ parts.push("[^/]*");
20320
+ } else if (character === "?") {
20321
+ parts.push("[^/]");
20322
+ } else {
20323
+ parts.push(escapeRegexCharacter(character));
20324
+ }
20325
+ index += 1;
20326
+ }
20327
+ return parts.join("");
20328
+ }
20329
+ function matchesGlob(pattern, candidate) {
20330
+ return new RegExp(`^(?:${globToRegexText(pattern)})$`).test(candidate);
20331
+ }
20332
+ function normalizeExclusionEntry(raw) {
20333
+ let text2 = raw.trim().replace(/\\/g, "/");
20334
+ if (text2.startsWith("./")) {
20335
+ text2 = text2.slice(2);
20336
+ }
20337
+ return text2.replace(REPEATED_SLASH, "/");
20338
+ }
20339
+ function malformedReason(normalized) {
20340
+ const checks = [
20341
+ [normalized === "", "entry is empty after normalization"],
20342
+ [
20343
+ normalized.startsWith("/") || DRIVE_PREFIX.test(normalized),
20344
+ "entry is an absolute path"
20345
+ ],
20346
+ [normalized.split("/").includes(".."), "entry contains a '..' segment"],
20347
+ [normalized.startsWith("!"), "negation entries are not supported"],
20348
+ [
20349
+ normalized.includes("[") || normalized.includes("]"),
20350
+ "character classes are not supported"
20351
+ ],
20352
+ [
20353
+ hasWildcard(normalized) && normalized.endsWith("/"),
20354
+ "a wildcard entry must not end with '/'"
20355
+ ],
20356
+ [normalized.includes(REPLACEMENT_CHARACTER), "text is not valid UTF-8"]
20357
+ ];
20358
+ const failed = checks.find(([isFailed]) => isFailed);
20359
+ return failed === void 0 ? void 0 : failed[1];
20360
+ }
20361
+ function classify(normalized) {
20362
+ if (hasWildcard(normalized)) {
20363
+ return "glob";
20364
+ }
20365
+ return normalized.endsWith("/") ? "directory" : "exact";
20366
+ }
20367
+ function parseExclusionManifest(text2, path16) {
20368
+ const body = text2.startsWith(BYTE_ORDER_MARK) ? text2.slice(BYTE_ORDER_MARK.length) : text2;
20369
+ const entries = [];
20370
+ body.split(LINE_SPLIT).forEach((raw, offset) => {
20371
+ const line = offset + 1;
20372
+ const stripped = raw.trim();
20373
+ if (stripped === "" || stripped.startsWith("#")) {
20374
+ return;
20375
+ }
20376
+ const normalized = normalizeExclusionEntry(raw);
20377
+ const reason = malformedReason(normalized);
20378
+ if (reason !== void 0) {
20379
+ throw new ExclusionManifestError(path16, line, reason);
20380
+ }
20381
+ entries.push({ raw, normalized, kind: classify(normalized), line });
20382
+ });
20383
+ return { path: path16, entries };
20384
+ }
20385
+ function matchesExclusionEntry(entry, candidate) {
20386
+ if (entry.kind === "glob") {
20387
+ return matchesGlob(entry.normalized, candidate);
20388
+ }
20389
+ const base = entry.normalized.replace(/\/+$/, "");
20390
+ return candidate === base || candidate.startsWith(`${base}/`);
20391
+ }
20392
+ function planExclusions(payloadRelativePaths, manifest, destinationExists) {
20393
+ const kept = [];
20394
+ const skipped = [];
20395
+ for (const relativePath2 of payloadRelativePaths) {
20396
+ const entry = manifest.entries.find(
20397
+ (candidate) => matchesExclusionEntry(candidate, relativePath2)
20398
+ );
20399
+ if (entry === void 0) {
20400
+ kept.push(relativePath2);
20401
+ continue;
20402
+ }
20403
+ skipped.push({
20404
+ relativePath: relativePath2,
20405
+ entry: entry.normalized,
20406
+ line: entry.line,
20407
+ destinationStatus: destinationExists(relativePath2) ? "present" : "absent"
20408
+ });
20409
+ }
20410
+ const claimed = new Set(
20411
+ skipped.map((skip) => `${String(skip.line)}\0${skip.entry}`)
20412
+ );
20413
+ const unmatchedEntries = manifest.entries.filter(
20414
+ (entry) => !claimed.has(`${String(entry.line)}\0${entry.normalized}`)
20415
+ );
20416
+ return { kept, skipped, unmatchedEntries };
20417
+ }
20418
+ function assertManifestPathIsRootLevel(relativePath2, rootFolders) {
20419
+ if (relativePath2.includes("/")) {
20420
+ throw new Error(
20421
+ `Exclusion manifest path must be root-level: ${relativePath2}`
20422
+ );
20423
+ }
20424
+ const root = rootFolders.find((name) => relativePath2.startsWith(name));
20425
+ if (root !== void 0) {
20426
+ throw new Error(
20427
+ `Exclusion manifest path must not begin with published root ${root}: ${relativePath2}`
20428
+ );
20429
+ }
20430
+ }
20431
+
20432
+ // ../../extensions/drm-copilot/src/lib/push-down/claude-exclusion-filter.ts
20433
+ var EXCLUSION_CONFLICT_LINE_PREFIX = "push-down exclusion conflict:";
20434
+ var DEFAULT_ARTIFACT_DIRECTORY = "artifacts/claude-customizations";
20435
+ var ExclusionViolationError = class extends Error {
20436
+ /**
20437
+ * @param message Description of the refused write.
20438
+ */
20439
+ constructor(message) {
20440
+ super(message);
20441
+ this.name = "ExclusionViolationError";
20442
+ }
20443
+ };
20444
+ function normalizePosix7(value) {
20445
+ return value.replace(/\\/g, "/").replace(/\/+$/, "");
20446
+ }
20447
+ function relativeToPosix7(path16, root) {
20448
+ const normalizedPath = normalizePosix7(path16);
20449
+ const prefix = `${normalizePosix7(root)}/`;
20450
+ return normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : void 0;
20451
+ }
20452
+ function findFirstMatch(manifest, candidate) {
20453
+ return manifest.entries.find(
20454
+ (entry) => matchesExclusionEntry(entry, candidate)
20455
+ );
20456
+ }
20457
+ var ExclusionFilterFileSystem = class {
20458
+ /** Skip records in enumeration order. */
20459
+ skipped = [];
20460
+ inner;
20461
+ sourceRoot;
20462
+ destinationRoot;
20463
+ manifest;
20464
+ artifactPrefix;
20465
+ /**
20466
+ * @param inner Adapter the engine would otherwise use.
20467
+ * @param sourceRoot Root the engine enumerates payload files under.
20468
+ * @param destinationRoot Destination workspace root.
20469
+ * @param manifest Parsed manifest.
20470
+ * @param artifactDirectory Destination-relative summary-artifact directory
20471
+ * exempt from the write guard.
20472
+ */
20473
+ constructor(inner, sourceRoot, destinationRoot, manifest, artifactDirectory = DEFAULT_ARTIFACT_DIRECTORY) {
20474
+ this.inner = inner;
20475
+ this.sourceRoot = normalizePosix7(sourceRoot);
20476
+ this.destinationRoot = normalizePosix7(destinationRoot);
20477
+ this.manifest = manifest;
20478
+ this.artifactPrefix = `${normalizePosix7(artifactDirectory)}/`;
20479
+ }
20480
+ listFiles(root) {
20481
+ const listed = this.inner.listFiles(root);
20482
+ const relativeByPath = listed.map(
20483
+ (path16) => [path16, relativeToPosix7(path16, this.sourceRoot)]
20484
+ );
20485
+ const candidates = relativeByPath.flatMap(
20486
+ ([, relative2]) => relative2 === void 0 ? [] : [relative2]
20487
+ );
20488
+ const plan = planExclusions(
20489
+ candidates,
20490
+ this.manifest,
20491
+ (relative2) => this.inner.isFile(`${this.destinationRoot}/${relative2}`)
20492
+ );
20493
+ this.skipped.push(...plan.skipped);
20494
+ const kept = new Set(plan.kept);
20495
+ return relativeByPath.filter(([, relative2]) => relative2 === void 0 || kept.has(relative2)).map(([path16]) => path16);
20496
+ }
20497
+ isDir(path16) {
20498
+ return this.inner.isDir(path16);
20499
+ }
20500
+ isFile(path16) {
20501
+ return this.inner.isFile(path16);
20502
+ }
20503
+ readTextFile(path16) {
20504
+ return this.inner.readTextFile(path16);
20505
+ }
20506
+ ensureDir(path16) {
20507
+ this.inner.ensureDir(path16);
20508
+ }
20509
+ /**
20510
+ * Write through the inner adapter unless the target is excluded.
20511
+ *
20512
+ * @param path Target path.
20513
+ * @param content Text to write.
20514
+ * @throws ExclusionViolationError When the destination-relative target is
20515
+ * the manifest path or matches a manifest entry.
20516
+ */
20517
+ writeTextFile(path16, content) {
20518
+ const relative2 = relativeToPosix7(path16, this.destinationRoot);
20519
+ if (relative2 !== void 0 && !relative2.startsWith(this.artifactPrefix)) {
20520
+ if (relative2 === EXCLUSION_MANIFEST_RELATIVE_PATH) {
20521
+ throw new ExclusionViolationError(
20522
+ `Refusing to write the push-down exclusion manifest: ${relative2}`
20523
+ );
20524
+ }
20525
+ const entry = findFirstMatch(this.manifest, relative2);
20526
+ if (entry !== void 0) {
20527
+ throw new ExclusionViolationError(
20528
+ `Refusing to write excluded path ${relative2} (entry ${entry.normalized}, line ${String(entry.line)})`
20529
+ );
20530
+ }
20531
+ }
20532
+ this.inner.writeTextFile(path16, content);
20533
+ }
20534
+ };
20535
+ function readExclusionManifest(fs12, destinationRoot) {
20536
+ const manifestPath = `${normalizePosix7(destinationRoot)}/${EXCLUSION_MANIFEST_RELATIVE_PATH}`;
20537
+ if (!fs12.isFile(manifestPath)) {
20538
+ if (fs12.isDir(manifestPath)) {
20539
+ throw new ExclusionManifestError(
20540
+ EXCLUSION_MANIFEST_RELATIVE_PATH,
20541
+ void 0,
20542
+ "path is not a regular file"
20543
+ );
20544
+ }
20545
+ return void 0;
20546
+ }
20547
+ return parseExclusionManifest(
20548
+ fs12.readTextFile(manifestPath),
20549
+ EXCLUSION_MANIFEST_RELATIVE_PATH
20550
+ );
20551
+ }
20552
+ function buildExclusionReport(manifest, skipped) {
20553
+ const claimed = new Set(
20554
+ skipped.map((skip) => `${String(skip.line)}\0${skip.entry}`)
20555
+ );
20556
+ return {
20557
+ manifestPath: manifest.path,
20558
+ entries: manifest.entries,
20559
+ skipped: [...skipped],
20560
+ unmatchedEntries: manifest.entries.filter(
20561
+ (entry) => !claimed.has(`${String(entry.line)}\0${entry.normalized}`)
20562
+ )
20563
+ };
20564
+ }
20565
+ function renderExclusionLines(report) {
20566
+ const skippedLines = report.skipped.map((skip) => {
20567
+ const suffix2 = `${skip.relativePath} (entry ${skip.entry}, line ${String(skip.line)})`;
20568
+ return skip.destinationStatus === "present" ? `${EXCLUSION_CONFLICT_LINE_PREFIX} destination file present, not overwritten: ${suffix2}` : `push-down exclusion: skipped ${suffix2}`;
20569
+ });
20570
+ const unmatchedLines = report.unmatchedEntries.map(
20571
+ (entry) => `push-down exclusion: entry matched no payload path: ${entry.normalized} (line ${String(entry.line)})`
20572
+ );
20573
+ return [...skippedLines, ...unmatchedLines];
20574
+ }
20575
+ function exclusionReportPayload(report) {
20576
+ return {
20577
+ conflict_count: report.skipped.filter(
20578
+ (skip) => skip.destinationStatus === "present"
20579
+ ).length,
20580
+ entries: report.entries.map((entry) => entry.normalized),
20581
+ manifest_path: report.manifestPath,
20582
+ skipped: report.skipped.map((skip) => ({
20583
+ destination_status: skip.destinationStatus,
20584
+ entry: skip.entry,
20585
+ line: skip.line,
20586
+ relative_path: skip.relativePath
20587
+ })),
20588
+ skipped_count: report.skipped.length,
20589
+ unmatched_entries: report.unmatchedEntries.map((entry) => entry.normalized)
20590
+ };
20591
+ }
20592
+ function appendExclusionsToArtifact(fs12, artifactPath, report) {
20593
+ const payload = JSON.parse(fs12.readTextFile(artifactPath));
20594
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
20595
+ throw new Error(`Summary artifact is not a JSON object: ${artifactPath}`);
20596
+ }
20597
+ const document = {
20598
+ ...payload,
20599
+ exclusions: exclusionReportPayload(report)
20600
+ };
20601
+ fs12.writeTextFile(artifactPath, stringifySorted(document, 2));
20602
+ }
20603
+
20046
20604
  // ../../extensions/drm-copilot/src/lib/push-down/claude-customizations.ts
20047
20605
  var ARTIFACT_DIRECTORY4 = "artifacts/claude-customizations";
20048
20606
  var ROOT_FOLDERS2 = [".claude", "config"];
20607
+ assertManifestPathIsRootLevel(EXCLUSION_MANIFEST_RELATIVE_PATH, ROOT_FOLDERS2);
20049
20608
  var ROUTING_MERGE_RELATIVE_PATH = "config/orchestration-routing.json";
20050
20609
  var EXCLUDED_RELATIVE_PATHS = [
20051
- ".claude/settings.local.json"
20610
+ ".claude/settings.local.json",
20611
+ "config/blast-radius.local.json"
20612
+ ];
20613
+ var DESTINATION_WRITE_DECORATORS = [
20614
+ {
20615
+ relativePath: ROUTING_MERGE_RELATIVE_PATH,
20616
+ wrap: (inner, destinationRoot) => new RoutingMergeFileSystem(
20617
+ inner,
20618
+ destinationRoot,
20619
+ ROUTING_MERGE_RELATIVE_PATH
20620
+ )
20621
+ },
20622
+ {
20623
+ relativePath: BLAST_RADIUS_RELATIVE_PATH,
20624
+ wrap: (inner, destinationRoot) => new BlastRadiusOverlayFileSystem(inner, destinationRoot)
20625
+ },
20626
+ {
20627
+ relativePath: BLAST_RADIUS_RELATIVE_PATH,
20628
+ wrap: (inner, destinationRoot, { listEntries }) => listEntries === void 0 ? new BlastRadiusDeriveFileSystem(inner, destinationRoot) : new BlastRadiusDeriveFileSystem(
20629
+ inner,
20630
+ destinationRoot,
20631
+ listEntries
20632
+ )
20633
+ }
20052
20634
  ];
20053
20635
  var BUNDLE_ROOT_RELATIVE_DIR = "extensions/drm-copilot/resources/claude-customizations";
20054
20636
  var PACK_MANIFEST_SUBDIR2 = "pack-manifests";
@@ -20091,23 +20673,19 @@ function pushDownCustomizations4(options) {
20091
20673
  } = options;
20092
20674
  const effectiveSource = sourceRoot ?? repoRoot;
20093
20675
  const effectiveBundle = bundleRoot ?? joinPosix7(effectiveSource, BUNDLE_ROOT_RELATIVE_DIR);
20676
+ const manifest = readExclusionManifest(fs12, destinationRoot);
20094
20677
  const publishedPaths = resolvePublishedPaths2(
20095
20678
  packs ?? null,
20096
20679
  effectiveBundle,
20097
20680
  fs12
20098
20681
  );
20099
- const mergingFs = new RoutingMergeFileSystem(
20100
- fs12,
20101
- destinationRoot,
20102
- ROUTING_MERGE_RELATIVE_PATH
20103
- );
20104
- const derivingFs = listEntries === void 0 ? new BlastRadiusDeriveFileSystem(mergingFs, destinationRoot) : new BlastRadiusDeriveFileSystem(
20105
- mergingFs,
20106
- destinationRoot,
20107
- listEntries
20682
+ const decoratorOptions = listEntries === void 0 ? {} : { listEntries };
20683
+ const decoratedFs = DESTINATION_WRITE_DECORATORS.reduce(
20684
+ (inner, decorator) => decorator.wrap(inner, destinationRoot, decoratorOptions),
20685
+ fs12
20108
20686
  );
20109
20687
  const excludingFs = new ExcludingFileSystem(
20110
- derivingFs,
20688
+ decoratedFs,
20111
20689
  repoRoot,
20112
20690
  EXCLUDED_RELATIVE_PATHS,
20113
20691
  {
@@ -20119,10 +20697,17 @@ function pushDownCustomizations4(options) {
20119
20697
  variantRoot: effectiveBundle
20120
20698
  }
20121
20699
  );
20700
+ const exclusionFs = manifest === void 0 ? void 0 : new ExclusionFilterFileSystem(
20701
+ excludingFs,
20702
+ effectiveSource,
20703
+ destinationRoot,
20704
+ manifest,
20705
+ ARTIFACT_DIRECTORY4
20706
+ );
20122
20707
  const summary = pushDownCustomizations({
20123
20708
  repoRoot,
20124
20709
  destinationRoot,
20125
- fs: excludingFs,
20710
+ fs: exclusionFs ?? excludingFs,
20126
20711
  ...sourceRoot === void 0 ? {} : { sourceRoot },
20127
20712
  ...artifactRoot === void 0 ? {} : { artifactRoot },
20128
20713
  rootFolders: ROOT_FOLDERS2,
@@ -20130,19 +20715,41 @@ function pushDownCustomizations4(options) {
20130
20715
  rewriteReferences: passthroughRewrite2,
20131
20716
  ...clock === void 0 ? {} : { clock }
20132
20717
  });
20133
- deliverDestinationGitignore(fs12, destinationRoot);
20134
- return summary;
20718
+ const gitignoreSkip = deliverDestinationGitignore(
20719
+ fs12,
20720
+ destinationRoot,
20721
+ manifest
20722
+ );
20723
+ if (manifest === void 0 || exclusionFs === void 0) {
20724
+ return summary;
20725
+ }
20726
+ const skipped = gitignoreSkip === void 0 ? exclusionFs.skipped : [...exclusionFs.skipped, gitignoreSkip];
20727
+ const report = buildExclusionReport(manifest, skipped);
20728
+ appendExclusionsToArtifact(fs12, summary.artifactPath, report);
20729
+ return { ...summary, exclusions: report };
20135
20730
  }
20136
- function deliverDestinationGitignore(fs12, destinationRoot) {
20731
+ function deliverDestinationGitignore(fs12, destinationRoot, manifest) {
20137
20732
  const destinationPath = joinPosix7(
20138
20733
  destinationRoot,
20139
20734
  CLAUDE_GITIGNORE_RELATIVE_PATH
20140
20735
  );
20736
+ const entry = manifest?.entries.find(
20737
+ (candidate) => matchesExclusionEntry(candidate, CLAUDE_GITIGNORE_RELATIVE_PATH)
20738
+ );
20739
+ if (entry !== void 0) {
20740
+ return {
20741
+ relativePath: CLAUDE_GITIGNORE_RELATIVE_PATH,
20742
+ entry: entry.normalized,
20743
+ line: entry.line,
20744
+ destinationStatus: fs12.isFile(destinationPath) ? "present" : "absent"
20745
+ };
20746
+ }
20141
20747
  const currentText = fs12.isFile(destinationPath) ? fs12.readTextFile(destinationPath) : "";
20142
20748
  const mergedText = mergeClaudeGitignore(currentText);
20143
20749
  if (mergedText !== currentText) {
20144
20750
  fs12.writeTextFile(destinationPath, mergedText);
20145
20751
  }
20752
+ return void 0;
20146
20753
  }
20147
20754
 
20148
20755
  // ../../extensions/drm-copilot/src/lib/push-down/push-down-service-call.ts
@@ -20216,11 +20823,16 @@ function pushDownClaudeCustomizationsServiceCall(input) {
20216
20823
  ...input.memoryMode === void 0 ? {} : { memoryMode: input.memoryMode },
20217
20824
  ...input.clock === void 0 ? {} : { clock: input.clock }
20218
20825
  });
20826
+ const lines = summary.exclusions === void 0 ? [] : renderExclusionLines(summary.exclusions);
20827
+ for (const line of lines) {
20828
+ input.log?.(line);
20829
+ }
20219
20830
  return {
20220
20831
  tool: "push_down_claude_customizations",
20221
20832
  workspaceRoot: input.workspaceRoot,
20222
20833
  summary: "Pushed bundled Claude Code customizations into the destination workspace.",
20223
- artifacts: [normalizeGeneratedPath(summary.artifactPath)]
20834
+ artifacts: [normalizeGeneratedPath(summary.artifactPath)],
20835
+ ...lines.length > 0 ? { warnings: lines } : {}
20224
20836
  };
20225
20837
  }
20226
20838
 
@@ -28385,7 +28997,7 @@ function isPositiveInteger(value) {
28385
28997
  function isNonNegativeInteger(value) {
28386
28998
  return isIntegral(value) && value >= 0;
28387
28999
  }
28388
- function isStringList(value) {
29000
+ function isStringList2(value) {
28389
29001
  return Array.isArray(value) && value.every(isNonEmptyString2);
28390
29002
  }
28391
29003
  function inBoundedRange(value, minimum, maximum) {
@@ -28403,7 +29015,7 @@ function validateBlastRadiusBlock(radius, context) {
28403
29015
  }
28404
29016
  const errors = [];
28405
29017
  for (const field of BLAST_RADIUS_LIST_FIELDS) {
28406
- if (!isStringList(radius[field])) {
29018
+ if (!isStringList2(radius[field])) {
28407
29019
  errors.push(
28408
29020
  `${context} blast_radius.${field} must be a list of non-empty strings.`
28409
29021
  );
@@ -28828,14 +29440,14 @@ function validateDriftEvents(events, issueNums, context) {
28828
29440
  );
28829
29441
  }
28830
29442
  for (const field of ["declared", "observed"]) {
28831
- if (!isStringList(entry[field])) {
29443
+ if (!isStringList2(entry[field])) {
28832
29444
  errors.push(
28833
29445
  `${entryContext} ${field} must be a list of non-empty strings.`
28834
29446
  );
28835
29447
  }
28836
29448
  }
28837
29449
  const escapedPaths = entry["escaped_paths"];
28838
- if (!isStringList(escapedPaths) || !Array.isArray(escapedPaths) || escapedPaths.length === 0) {
29450
+ if (!isStringList2(escapedPaths) || !Array.isArray(escapedPaths) || escapedPaths.length === 0) {
28839
29451
  errors.push(
28840
29452
  `${entryContext} escaped_paths must be a non-empty list of non-empty strings.`
28841
29453
  );
@@ -33418,7 +34030,7 @@ function resolveReadinessSignal(fs12, featureDir) {
33418
34030
  }
33419
34031
  return [null, null];
33420
34032
  }
33421
- function relativeToPosix6(root, path16) {
34033
+ function relativeToPosix8(root, path16) {
33422
34034
  const normalizedRoot = toPosixPath2(root).replace(/\/+$/u, "");
33423
34035
  const normalized = toPosixPath2(path16);
33424
34036
  if (normalized.startsWith(`${normalizedRoot}/`)) {
@@ -33451,7 +34063,7 @@ function discoverCanonicalEvidenceFiles(fs12, root, feature) {
33451
34063
  for (const pattern of CANONICAL_GLOBS) {
33452
34064
  for (const candidate of fs12.glob(featureRoot, pattern)) {
33453
34065
  if (fs12.isFile(candidate)) {
33454
- discovered.add(relativeToPosix7(normalizedRoot, candidate));
34066
+ discovered.add(relativeToPosix9(normalizedRoot, candidate));
33455
34067
  }
33456
34068
  }
33457
34069
  }
@@ -33544,7 +34156,7 @@ function parseIntegerStrict(value) {
33544
34156
  }
33545
34157
  return Number.parseInt(value, 10);
33546
34158
  }
33547
- function relativeToPosix7(root, absolute) {
34159
+ function relativeToPosix9(root, absolute) {
33548
34160
  const normalized = toPosixPath2(absolute);
33549
34161
  if (normalized.startsWith(`${root}/`)) {
33550
34162
  return normalized.slice(root.length + 1);
@@ -33732,11 +34344,11 @@ function buildContextFiles(params) {
33732
34344
  const contextFiles = [];
33733
34345
  for (const path16 of candidatePaths) {
33734
34346
  if (fs12.exists(path16)) {
33735
- contextFiles.push(relativeToPosix6(root, path16));
34347
+ contextFiles.push(relativeToPosix8(root, path16));
33736
34348
  }
33737
34349
  }
33738
34350
  if (readinessSource !== null) {
33739
- contextFiles.push(relativeToPosix6(root, readinessSource));
34351
+ contextFiles.push(relativeToPosix8(root, readinessSource));
33740
34352
  }
33741
34353
  const evidenceContextFiles = discoverCanonicalEvidenceFiles(
33742
34354
  fs12,