@danmoisan/drm-copilot-mcp 0.0.5 → 1.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 (96) hide show
  1. package/out/mcp-server.js +10268 -380
  2. package/package.json +2 -2
  3. package/resources/claude-customizations/.claude/agents/orchestrator.md +6 -2
  4. package/resources/claude-customizations/.claude/agents/pr-author.md +78 -0
  5. package/resources/claude-customizations/.claude/agents/task-researcher.md +10 -6
  6. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +68 -10
  7. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +148 -11
  8. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +163 -0
  9. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +40 -14
  10. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +198 -0
  11. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +153 -10
  12. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +68 -1
  13. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +136 -0
  14. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +22 -6
  15. package/resources/claude-customizations/.claude/settings.json +22 -0
  16. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +0 -1
  17. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +76 -2
  18. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +5 -3
  19. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +69 -0
  20. package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +96 -0
  21. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +77 -0
  22. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +64 -0
  23. package/resources/claude-customizations/pack-manifests/core.json +65 -0
  24. package/resources/claude-customizations/pack-manifests/csharp-legacy.json +11 -0
  25. package/resources/claude-customizations/pack-manifests/csharp-modern.json +12 -0
  26. package/resources/claude-customizations/pack-manifests/powershell.json +14 -0
  27. package/resources/claude-customizations/pack-manifests/python.json +14 -0
  28. package/resources/claude-customizations/pack-manifests/typescript.json +9 -0
  29. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +30 -13
  30. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +113 -10
  31. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +33 -8
  32. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +12 -7
  33. package/resources/codex-and-agents-customizations/.codex/config.toml +65 -6
  34. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
  35. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +300 -0
  36. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +148 -0
  37. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +336 -0
  38. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +83 -17
  39. package/resources/config/orchestration-routing.json +5 -13
  40. package/resources/customizations/.github/agents/pr-author.agent.md +25 -0
  41. package/resources/customizations/.github/agents/task-researcher.agent.md +4 -4
  42. package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +1 -1
  43. package/resources/customizations/.github/prompts/research-issue.prompt.md +4 -3
  44. package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +0 -5
  45. package/resources/scripts/dev_tools/__init__.py +0 -0
  46. package/resources/scripts/dev_tools/_orchestrator_state_human_interaction.py +0 -127
  47. package/resources/scripts/dev_tools/_orchestrator_state_routing.py +0 -216
  48. package/resources/scripts/dev_tools/agentic_sync.py +0 -819
  49. package/resources/scripts/dev_tools/codex_native_converter/__init__.py +0 -11
  50. package/resources/scripts/dev_tools/codex_native_converter/__main__.py +0 -6
  51. package/resources/scripts/dev_tools/codex_native_converter/cli.py +0 -11
  52. package/resources/scripts/dev_tools/new_active_feature_folder.py +0 -79
  53. package/resources/scripts/dev_tools/new_active_feature_folder_docs.py +0 -268
  54. package/resources/scripts/dev_tools/new_active_feature_folder_flow.py +0 -366
  55. package/resources/scripts/dev_tools/new_active_feature_folder_io.py +0 -306
  56. package/resources/scripts/dev_tools/new_active_feature_folder_markdown.py +0 -252
  57. package/resources/scripts/dev_tools/new_active_feature_folder_models.py +0 -136
  58. package/resources/scripts/dev_tools/new_potential_bug_entry.py +0 -465
  59. package/resources/scripts/dev_tools/potential_to_issue.py +0 -421
  60. package/resources/scripts/dev_tools/potential_to_issue_content.py +0 -212
  61. package/resources/scripts/dev_tools/pr_context/__init__.py +0 -0
  62. package/resources/scripts/dev_tools/pr_context/collector.py +0 -619
  63. package/resources/scripts/dev_tools/pr_context/feature_docs.py +0 -349
  64. package/resources/scripts/dev_tools/pr_context/git.py +0 -153
  65. package/resources/scripts/dev_tools/pr_context/github.py +0 -549
  66. package/resources/scripts/dev_tools/pr_context/models.py +0 -198
  67. package/resources/scripts/dev_tools/pr_context/render.py +0 -342
  68. package/resources/scripts/dev_tools/pr_context/render_feature_excerpts.py +0 -256
  69. package/resources/scripts/dev_tools/pr_context/render_pr_helpers.py +0 -291
  70. package/resources/scripts/dev_tools/pr_context/summary_helpers.py +0 -386
  71. package/resources/scripts/dev_tools/pr_context/verification_evidence.py +0 -171
  72. package/resources/scripts/dev_tools/prompt_mode_contract.py +0 -152
  73. package/resources/scripts/dev_tools/push_down_claude_customizations.py +0 -374
  74. package/resources/scripts/dev_tools/push_down_codex_and_agents_customizations.py +0 -139
  75. package/resources/scripts/dev_tools/push_down_copilot_customizations.py +0 -504
  76. package/resources/scripts/dev_tools/push_down_copilot_customizations_filesystem.py +0 -217
  77. package/resources/scripts/dev_tools/push_down_copilot_customizations_rewrites.py +0 -293
  78. package/resources/scripts/dev_tools/resolve_file_prompt.py +0 -457
  79. package/resources/scripts/dev_tools/resolve_hard_lock_prompt.py +0 -444
  80. package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +0 -246
  81. package/resources/scripts/dev_tools/validate_orchestration_review_artifacts.py +0 -107
  82. package/resources/scripts/dev_tools/validate_orchestrator_state.py +0 -428
  83. package/resources/scripts/dev_tools/validate_policy_audit_artifact.py +0 -448
  84. package/resources/templates/codex_native_converter.py +0 -35
  85. package/resources/templates/collect_commit_context.py +0 -212
  86. package/resources/templates/collect_pr_context.py +0 -74
  87. package/resources/templates/hello_python.py +0 -11
  88. package/resources/templates/new_active_feature_folder.py +0 -67
  89. package/resources/templates/new_potential_bug_entry.py +0 -54
  90. package/resources/templates/potential_to_issue.py +0 -55
  91. package/resources/templates/push_down_claude_customizations.py +0 -409
  92. package/resources/templates/push_down_codex_and_agents_customizations.py +0 -95
  93. package/resources/templates/push_down_copilot_customizations.py +0 -124
  94. package/resources/templates/resolve_atomic_plan_prompt.py +0 -75
  95. package/resources/templates/resolve_hard_lock_prompt.py +0 -65
  96. package/resources/templates/validate_orchestration_artifacts.py +0 -55
