@laitszkin/apollo-toolkit 3.13.2 → 3.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/AGENTS.md +7 -7
  2. package/CHANGELOG.md +36 -0
  3. package/CLAUDE.md +8 -8
  4. package/analyse-app-logs/SKILL.md +3 -3
  5. package/bin/apollo-toolkit.ts +7 -0
  6. package/codex/codex-memory-manager/SKILL.md +2 -2
  7. package/codex/learn-skill-from-conversations/SKILL.md +3 -3
  8. package/dist/bin/apollo-toolkit.d.ts +2 -0
  9. package/dist/bin/apollo-toolkit.js +7 -0
  10. package/dist/lib/cli.d.ts +41 -0
  11. package/dist/lib/cli.js +655 -0
  12. package/dist/lib/installer.d.ts +59 -0
  13. package/dist/lib/installer.js +404 -0
  14. package/dist/lib/tool-runner.d.ts +19 -0
  15. package/dist/lib/tool-runner.js +536 -0
  16. package/dist/lib/tools/architecture.d.ts +2 -0
  17. package/dist/lib/tools/architecture.js +23 -0
  18. package/dist/lib/tools/create-specs.d.ts +2 -0
  19. package/dist/lib/tools/create-specs.js +175 -0
  20. package/dist/lib/tools/docs-to-voice.d.ts +2 -0
  21. package/dist/lib/tools/docs-to-voice.js +705 -0
  22. package/dist/lib/tools/enforce-video-aspect-ratio.d.ts +2 -0
  23. package/dist/lib/tools/enforce-video-aspect-ratio.js +312 -0
  24. package/dist/lib/tools/extract-conversations.d.ts +2 -0
  25. package/dist/lib/tools/extract-conversations.js +105 -0
  26. package/dist/lib/tools/extract-pdf-text.d.ts +2 -0
  27. package/dist/lib/tools/extract-pdf-text.js +92 -0
  28. package/dist/lib/tools/filter-logs.d.ts +2 -0
  29. package/dist/lib/tools/filter-logs.js +94 -0
  30. package/dist/lib/tools/find-github-issues.d.ts +2 -0
  31. package/dist/lib/tools/find-github-issues.js +176 -0
  32. package/dist/lib/tools/generate-storyboard-images.d.ts +2 -0
  33. package/dist/lib/tools/generate-storyboard-images.js +419 -0
  34. package/dist/lib/tools/log-cli-utils.d.ts +35 -0
  35. package/dist/lib/tools/log-cli-utils.js +233 -0
  36. package/dist/lib/tools/open-github-issue.d.ts +2 -0
  37. package/dist/lib/tools/open-github-issue.js +750 -0
  38. package/dist/lib/tools/read-github-issue.d.ts +2 -0
  39. package/dist/lib/tools/read-github-issue.js +134 -0
  40. package/dist/lib/tools/render-error-book.d.ts +2 -0
  41. package/dist/lib/tools/render-error-book.js +265 -0
  42. package/dist/lib/tools/render-katex.d.ts +2 -0
  43. package/dist/lib/tools/render-katex.js +294 -0
  44. package/dist/lib/tools/review-threads.d.ts +2 -0
  45. package/dist/lib/tools/review-threads.js +491 -0
  46. package/dist/lib/tools/search-logs.d.ts +2 -0
  47. package/dist/lib/tools/search-logs.js +164 -0
  48. package/dist/lib/tools/sync-memory-index.d.ts +2 -0
  49. package/dist/lib/tools/sync-memory-index.js +113 -0
  50. package/dist/lib/tools/validate-openai-agent-config.d.ts +2 -0
  51. package/dist/lib/tools/validate-openai-agent-config.js +190 -0
  52. package/dist/lib/tools/validate-skill-frontmatter.d.ts +2 -0
  53. package/dist/lib/tools/validate-skill-frontmatter.js +118 -0
  54. package/dist/lib/types.d.ts +82 -0
  55. package/dist/lib/types.js +2 -0
  56. package/dist/lib/updater.d.ts +34 -0
  57. package/dist/lib/updater.js +112 -0
  58. package/dist/lib/utils/format.d.ts +2 -0
  59. package/dist/lib/utils/format.js +6 -0
  60. package/dist/lib/utils/terminal.d.ts +12 -0
  61. package/dist/lib/utils/terminal.js +26 -0
  62. package/docs-to-voice/SKILL.md +0 -1
  63. package/generate-spec/SKILL.md +1 -1
  64. package/katex/SKILL.md +1 -2
  65. package/lib/cli.ts +780 -0
  66. package/lib/installer.ts +466 -0
  67. package/lib/tool-runner.ts +561 -0
  68. package/lib/tools/architecture.ts +20 -0
  69. package/lib/tools/create-specs.ts +204 -0
  70. package/lib/tools/docs-to-voice.ts +799 -0
  71. package/lib/tools/enforce-video-aspect-ratio.ts +368 -0
  72. package/lib/tools/extract-conversations.ts +114 -0
  73. package/lib/tools/extract-pdf-text.ts +99 -0
  74. package/lib/tools/filter-logs.ts +118 -0
  75. package/lib/tools/find-github-issues.ts +211 -0
  76. package/lib/tools/generate-storyboard-images.ts +455 -0
  77. package/lib/tools/log-cli-utils.ts +262 -0
  78. package/lib/tools/open-github-issue.ts +930 -0
  79. package/lib/tools/read-github-issue.ts +179 -0
  80. package/lib/tools/render-error-book.ts +300 -0
  81. package/lib/tools/render-katex.ts +325 -0
  82. package/lib/tools/review-threads.ts +590 -0
  83. package/lib/tools/search-logs.ts +200 -0
  84. package/lib/tools/sync-memory-index.ts +114 -0
  85. package/lib/tools/validate-openai-agent-config.ts +213 -0
  86. package/lib/tools/validate-skill-frontmatter.ts +124 -0
  87. package/lib/types.ts +90 -0
  88. package/lib/updater.ts +165 -0
  89. package/lib/utils/format.ts +7 -0
  90. package/lib/utils/terminal.ts +22 -0
  91. package/open-github-issue/SKILL.md +2 -2
  92. package/optimise-skill/SKILL.md +1 -1
  93. package/package.json +13 -4
  94. package/resources/project-architecture/assets/architecture.css +764 -0
  95. package/resources/project-architecture/assets/viewer.client.js +144 -0
  96. package/resources/project-architecture/index.html +42 -0
  97. package/review-spec-related-changes/SKILL.md +1 -1
  98. package/solve-issues-found-during-review/SKILL.md +2 -1
  99. package/tsconfig.json +28 -0
  100. package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
  101. package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
  102. package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
  103. package/analyse-app-logs/scripts/filter_logs_by_time.py +0 -64
  104. package/analyse-app-logs/scripts/log_cli_utils.py +0 -112
  105. package/analyse-app-logs/scripts/search_logs.py +0 -137
  106. package/analyse-app-logs/tests/test_filter_logs_by_time.py +0 -95
  107. package/analyse-app-logs/tests/test_search_logs.py +0 -100
  108. package/codex/codex-memory-manager/scripts/extract_recent_conversations.py +0 -369
  109. package/codex/codex-memory-manager/scripts/sync_memory_index.py +0 -130
  110. package/codex/codex-memory-manager/tests/test_extract_recent_conversations.py +0 -177
  111. package/codex/codex-memory-manager/tests/test_memory_template.py +0 -37
  112. package/codex/codex-memory-manager/tests/test_sync_memory_index.py +0 -84
  113. package/codex/learn-skill-from-conversations/scripts/extract_recent_conversations.py +0 -369
  114. package/codex/learn-skill-from-conversations/tests/test_extract_recent_conversations.py +0 -177
  115. package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
  116. package/docs-to-voice/scripts/docs_to_voice.py +0 -1385
  117. package/docs-to-voice/scripts/docs_to_voice.sh +0 -11
  118. package/docs-to-voice/tests/test_docs_to_voice_api_max_chars.py +0 -210
  119. package/docs-to-voice/tests/test_docs_to_voice_sentence_timeline.py +0 -115
  120. package/docs-to-voice/tests/test_docs_to_voice_settings.py +0 -43
  121. package/docs-to-voice/tests/test_docs_to_voice_shell_wrapper.py +0 -51
  122. package/docs-to-voice/tests/test_docs_to_voice_speech_rate.py +0 -57
  123. package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
  124. package/generate-spec/scripts/create-specs +0 -215
  125. package/generate-spec/tests/test_create_specs.py +0 -200
  126. package/init-project-html/scripts/architecture-bootstrap-render.js +0 -16
  127. package/init-project-html/scripts/architecture.js +0 -296
  128. package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
  129. package/katex/scripts/render_katex.py +0 -247
  130. package/katex/scripts/render_katex.sh +0 -11
  131. package/katex/tests/test_render_katex.py +0 -174
  132. package/learning-error-book/scripts/render_error_book_json_to_pdf.py +0 -590
  133. package/learning-error-book/tests/test_render_error_book_json_to_pdf.py +0 -134
  134. package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
  135. package/open-github-issue/scripts/open_github_issue.py +0 -705
  136. package/open-github-issue/tests/test_open_github_issue.py +0 -381
  137. package/openai-text-to-image-storyboard/scripts/generate_storyboard_images.py +0 -763
  138. package/openai-text-to-image-storyboard/tests/test_generate_storyboard_images.py +0 -177
  139. package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
  140. package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
  141. package/read-github-issue/scripts/find_issues.py +0 -148
  142. package/read-github-issue/scripts/read_issue.py +0 -108
  143. package/read-github-issue/tests/test_find_issues.py +0 -127
  144. package/read-github-issue/tests/test_read_issue.py +0 -109
  145. package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
  146. package/resolve-review-comments/scripts/review_threads.py +0 -425
  147. package/resolve-review-comments/tests/test_review_threads.py +0 -74
  148. package/scripts/validate_openai_agent_config.py +0 -209
  149. package/scripts/validate_skill_frontmatter.py +0 -131
  150. package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
  151. package/text-to-short-video/scripts/enforce_video_aspect_ratio.py +0 -350
  152. package/text-to-short-video/tests/test_enforce_video_aspect_ratio.py +0 -194
  153. package/weekly-financial-event-report/scripts/extract_pdf_text_pdfkit.swift +0 -99
  154. package/weekly-financial-event-report/tests/test_extract_pdf_text_pdfkit.py +0 -64
