Files
2026-10-18 16:21:54 +08:00

62 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Create a uniquely numbered, generic Code4History research card. Stdlib only."""
from __future__ import annotations
import argparse
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
KINDS = {
'primary': ('SRC-P', '01_sources/records/primary', 'primary.md'),
'secondary': ('SRC-S', '01_sources/records/secondary', 'secondary.md'),
'evidence': ('EV', '02_evidence', 'evidence.md'),
'lead': ('L', '03_inquiry/leads', 'lead.md'),
'question': ('Q', '03_inquiry/questions', 'question.md'),
'hypothesis': ('H', '03_inquiry/hypotheses', 'hypothesis.md'),
'claim': ('C', '06_arguments/claims', 'claim.md'),
'synthesis': ('SYN', '06_arguments/syntheses', 'synthesis.md'),
'conclusion': ('CON', '06_arguments/conclusions', 'conclusion.md'),
'decision': ('D', '00_governance', 'decision.md'),
'work': ('W', '05_historiography/works', 'work.md'),
}
def create(root: Path, kind: str, slug: str, dry_run: bool = False) -> tuple[str, Path]:
if kind not in KINDS: raise ValueError(f'Unknown kind: {kind}')
if not re.fullmatch(r'[a-z0-9]+(?:-[a-z0-9]+)*', slug):
raise ValueError('Slug must be lowercase ASCII kebab-case (e.g. archive-letter).')
prefix, folder, template = KINDS[kind]
folder_path = root / folder
folder_path.mkdir(parents=True, exist_ok=True)
# Identify all allocated IDs, including cards no longer in their expected folder.
occupied = set()
id_pattern = re.compile(rf'^{re.escape(prefix)}-(\d{{4}})(?:-|\.md)')
for p in root.rglob('*.md'):
if '10_templates' in p.parts or '.agents' in p.parts:
continue
m = id_pattern.match(p.name)
if m: occupied.add(int(m.group(1)))
n = max(occupied, default=0) + 1
ident = f'{prefix}-{n:04d}'
output = folder_path / f'{ident}-{slug}.md'
if output.exists(): raise FileExistsError(output)
if not dry_run:
template_path = root / '10_templates' / template
if not template_path.is_file(): raise FileNotFoundError(template_path)
text = template_path.read_text(encoding='utf-8').replace('__ID__', ident)
output.write_text(text, encoding='utf-8')
return ident, output
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--kind', required=True, choices=sorted(KINDS))
parser.add_argument('--slug', required=True, help='ASCII kebab-case; not a historical claim')
parser.add_argument('--dry-run', action='store_true')
args = parser.parse_args()
try:
ident, path = create(ROOT, args.kind, args.slug, args.dry_run)
except (ValueError, FileNotFoundError, FileExistsError) as exc:
parser.error(str(exc))
print(f'{"WOULD CREATE" if args.dry_run else "CREATED"}: {ident} → {path.relative_to(ROOT)}')
if __name__ == '__main__': main()