@@ -1,374 +0,0 @@
1
- """Publish bundled `.claude` content into a destination workspace.
2
-
3
- Purpose:
4
- Provide a dedicated public entry point for the Claude customization push-down
5
- workflow while reusing the shared publisher engine behind the existing
6
- `.github` customization flow. Settings-local configuration is excluded from
7
- push-down because it holds host-specific overrides that must not propagate.
8
-
9
- Agent-memory files under `.claude/agent-memory/` are filtered by a
10
- content-based scope check: only memories whose frontmatter declares
11
- `metadata.scope: general` are distributed to a destination workspace. A
12
- memory with an absent, malformed, or unrecognized scope is treated as
13
- `repo` and excluded (fail-safe default), so repository-specific memories do
14
- not leak into consumer workspaces. Files outside `.claude/agent-memory/`
15
- are copied verbatim and are never affected by the scope filter.
16
- """
17
-
18
- from __future__ import annotations
19
-
20
- import argparse
21
- import re
22
- from pathlib import Path
23
-
24
- AGENT_MEMORY_RELATIVE_ROOT = Path(".claude/agent-memory")
25
- GENERAL_MEMORY_SCOPE = "general"
26
- REPO_MEMORY_SCOPE = "repo"
27
-
28
- # Match a leading YAML frontmatter block: the first `---` line, the block body,
29
- # and the closing `---` line. DOTALL lets the body span multiple lines.
30
- _FRONTMATTER_PATTERN = re.compile(
31
- r"\A---[ \t]*\r?\n(.*?)\r?\n---[ \t]*(?:\r?\n|\Z)", re.DOTALL
32
- )
33
- # Match a `metadata:` mapping key at column zero, then capture the indented
34
- # block lines that belong to it (lines that are more-indented or blank) until
35
- # the next column-zero key or end of the frontmatter body.
36
- _METADATA_BLOCK_PATTERN = re.compile(
37
- r"^metadata:[ \t]*\r?\n((?:[ \t]+.*(?:\r?\n|\Z)|\r?\n)*)",
38
- re.MULTILINE,
39
- )
40
- # Match a `scope:` leaf inside the metadata block, capturing its scalar value
41
- # up to an optional inline comment. Surrounding quotes are stripped later.
42
- _SCOPE_LEAF_PATTERN = re.compile(
43
- r"^[ \t]+scope:[ \t]*([^\r\n#]*)",
44
- re.MULTILINE,
45
- )
46
-
47
- try:
48
- from scripts.dev_tools.push_down_copilot_customizations import (
49
- PushDownFileSystem,
50
- PushDownSummary,
51
- RealPushDownFileSystem,
52
- resolve_cli_path,
53
- )
54
- from scripts.dev_tools.push_down_copilot_customizations import (
55
- push_down_customizations as push_down_scoped_customizations,
56
- )
57
- except ModuleNotFoundError as error:
58
- if error.name is None or not error.name.startswith("scripts"):
59
- raise
60
- from dev_tools.push_down_copilot_customizations import (
61
- PushDownFileSystem,
62
- PushDownSummary,
63
- RealPushDownFileSystem,
64
- resolve_cli_path,
65
- )
66
- from dev_tools.push_down_copilot_customizations import (
67
- push_down_customizations as push_down_scoped_customizations,
68
- )
69
-
70
- ARTIFACT_DIRECTORY = "artifacts/claude-customizations"
71
- MODULE_ENTRY_POINT = "scripts.dev_tools.push_down_claude_customizations"
72
- ROOT_FOLDERS: tuple[Path, ...] = (Path(".claude"),)
73
- EXCLUDED_RELATIVE_PATHS: tuple[Path, ...] = (Path(".claude/settings.local.json"),)
74
-
75
- __all__ = [
76
- "AGENT_MEMORY_RELATIVE_ROOT",
77
- "ARTIFACT_DIRECTORY",
78
- "EXCLUDED_RELATIVE_PATHS",
79
- "GENERAL_MEMORY_SCOPE",
80
- "PushDownSummary",
81
- "REPO_MEMORY_SCOPE",
82
- "ROOT_FOLDERS",
83
- "main",
84
- "parse_args",
85
- "push_down_customizations",
86
- ]
87
-
88
-
89
- def _read_memory_scope(content: str) -> str:
90
- """Return the declared memory scope from a file's YAML frontmatter.
91
-
92
- Purpose:
93
- Extract the `metadata.scope` leaf from the leading YAML frontmatter
94
- block using a narrow `re`-based parser. This avoids adding a runtime
95
- YAML dependency (PyYAML) while reading only the single leaf the
96
- push-down scope filter requires.
97
-
98
- Args:
99
- content (str): The full text of a candidate memory file, including any
100
- leading `---` frontmatter block.
101
-
102
- Returns:
103
- str: ``"general"`` only when the frontmatter contains a `metadata:`
104
- mapping whose `scope:` leaf is exactly ``general`` (quotes and inline
105
- comments stripped). In every other case — missing frontmatter, no
106
- closing `---`, no `metadata:` block, no `scope:` leaf, or any value
107
- other than exactly ``general`` — it returns ``"repo"`` as the fail-safe
108
- default so nothing leaks by accident.
109
-
110
- Raises:
111
- None.
112
-
113
- Side Effects:
114
- None.
115
- """
116
-
117
- # Isolate the leading frontmatter block; absent or unterminated frontmatter
118
- # fails safe to the repo scope so unmarked files are never distributed.
119
- frontmatter_match = _FRONTMATTER_PATTERN.match(content)
120
- if frontmatter_match is None:
121
- return REPO_MEMORY_SCOPE
122
- frontmatter_body = frontmatter_match.group(1)
123
-
124
- # Locate the metadata mapping; without it there is no scope leaf to read.
125
- metadata_match = _METADATA_BLOCK_PATTERN.search(frontmatter_body)
126
- if metadata_match is None:
127
- return REPO_MEMORY_SCOPE
128
- metadata_block = metadata_match.group(1)
129
-
130
- # Read the scope leaf from within the metadata block only; a top-level
131
- # `scope:` outside `metadata:` is intentionally ignored.
132
- scope_match = _SCOPE_LEAF_PATTERN.search(metadata_block)
133
- if scope_match is None:
134
- return REPO_MEMORY_SCOPE
135
-
136
- # Strip surrounding whitespace and optional matching quotes before the
137
- # exact-match comparison; only an exact `general` is treated as general.
138
- scope_value = scope_match.group(1).strip()
139
- if (
140
- len(scope_value) >= 2
141
- and scope_value[0] == scope_value[-1]
142
- and scope_value[0] in {'"', "'"}
143
- ):
144
- scope_value = scope_value[1:-1].strip()
145
- if scope_value == GENERAL_MEMORY_SCOPE:
146
- return GENERAL_MEMORY_SCOPE
147
- return REPO_MEMORY_SCOPE
148
-
149
-
150
- def _is_general_memory_file(relative_path: Path, content: str) -> bool:
151
- """Return whether a candidate file may be distributed by push-down.
152
-
153
- Purpose:
154
- Decide inclusion for one source file. Files under
155
- `.claude/agent-memory/` are distributed only when general-scoped;
156
- every other file is always distributed and is unaffected by the scope
157
- filter.
158
-
159
- Args:
160
- relative_path (Path): The file path relative to the repository root.
161
- content (str): The full text of the file, used to read the memory
162
- scope when the path is under the agent-memory subtree.
163
-
164
- Returns:
165
- bool: ``True`` when the path is outside `.claude/agent-memory/`, or when
166
- the path is under that subtree and `_read_memory_scope(content)` is
167
- exactly ``general``. ``False`` only for an agent-memory file whose
168
- scope is not general (the fail-safe exclusion).
169
-
170
- Raises:
171
- None.
172
-
173
- Side Effects:
174
- None.
175
- """
176
-
177
- # Files outside the agent-memory subtree are always copied; the scope
178
- # filter never applies to rules, skills, agents, hooks, or settings.
179
- try:
180
- relative_path.relative_to(AGENT_MEMORY_RELATIVE_ROOT)
181
- except ValueError:
182
- return True
183
- return _read_memory_scope(content) == GENERAL_MEMORY_SCOPE
184
-
185
-
186
- class _ExcludingFileSystem:
187
- """Wrap a PushDownFileSystem and filter specified paths from list_files.
188
-
189
- Purpose:
190
- Prevent host-specific files (e.g. `settings.local.json`) from being
191
- included when the publisher enumerates source files, and exclude
192
- repository-specific agent memories so only general-scoped memories are
193
- distributed. Chosen over a post-enumeration filter so the exclusion
194
- travels with the filesystem contract and is transparent to the shared
195
- engine.
196
-
197
- Usage:
198
- Instantiate with the inner adapter and exclusion paths relative to
199
- the repo root; pass the result as the `fs` argument to the engine.
200
-
201
- Invariants / Constraints:
202
- Excluded paths are resolved once at construction time for O(1) checks.
203
- The content-based scope filter reads file content only for candidates
204
- under `.claude/agent-memory/`; all other files skip the read.
205
-
206
- Side Effects:
207
- Delegates all I/O to the inner adapter.
208
- """
209
-
210
- def __init__(
211
- self, inner: PushDownFileSystem, repo_root: Path, excluded: tuple[Path, ...]
212
- ) -> None:
213
- """Set up the adapter; resolve exclusion paths relative to repo_root."""
214
- self._inner = inner
215
- # Retain the resolved repo root so per-file scope checks can derive the
216
- # repo-relative path needed by the agent-memory scope filter.
217
- self._repo_root = repo_root.resolve()
218
- # Resolve once at init so list_files per-path checks are O(1).
219
- self._excluded: frozenset[Path] = frozenset(
220
- (repo_root / p).resolve() for p in excluded
221
- )
222
-
223
- def _is_scope_included(self, path: Path) -> bool:
224
- """Return whether a candidate file passes the agent-memory scope filter.
225
-
226
- Purpose:
227
- Apply the general-vs-repo memory scope decision to one enumerated
228
- file, reading its content only when the path is under the
229
- agent-memory subtree.
230
-
231
- Args:
232
- path (Path): The absolute candidate path returned by the inner
233
- adapter's ``list_files``.
234
-
235
- Returns:
236
- bool: ``True`` when the file is outside `.claude/agent-memory/` or
237
- is a general-scoped memory; ``False`` for a non-general memory.
238
-
239
- Raises:
240
- None.
241
-
242
- Side Effects:
243
- Reads file content via the inner adapter for agent-memory
244
- candidates only.
245
- """
246
-
247
- # Derive the repo-relative path so the agent-memory check matches the
248
- # `.claude/agent-memory/` prefix regardless of the absolute location.
249
- try:
250
- relative_path = path.resolve().relative_to(self._repo_root)
251
- except ValueError:
252
- # A path outside the repo root cannot be an agent memory; include it.
253
- return True
254
-
255
- # Skip the content read entirely for files outside the memory subtree.
256
- try:
257
- relative_path.relative_to(AGENT_MEMORY_RELATIVE_ROOT)
258
- except ValueError:
259
- return True
260
-
261
- content = self._inner.read_text(path)
262
- return _is_general_memory_file(relative_path, content)
263
-
264
- def list_files(self, root: Path) -> list[Path]:
265
- """Return inner list_files output with excluded paths removed.
266
-
267
- Drops paths in ``EXCLUDED_RELATIVE_PATHS`` and any agent-memory file
268
- that is not general-scoped per the content-based scope filter.
269
- """
270
- return [
271
- p
272
- for p in self._inner.list_files(root)
273
- if p.resolve() not in self._excluded and self._is_scope_included(p)
274
- ]
275
-
276
- def is_dir(self, path: Path) -> bool:
277
- """Delegate to inner adapter."""
278
- return self._inner.is_dir(path)
279
-
280
- def is_file(self, path: Path) -> bool:
281
- """Delegate to inner adapter."""
282
- return self._inner.is_file(path)
283
-
284
- def read_text(self, path: Path) -> str:
285
- """Delegate to inner adapter."""
286
- return self._inner.read_text(path)
287
-
288
- def write_text(self, path: Path, content: str) -> None:
289
- """Delegate to inner adapter."""
290
- self._inner.write_text(path, content)
291
-
292
- def ensure_dir(self, path: Path) -> None:
293
- """Delegate to inner adapter."""
294
- self._inner.ensure_dir(path)
295
-
296
-
297
- def _passthrough_rewrite(
298
- text: str,
299
- ) -> tuple[str, int, int, list[str]]:
300
- """Return unmodified text for payloads that do not need command rewrites."""
301
-
302
- return text, 0, 0, []
303
-
304
-
305
- def push_down_customizations(
306
- *,
307
- repo_root: Path,
308
- destination_root: Path,
309
- fs: PushDownFileSystem,
310
- source_root: Path | None = None,
311
- artifact_root: Path | None = None,
312
- ) -> PushDownSummary:
313
- """Copy the `.claude` tree into the destination workspace.
314
-
315
- Excludes paths in `EXCLUDED_RELATIVE_PATHS` by wrapping `fs` in
316
- `_ExcludingFileSystem` before delegating to the shared engine.
317
- """
318
-
319
- # Wrap the caller-supplied adapter so enumeration omits excluded paths.
320
- excluding_fs = _ExcludingFileSystem(fs, repo_root, EXCLUDED_RELATIVE_PATHS)
321
- return push_down_scoped_customizations(
322
- repo_root=repo_root,
323
- destination_root=destination_root,
324
- fs=excluding_fs,
325
- source_root=source_root,
326
- artifact_root=artifact_root,
327
- root_folders=ROOT_FOLDERS,
328
- artifact_directory=ARTIFACT_DIRECTORY,
329
- rewrite_references=_passthrough_rewrite,
330
- )
331
-
332
-
333
- def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
334
- """Parse CLI arguments for the Claude customization push-down publisher."""
335
-
336
- parser = argparse.ArgumentParser(
337
- description=(
338
- "Publish bundled Claude customizations with "
339
- f"python -m {MODULE_ENTRY_POINT}."
340
- )
341
- )
342
- parser.add_argument(
343
- "--destination",
344
- required=True,
345
- help=("Destination workspace root that will receive the copied .claude tree."),
346
- )
347
- return parser.parse_args(argv)
348
-
349
-
350
- def main(
351
- argv: list[str] | None = None,
352
- *,
353
- repo_root: Path | None = None,
354
- fs: PushDownFileSystem | None = None,
355
- ) -> int:
356
- """Run the Claude customization push-down publisher CLI."""
357
-
358
- args = parse_args(argv)
359
- resolved_repo_root = resolve_cli_path(repo_root or Path.cwd())
360
- resolved_destination = resolve_cli_path(args.destination)
361
- resolved_fs = fs or RealPushDownFileSystem()
362
- summary = push_down_customizations(
363
- repo_root=resolved_repo_root,
364
- destination_root=resolved_destination,
365
- fs=resolved_fs,
366
- source_root=resolved_repo_root,
367
- artifact_root=resolved_repo_root,
368
- )
369
- print(f"Wrote push-down summary artifact to: {summary.artifact_path}")
370
- return 0
371
-
372
-
373
- if __name__ == "__main__":
374
- raise SystemExit(main())
@@ -1,139 +0,0 @@
1
- """Publish bundled `.codex` and `.agents` content into a destination workspace.
2
-
3
- Purpose:
4
- Provide a dedicated public entry point for the Codex/agents push-down
5
- workflow while reusing the shared publisher engine behind the existing
6
- `.github` customization flow.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import argparse
12
- from pathlib import Path
13
- from typing import Protocol
14
-
15
- try:
16
- from scripts.dev_tools.push_down_copilot_customizations import (
17
- PushDownFileSystem,
18
- RealPushDownFileSystem,
19
- resolve_cli_path,
20
- )
21
- from scripts.dev_tools.push_down_copilot_customizations import (
22
- push_down_customizations as push_down_scoped_customizations,
23
- )
24
- except ModuleNotFoundError as error:
25
- if error.name is None or not error.name.startswith("scripts"):
26
- raise
27
- from dev_tools.push_down_copilot_customizations import (
28
- PushDownFileSystem,
29
- RealPushDownFileSystem,
30
- resolve_cli_path,
31
- )
32
- from dev_tools.push_down_copilot_customizations import (
33
- push_down_customizations as push_down_scoped_customizations,
34
- )
35
-
36
- ARTIFACT_DIRECTORY = "artifacts/codex-and-agents-customizations"
37
- MODULE_ENTRY_POINT = "scripts.dev_tools.push_down_codex_and_agents_customizations"
38
- ROOT_FOLDERS: tuple[Path, ...] = (Path(".codex"), Path(".agents"))
39
-
40
- __all__ = ["PushDownSummary", "main", "parse_args", "push_down_customizations"]
41
-
42
-
43
- class PushDownSummary(Protocol):
44
- """
45
- Describe the summary surface returned by the bundled push-down publisher.
46
-
47
- Purpose:
48
- Provide a stable typed contract for callers without tying Pyright to a
49
- specific import path for the shared publisher implementation.
50
-
51
- Usage:
52
- Returned by `push_down_customizations()` and consumed by `main()` when
53
- reporting the written artifact path.
54
-
55
- Attributes:
56
- artifact_path (str): JSON artifact path written for the run.
57
- """
58
-
59
- @property
60
- def artifact_path(self) -> str:
61
- """Return the JSON artifact path written for the push-down run."""
62
- ...
63
-
64
-
65
- def _passthrough_rewrite(
66
- text: str,
67
- ) -> tuple[str, int, int, list[str]]:
68
- """Return unmodified text for payloads that do not need command rewrites."""
69
-
70
- return text, 0, 0, []
71
-
72
-
73
- def push_down_customizations(
74
- *,
75
- repo_root: Path,
76
- destination_root: Path,
77
- fs: PushDownFileSystem,
78
- source_root: Path | None = None,
79
- artifact_root: Path | None = None,
80
- ) -> PushDownSummary:
81
- """Copy bundled `.codex` and `.agents` trees into the destination workspace."""
82
-
83
- return push_down_scoped_customizations(
84
- repo_root=repo_root,
85
- destination_root=destination_root,
86
- fs=fs,
87
- source_root=source_root,
88
- artifact_root=artifact_root,
89
- root_folders=ROOT_FOLDERS,
90
- artifact_directory=ARTIFACT_DIRECTORY,
91
- rewrite_references=_passthrough_rewrite,
92
- )
93
-
94
-
95
- def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
96
- """Parse CLI arguments for the Codex/agents push-down publisher."""
97
-
98
- parser = argparse.ArgumentParser(
99
- description=(
100
- "Publish bundled Codex and agents customizations with "
101
- f"python -m {MODULE_ENTRY_POINT}."
102
- )
103
- )
104
- parser.add_argument(
105
- "--destination",
106
- required=True,
107
- help=(
108
- "Destination workspace root that will receive the copied .codex "
109
- "and .agents trees."
110
- ),
111
- )
112
- return parser.parse_args(argv)
113
-
114
-
115
- def main(
116
- argv: list[str] | None = None,
117
- *,
118
- repo_root: Path | None = None,
119
- fs: PushDownFileSystem | None = None,
120
- ) -> int:
121
- """Run the Codex/agents push-down publisher CLI."""
122
-
123
- args = parse_args(argv)
124
- resolved_repo_root = resolve_cli_path(repo_root or Path.cwd())
125
- resolved_destination = resolve_cli_path(args.destination)
126
- resolved_fs = fs or RealPushDownFileSystem()
127
- summary = push_down_customizations(
128
- repo_root=resolved_repo_root,
129
- destination_root=resolved_destination,
130
- fs=resolved_fs,
131
- source_root=resolved_repo_root,
132
- artifact_root=resolved_repo_root,
133
- )
134
- print(f"Wrote push-down summary artifact to: {summary.artifact_path}")
135
- return 0
136
-
137
-
138
- if __name__ == "__main__":
139
- raise SystemExit(main())