@@ -1,215 +0,0 @@
1
- #!/usr/bin/env python3
2
- from __future__ import annotations
3
-
4
- import argparse
5
- import re
6
- from datetime import date
7
- from pathlib import Path
8
-
9
- TEMPLATE_FILENAMES = (
10
- "spec.md",
11
- "tasks.md",
12
- "checklist.md",
13
- "contract.md",
14
- "design.md",
15
- )
16
- COORDINATION_TEMPLATE = "coordination.md"
17
- PREPARATION_TEMPLATE = "preparation.md"
18
- PLACEHOLDERS = ("[Feature Name]", "[功能名稱]")
19
-
20
-
21
- def _slugify(text: str) -> str:
22
- slug = text.lower().strip()
23
- slug = re.sub(r"[^a-z0-9]+", "-", slug)
24
- slug = re.sub(r"-+", "-", slug).strip("-")
25
- return slug
26
-
27
-
28
- def _default_template_dir() -> Path:
29
- return Path(__file__).resolve().parent.parent / "references" / "templates"
30
-
31
-
32
- def _render(
33
- content: str,
34
- today: str,
35
- feature_name: str,
36
- change_name: str,
37
- batch_name: str | None,
38
- ) -> str:
39
- rendered = content.replace("[YYYY-MM-DD]", today)
40
- for placeholder in PLACEHOLDERS:
41
- rendered = rendered.replace(placeholder, feature_name)
42
- rendered = rendered.replace("[change_name]", change_name)
43
- rendered = rendered.replace("[batch_name]", batch_name or "None")
44
- return rendered
45
-
46
-
47
- def main() -> int:
48
- parser = argparse.ArgumentParser(
49
- description=(
50
- "Create planning docs (spec.md, tasks.md, checklist.md, contract.md, design.md) "
51
- "from templates with folder format docs/plans/{date}/{change_name} "
52
- "or docs/plans/{date}/{batch_name}/{change_name}, including coordination "
53
- "and optional preparation templates for batch specs that need shared "
54
- "pre-work before parallel implementation."
55
- ),
56
- )
57
- parser.add_argument("feature_name", help="Display name used in generated documents")
58
- parser.add_argument(
59
- "--change-name",
60
- "--slug",
61
- dest="change_name",
62
- help=(
63
- "Folder name part used after date. "
64
- "Defaults to slugified feature_name when omitted."
65
- ),
66
- )
67
- parser.add_argument(
68
- "--batch-name",
69
- help=(
70
- "Optional batch folder used for parallel spec generation. "
71
- "When provided, specs are written under docs/plans/{date}/{batch_name}/{change_name}."
72
- ),
73
- )
74
- parser.add_argument(
75
- "--with-coordination",
76
- action="store_true",
77
- help=(
78
- "When used with --batch-name, also create or update "
79
- "docs/plans/{date}/{batch_name}/coordination.md from the shared template "
80
- "for up-front ownership and collision coordination."
81
- ),
82
- )
83
- parser.add_argument(
84
- "--with-preparation",
85
- action="store_true",
86
- help=(
87
- "When used with --batch-name, also create or update "
88
- "docs/plans/{date}/{batch_name}/preparation.md from the shared template "
89
- "for prerequisite work that must be completed before parallel implementation."
90
- ),
91
- )
92
- parser.add_argument(
93
- "--output-dir",
94
- default="docs/plans",
95
- help="Output directory (default: docs/plans)",
96
- )
97
- parser.add_argument(
98
- "--template-dir",
99
- default=str(_default_template_dir()),
100
- help="Directory containing planning document templates",
101
- )
102
- parser.add_argument(
103
- "--force",
104
- action="store_true",
105
- help="Overwrite existing files if present",
106
- )
107
- args = parser.parse_args()
108
-
109
- feature_name = args.feature_name.strip()
110
- if not feature_name:
111
- raise SystemExit("feature_name cannot be empty")
112
-
113
- change_name = (
114
- args.change_name.strip() if args.change_name else _slugify(feature_name)
115
- )
116
- if not change_name:
117
- raise SystemExit(
118
- "Unable to build change_name. Provide --change-name with ASCII letters/numbers."
119
- )
120
-
121
- batch_name = args.batch_name.strip() if args.batch_name else None
122
- if args.with_coordination and not batch_name:
123
- raise SystemExit("--with-coordination requires --batch-name")
124
- if args.with_preparation and not batch_name:
125
- raise SystemExit("--with-preparation requires --batch-name")
126
-
127
- template_dir = Path(args.template_dir).expanduser().resolve()
128
- if not template_dir.exists() or not template_dir.is_dir():
129
- raise SystemExit(f"Template directory not found: {template_dir}")
130
-
131
- missing_templates = [
132
- name for name in TEMPLATE_FILENAMES if not (template_dir / name).exists()
133
- ]
134
- if args.with_coordination and not (template_dir / COORDINATION_TEMPLATE).exists():
135
- missing_templates.append(COORDINATION_TEMPLATE)
136
- if args.with_preparation and not (template_dir / PREPARATION_TEMPLATE).exists():
137
- missing_templates.append(PREPARATION_TEMPLATE)
138
- if missing_templates:
139
- missing = ", ".join(missing_templates)
140
- raise SystemExit(f"Missing template files in {template_dir}: {missing}")
141
-
142
- output_dir = Path(args.output_dir).expanduser().resolve()
143
- today = date.today().isoformat()
144
- date_root = output_dir / today
145
- batch_root = date_root / batch_name if batch_name else None
146
- output_root = (batch_root / change_name) if batch_root else (date_root / change_name)
147
-
148
- output_paths = [output_root / name for name in TEMPLATE_FILENAMES]
149
- coordination_path = (
150
- batch_root / COORDINATION_TEMPLATE if args.with_coordination and batch_root else None
151
- )
152
- preparation_path = (
153
- batch_root / PREPARATION_TEMPLATE if args.with_preparation and batch_root else None
154
- )
155
- existing_files = [path for path in output_paths if path.exists()]
156
- if existing_files and not args.force:
157
- existing = ", ".join(str(path) for path in existing_files)
158
- raise SystemExit(
159
- f"Files already exist: {existing}. Use --force to overwrite existing files."
160
- )
161
-
162
- output_root.mkdir(parents=True, exist_ok=True)
163
-
164
- for filename in TEMPLATE_FILENAMES:
165
- template_path = template_dir / filename
166
- output_path = output_root / filename
167
- content = template_path.read_text(encoding="utf-8")
168
- output_path.write_text(
169
- _render(
170
- content=content,
171
- today=today,
172
- feature_name=feature_name,
173
- change_name=change_name,
174
- batch_name=batch_name,
175
- ),
176
- encoding="utf-8",
177
- )
178
-
179
- if coordination_path and (args.force or not coordination_path.exists()):
180
- coordination_template = template_dir / COORDINATION_TEMPLATE
181
- coordination_path.write_text(
182
- _render(
183
- content=coordination_template.read_text(encoding="utf-8"),
184
- today=today,
185
- feature_name=feature_name,
186
- change_name=change_name,
187
- batch_name=batch_name,
188
- ),
189
- encoding="utf-8",
190
- )
191
-
192
- if preparation_path and (args.force or not preparation_path.exists()):
193
- preparation_template = template_dir / PREPARATION_TEMPLATE
194
- preparation_path.write_text(
195
- _render(
196
- content=preparation_template.read_text(encoding="utf-8"),
197
- today=today,
198
- feature_name=feature_name,
199
- change_name=change_name,
200
- batch_name=batch_name,
201
- ),
202
- encoding="utf-8",
203
- )
204
-
205
- for output_path in output_paths:
206
- print(output_path)
207
- if coordination_path:
208
- print(coordination_path)
209
- if preparation_path:
210
- print(preparation_path)
211
- return 0
212
-
213
-
214
- if __name__ == "__main__":
215
- raise SystemExit(main())
@@ -1,200 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- from __future__ import annotations
4
-
5
- import importlib.machinery
6
- import importlib.util
7
- import io
8
- import random
9
- import re
10
- import string
11
- import sys
12
- import tempfile
13
- import unittest
14
- from datetime import date
15
- from pathlib import Path
16
- from unittest.mock import patch
17
-
18
-
19
- SCRIPT_PATH = Path(__file__).resolve().parents[1] / "scripts" / "create-specs"
20
- LOADER = importlib.machinery.SourceFileLoader("create_specs", str(SCRIPT_PATH))
21
- SPEC = importlib.util.spec_from_loader("create_specs", LOADER)
22
- MODULE = importlib.util.module_from_spec(SPEC)
23
- SPEC.loader.exec_module(MODULE)
24
-
25
-
26
- class FixedDate(date):
27
- @classmethod
28
- def today(cls) -> "FixedDate":
29
- return cls(2026, 4, 18)
30
-
31
-
32
- class CreateSpecsTests(unittest.TestCase):
33
- def test_slugify_property_keeps_safe_slug_shape(self) -> None:
34
- alphabet = string.ascii_letters + string.digits + string.punctuation + " \t中文"
35
- generator = random.Random(20260418)
36
-
37
- for _ in range(250):
38
- raw = "".join(generator.choice(alphabet) for _ in range(generator.randint(0, 40)))
39
- slug = MODULE._slugify(raw)
40
- with self.subTest(raw=raw, slug=slug):
41
- self.assertEqual(slug, slug.lower())
42
- self.assertNotIn("--", slug)
43
- self.assertFalse(slug.startswith("-"))
44
- self.assertFalse(slug.endswith("-"))
45
- self.assertRegex(slug, r"^[a-z0-9-]*$")
46
-
47
- def test_render_replaces_all_placeholders(self) -> None:
48
- rendered = MODULE._render(
49
- content=(
50
- "[YYYY-MM-DD]\n"
51
- "[Feature Name]\n"
52
- "[功能名稱]\n"
53
- "[change_name]\n"
54
- "[batch_name]\n"
55
- ),
56
- today="2026-04-18",
57
- feature_name="Batch-safe planner",
58
- change_name="batch-safe-planner",
59
- batch_name="parallel-batch",
60
- )
61
-
62
- self.assertIn("2026-04-18", rendered)
63
- self.assertEqual(rendered.count("Batch-safe planner"), 2)
64
- self.assertIn("batch-safe-planner", rendered)
65
- self.assertIn("parallel-batch", rendered)
66
-
67
- def test_main_creates_spec_files_and_shared_batch_files(self) -> None:
68
- with tempfile.TemporaryDirectory() as temp_dir:
69
- root = Path(temp_dir)
70
- template_dir = root / "templates"
71
- output_dir = root / "docs" / "plans"
72
- template_dir.mkdir(parents=True)
73
-
74
- for name in MODULE.TEMPLATE_FILENAMES:
75
- (template_dir / name).write_text(
76
- f"{name} [YYYY-MM-DD] [Feature Name] [change_name] [batch_name]\n",
77
- encoding="utf-8",
78
- )
79
- (template_dir / MODULE.COORDINATION_TEMPLATE).write_text(
80
- "coordination [YYYY-MM-DD] [Feature Name] [change_name] [batch_name]\n",
81
- encoding="utf-8",
82
- )
83
- (template_dir / MODULE.PREPARATION_TEMPLATE).write_text(
84
- "preparation [YYYY-MM-DD] [Feature Name] [change_name] [batch_name]\n",
85
- encoding="utf-8",
86
- )
87
-
88
- argv = [
89
- "create-specs",
90
- "My Feature",
91
- "--batch-name",
92
- "parallel-batch",
93
- "--with-coordination",
94
- "--with-preparation",
95
- "--output-dir",
96
- str(output_dir),
97
- "--template-dir",
98
- str(template_dir),
99
- ]
100
-
101
- with patch.object(MODULE, "date", FixedDate), patch.object(sys, "argv", argv), patch(
102
- "sys.stdout", new_callable=io.StringIO
103
- ) as stdout:
104
- exit_code = MODULE.main()
105
-
106
- self.assertEqual(exit_code, 0)
107
- change_root = output_dir / "2026-04-18" / "parallel-batch" / "my-feature"
108
- for name in MODULE.TEMPLATE_FILENAMES:
109
- content = (change_root / name).read_text(encoding="utf-8")
110
- self.assertIn("2026-04-18", content)
111
- self.assertIn("My Feature", content)
112
- self.assertIn("my-feature", content)
113
- self.assertIn("parallel-batch", content)
114
-
115
- coordination = output_dir / "2026-04-18" / "parallel-batch" / "coordination.md"
116
- self.assertTrue(coordination.is_file())
117
- self.assertIn(str(coordination), stdout.getvalue())
118
- preparation = output_dir / "2026-04-18" / "parallel-batch" / "preparation.md"
119
- self.assertTrue(preparation.is_file())
120
- self.assertIn("preparation", preparation.read_text(encoding="utf-8"))
121
- self.assertIn(str(preparation), stdout.getvalue())
122
-
123
- def test_main_requires_batch_name_for_preparation_file(self) -> None:
124
- with tempfile.TemporaryDirectory() as temp_dir:
125
- template_dir = Path(temp_dir) / "templates"
126
- template_dir.mkdir(parents=True)
127
- for name in MODULE.TEMPLATE_FILENAMES:
128
- (template_dir / name).write_text("template\n", encoding="utf-8")
129
- (template_dir / MODULE.PREPARATION_TEMPLATE).write_text(
130
- "preparation\n",
131
- encoding="utf-8",
132
- )
133
-
134
- argv = [
135
- "create-specs",
136
- "My Feature",
137
- "--with-preparation",
138
- "--template-dir",
139
- str(template_dir),
140
- ]
141
-
142
- with patch.object(sys, "argv", argv):
143
- with self.assertRaises(SystemExit) as context:
144
- MODULE.main()
145
-
146
- self.assertIn("--with-preparation requires --batch-name", str(context.exception))
147
-
148
- def test_main_rejects_existing_files_without_force(self) -> None:
149
- with tempfile.TemporaryDirectory() as temp_dir:
150
- root = Path(temp_dir)
151
- template_dir = root / "templates"
152
- output_dir = root / "docs" / "plans"
153
- template_dir.mkdir(parents=True)
154
- for name in MODULE.TEMPLATE_FILENAMES:
155
- (template_dir / name).write_text("template\n", encoding="utf-8")
156
-
157
- existing_root = output_dir / "2026-04-18" / "existing-change"
158
- existing_root.mkdir(parents=True)
159
- (existing_root / "spec.md").write_text("existing\n", encoding="utf-8")
160
-
161
- argv = [
162
- "create-specs",
163
- "Existing Change",
164
- "--change-name",
165
- "existing-change",
166
- "--output-dir",
167
- str(output_dir),
168
- "--template-dir",
169
- str(template_dir),
170
- ]
171
-
172
- with patch.object(MODULE, "date", FixedDate), patch.object(sys, "argv", argv):
173
- with self.assertRaises(SystemExit) as context:
174
- MODULE.main()
175
-
176
- self.assertIn("Files already exist", str(context.exception))
177
-
178
- def test_main_requires_explicit_ascii_change_name_when_slug_is_empty(self) -> None:
179
- with tempfile.TemporaryDirectory() as temp_dir:
180
- template_dir = Path(temp_dir) / "templates"
181
- template_dir.mkdir(parents=True)
182
- for name in MODULE.TEMPLATE_FILENAMES:
183
- (template_dir / name).write_text("template\n", encoding="utf-8")
184
-
185
- argv = [
186
- "create-specs",
187
- "功能名稱",
188
- "--template-dir",
189
- str(template_dir),
190
- ]
191
-
192
- with patch.object(sys, "argv", argv):
193
- with self.assertRaises(SystemExit) as context:
194
- MODULE.main()
195
-
196
- self.assertIn("Unable to build change_name", str(context.exception))
197
-
198
-
199
- if __name__ == "__main__":
200
- unittest.main()
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- // One-shot helper: `node architecture-bootstrap-render.js render --project <root> ...`
5
- // Invoked synchronously from architecture.js (legacy open) when index.html is missing
6
- // so the sync `main()` can still bootstrap an empty tree without duplicating elk layout.
7
-
8
- const cli = require('../lib/atlas/cli');
9
-
10
- (async () => {
11
- const code = await cli.dispatch(process.argv.slice(2));
12
- process.exit(typeof code === 'number' ? code : 1);
13
- })().catch((err) => {
14
- process.stderr.write(`${err && err.stack ? err.stack : err}\n`);
15
- process.exit(1);
16
- });