Skip to content

Commit bdfab45

Browse files
refactor: delegate to super().setup() and post-process for hints
- Eliminates setup() duplication by calling super().setup() then post-processing command files to inject argument-hint - Fixes EOL preservation to correctly detect \r\n vs \n - No drift risk if MarkdownIntegration.setup() changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6e5ba27 commit bdfab45

File tree

1 file changed

+37
-43
lines changed

1 file changed

+37
-43
lines changed

src/specify_cli/integrations/claude/__init__.py

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,13 @@ def inject_argument_hint(content: str, hint: str) -> str:
7676
continue
7777
if in_fm and not injected and stripped.startswith("description:"):
7878
out.append(line)
79-
# Preserve the line-ending style of the file
80-
eol = "\n" if line.endswith("\n") else ""
79+
# Preserve the exact line-ending style (\r\n vs \n)
80+
if line.endswith("\r\n"):
81+
eol = "\r\n"
82+
elif line.endswith("\n"):
83+
eol = "\n"
84+
else:
85+
eol = ""
8186
out.append(f"argument-hint: {hint}{eol}")
8287
injected = True
8388
continue
@@ -91,45 +96,34 @@ def setup(
9196
parsed_options: dict[str, Any] | None = None,
9297
**opts: Any,
9398
) -> list[Path]:
94-
templates = self.list_command_templates()
95-
if not templates:
96-
return []
97-
98-
project_root_resolved = project_root.resolve()
99-
if manifest.project_root != project_root_resolved:
100-
raise ValueError(
101-
f"manifest.project_root ({manifest.project_root}) does not match "
102-
f"project_root ({project_root_resolved})"
103-
)
104-
105-
dest = self.commands_dest(project_root).resolve()
106-
try:
107-
dest.relative_to(project_root_resolved)
108-
except ValueError as exc:
109-
raise ValueError(
110-
f"Integration destination {dest} escapes "
111-
f"project root {project_root_resolved}"
112-
) from exc
113-
dest.mkdir(parents=True, exist_ok=True)
114-
115-
script_type = opts.get("script_type", "sh")
116-
arg_placeholder = self.registrar_config.get("args", "$ARGUMENTS") if self.registrar_config else "$ARGUMENTS"
117-
created: list[Path] = []
118-
119-
for src_file in templates:
120-
raw = src_file.read_text(encoding="utf-8")
121-
processed = self.process_template(raw, self.key, script_type, arg_placeholder)
122-
123-
# Inject argument-hint for Claude Code commands
124-
hint = ARGUMENT_HINTS.get(src_file.stem, "")
125-
if hint:
126-
processed = self.inject_argument_hint(processed, hint)
127-
128-
dst_name = self.command_filename(src_file.stem)
129-
dst_file = self.write_file_and_record(
130-
processed, dest / dst_name, project_root, manifest
131-
)
132-
created.append(dst_file)
133-
134-
created.extend(self.install_scripts(project_root, manifest))
99+
"""Run standard MarkdownIntegration setup, then inject argument-hint."""
100+
created = super().setup(project_root, manifest, parsed_options, **opts)
101+
102+
# Post-process generated command files to add argument-hint
103+
commands_dest = self.commands_dest(project_root).resolve()
104+
ext = self.registrar_config.get("extension", ".md") if self.registrar_config else ".md"
105+
106+
for path in created:
107+
# Only touch command files, not scripts
108+
try:
109+
path.resolve().relative_to(commands_dest)
110+
except ValueError:
111+
continue
112+
if path.suffix != ext:
113+
continue
114+
115+
# Extract template stem: speckit.plan.md -> plan
116+
stem = path.stem # speckit.plan
117+
if stem.startswith("speckit."):
118+
stem = stem[len("speckit."):]
119+
120+
hint = ARGUMENT_HINTS.get(stem, "")
121+
if not hint:
122+
continue
123+
124+
content = path.read_text(encoding="utf-8")
125+
updated = self.inject_argument_hint(content, hint)
126+
if updated != content:
127+
path.write_text(updated, encoding="utf-8")
128+
135129
return created

0 commit comments

Comments
 (0)