|
34 | 34 | js_render_result_name, |
35 | 35 | js_render_use_name, |
36 | 36 | ) |
| 37 | +from utils.tool_types_manifest_io import ( # noqa: E402 |
| 38 | + parse_file_activity_handler_names, |
| 39 | + serialize_tool_types_manifest, |
| 40 | +) |
37 | 41 |
|
38 | 42 | _FILE_ACTIVITY_HANDLER = { |
39 | 43 | "none": "None", |
@@ -125,7 +129,7 @@ def emit(self, record: ToolTypeRecord) -> list[Path]: |
125 | 129 |
|
126 | 130 | def _guard_not_present(self, record: ToolTypeRecord) -> None: |
127 | 131 | dispatch_text = self.paths.tool_dispatch.read_text(encoding="utf-8") |
128 | | - known = _parse_handlers_from_text(dispatch_text) |
| 132 | + known = parse_file_activity_handler_names(dispatch_text) |
129 | 133 | if record.name in known: |
130 | 134 | msg = f"tool type {record.name!r} already present in _FILE_ACTIVITY_HANDLERS" |
131 | 135 | raise ValueError(msg) |
@@ -439,9 +443,8 @@ def _plan_manifest(self, plan: _EmitPlan) -> None: |
439 | 443 | if self.paths.tool_dispatch not in plan.pending: |
440 | 444 | msg = "internal error: tool_dispatch was not staged before manifest" |
441 | 445 | raise ValueError(msg) |
442 | | - known = _parse_handlers_from_text(plan.pending[self.paths.tool_dispatch]) |
443 | | - payload = {"tool_types": sorted(known)} |
444 | | - plan.stage(self.paths.tool_types_manifest, json.dumps(payload, indent=2) + "\n") |
| 446 | + known = parse_file_activity_handler_names(plan.pending[self.paths.tool_dispatch]) |
| 447 | + plan.stage(self.paths.tool_types_manifest, serialize_tool_types_manifest(known)) |
445 | 448 |
|
446 | 449 | def _plan_record(self, record: ToolTypeRecord, plan: _EmitPlan) -> None: |
447 | 450 | path = self.paths.records_dir / f"{record.snake_name}.json" |
@@ -485,36 +488,6 @@ def _append_to_block(text: str, block_start: str, entry: str) -> str: |
485 | 488 | return text[:close_pos] + "\n" + entry + text[close_pos:] |
486 | 489 |
|
487 | 490 |
|
488 | | -def _parse_handlers_from_text(text: str) -> frozenset[str]: |
489 | | - marker = "_FILE_ACTIVITY_HANDLERS: dict" |
490 | | - start = text.find(marker) |
491 | | - if start == -1: |
492 | | - msg = f"could not find {marker} in staged tool_dispatch.py" |
493 | | - raise ValueError(msg) |
494 | | - brace_start = text.find("{", start) |
495 | | - if brace_start == -1: |
496 | | - msg = "could not find opening brace for _FILE_ACTIVITY_HANDLERS" |
497 | | - raise ValueError(msg) |
498 | | - depth = 0 |
499 | | - i = brace_start |
500 | | - while i < len(text): |
501 | | - ch = text[i] |
502 | | - if ch == "{": |
503 | | - depth += 1 |
504 | | - elif ch == "}": |
505 | | - depth -= 1 |
506 | | - if depth == 0: |
507 | | - body = text[brace_start + 1 : i] |
508 | | - keys = re.findall(r'"([^"]+)":', body) |
509 | | - if not keys: |
510 | | - msg = "no tool names found in staged _FILE_ACTIVITY_HANDLERS" |
511 | | - raise ValueError(msg) |
512 | | - return frozenset(keys) |
513 | | - i += 1 |
514 | | - msg = "unbalanced braces in staged _FILE_ACTIVITY_HANDLERS" |
515 | | - raise ValueError(msg) |
516 | | - |
517 | | - |
518 | 491 | def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: |
519 | 492 | parser = argparse.ArgumentParser(description=__doc__) |
520 | 493 | parser.add_argument( |
@@ -571,39 +544,42 @@ def _resolve_record(args: argparse.Namespace) -> ToolTypeRecord: |
571 | 544 |
|
572 | 545 |
|
573 | 546 | def main(argv: list[str] | None = None) -> int: |
574 | | - args = _parse_args(argv) |
575 | | - record = _resolve_record(args) |
576 | | - root = args.root.resolve() |
577 | | - |
578 | | - if args.write_record_only: |
579 | | - out = args.record or root / "tool_types" / f"{record.snake_name}.json" |
580 | | - if args.dry_run: |
581 | | - print(f"Would write record to {out}") |
| 547 | + try: |
| 548 | + args = _parse_args(argv) |
| 549 | + record = _resolve_record(args) |
| 550 | + root = args.root.resolve() |
| 551 | + |
| 552 | + if args.write_record_only: |
| 553 | + out = args.record or root / "tool_types" / f"{record.snake_name}.json" |
| 554 | + if args.dry_run: |
| 555 | + print(f"Would write record to {out}") |
| 556 | + return 0 |
| 557 | + record.save(out) |
| 558 | + print(f"Wrote {out}") |
582 | 559 | return 0 |
583 | | - record.save(out) |
584 | | - print(f"Wrote {out}") |
585 | | - return 0 |
586 | 560 |
|
587 | | - emitter = ScaffoldEmitter(root, dry_run=args.dry_run) |
588 | | - try: |
| 561 | + emitter = ScaffoldEmitter(root, dry_run=args.dry_run) |
589 | 562 | written = emitter.emit(record) |
| 563 | + |
| 564 | + if args.dry_run: |
| 565 | + print(f"Dry run complete for {record.name} ({len(written)} artifacts planned)") |
| 566 | + else: |
| 567 | + print(f"Scaffolded {record.name}: {len(written)} files updated") |
| 568 | + print("Next: complete TODO stubs, then run:") |
| 569 | + print( |
| 570 | + " pytest tests/test_scaffold_tool_type.py tests/test_tool_dispatch_sync.py " |
| 571 | + "tests/test_tool_dispatch_ordering.py tests/test_tool_dispatch_adversarial.py " |
| 572 | + "tests/test_jsonl_parser.py tests/test_real_session_fixtures.py -q" |
| 573 | + ) |
| 574 | + print(" npm test") |
| 575 | + return 0 |
| 576 | + except json.JSONDecodeError as exc: |
| 577 | + print(f"scaffold_tool_type: invalid JSON: {exc}", file=sys.stderr) |
| 578 | + return 1 |
590 | 579 | except ValueError as exc: |
591 | 580 | print(f"scaffold_tool_type: {exc}", file=sys.stderr) |
592 | 581 | return 1 |
593 | 582 |
|
594 | | - if args.dry_run: |
595 | | - print(f"Dry run complete for {record.name} ({len(written)} artifacts planned)") |
596 | | - else: |
597 | | - print(f"Scaffolded {record.name}: {len(written)} files updated") |
598 | | - print("Next: complete TODO stubs, then run:") |
599 | | - print( |
600 | | - " pytest tests/test_scaffold_tool_type.py tests/test_tool_dispatch_sync.py " |
601 | | - "tests/test_tool_dispatch_ordering.py tests/test_tool_dispatch_adversarial.py " |
602 | | - "tests/test_jsonl_parser.py tests/test_real_session_fixtures.py -q" |
603 | | - ) |
604 | | - print(" npm test") |
605 | | - return 0 |
606 | | - |
607 | 583 |
|
608 | 584 | if __name__ == "__main__": |
609 | 585 | raise SystemExit(main()) |
0 commit comments