Skills › Scientific Agent Skills › exploratory-data-analysis › capability_manifest.py
capability_manifest.py
184 lines · skills/exploratory-data-analysis/scripts/capability_manifest.py at 5f2bc03
1#!/usr/bin/env python3
2"""Emit the closed capability matrix or a redacted local-file manifest."""
4from __future__ import annotations
6import argparse
7from pathlib import Path
8from typing import Any
10from _capabilities import (
11 REFERENCE_ONLY_FORMATS,
12 automated_capability_rows,
13 capability_for_path,
14 validate_magic,
15)
16from _common import (
17 DEFAULT_MAX_FILE_BYTES,
18 CliError,
19 bounded_file_limit,
20 checked_input_file,
21 display_identifier,
22 emit_json,
23 run_cli,
24 sha256_file,
25 stable_token,
26)
29def build_parser() -> argparse.ArgumentParser:
30 parser = argparse.ArgumentParser(
31 description=(
32 "List exact EDA capabilities or inspect a bounded local file without "
33 "content sniffing, raw previews, or path disclosure."
34 )
35 )
36 subparsers = parser.add_subparsers(dest="command", required=True)
37 list_parser = subparsers.add_parser(
38 "list",
39 help="Print automated and reference-only capability rows",
40 )
41 list_parser.add_argument("--output", help="Optional local .json output path")
42 list_parser.add_argument(
43 "--root",
44 default=".",
45 help="Existing local directory that bounds the optional output",
46 )
47 list_parser.add_argument("--force", action="store_true")
49 inspect_parser = subparsers.add_parser(
50 "inspect",
51 help="Create a redacted manifest for one local regular file",
52 )
53 inspect_parser.add_argument("input", help="Local file path inside --root")
54 inspect_parser.add_argument(
55 "--root",
56 default=".",
57 help="Existing local directory that bounds all input/output paths",
58 )
59 inspect_parser.add_argument(
60 "--max-bytes",
61 type=int,
62 default=DEFAULT_MAX_FILE_BYTES,
63 help=f"Maximum input bytes (hard ceiling: {512 * 1024 * 1024})",
64 )
65 inspect_parser.add_argument(
66 "--sha256",
67 action="store_true",
68 help="Explicitly include a full-file SHA-256 content fingerprint",
69 )
70 inspect_parser.add_argument(
71 "--reveal-identifiers",
72 action="store_true",
73 help="Include only a sanitized basename; never include the full path",
74 )
75 inspect_parser.add_argument("--output", help="Optional local .json output path")
76 inspect_parser.add_argument("--force", action="store_true")
77 return parser
80def capability_matrix() -> dict[str, Any]:
81 """Return exact script and reference-only registrations."""
83 return {
84 "schema_version": "1.1",
85 "policy": {
86 "unknown_formats": "rejected",
87 "content_sniffing_fallback": False,
88 "reference_only_means_executable_support": False,
89 "all_automated_inspection_is_bounded": True,
90 },
91 "automated": automated_capability_rows(),
92 "reference_only": [
93 {"suffix": suffix, **REFERENCE_ONLY_FORMATS[suffix]}
94 for suffix in sorted(REFERENCE_ONLY_FORMATS)
95 ],
96 }
99def inspect_manifest(
100 path: Path,
101 *,
102 max_bytes: int,
103 include_sha256: bool = False,
104 reveal_identifiers: bool = False,
105) -> dict[str, Any]:
106 """Build a redacted manifest from a previously checked local file."""
108 capability = capability_for_path(path)
109 signature_check_supported = capability["suffix"] not in {
110 ".csv",
111 ".tsv",
112 ".json",
113 }
114 if capability["tier"].startswith("automated") and signature_check_supported:
115 validate_magic(path, capability["suffix"])
116 info = path.stat()
117 manifest: dict[str, Any] = {
118 "schema_version": "1.1",
119 "file": {
120 "file_id": stable_token(str(path), kind="file"),
121 "basename": display_identifier(
122 path.name,
123 kind="filename",
124 reveal_identifiers=reveal_identifiers,
125 ),
126 "full_path_emitted": False,
127 "size_bytes": info.st_size,
128 "regular_file": True,
129 "symlink": False,
130 },
131 "capability": capability,
132 "inspection": {
133 "format_signature_checked": (
134 capability["tier"].startswith("automated")
135 and signature_check_supported
136 ),
137 "content_values_read": False,
138 "reference_only": capability["tier"] == "reference_only",
139 },
140 }
141 if include_sha256:
142 manifest["file"]["sha256"] = sha256_file(path, max_bytes=max_bytes)
143 manifest["file"]["sha256_explicitly_requested"] = True
144 return manifest
147def _main() -> None:
148 args = build_parser().parse_args()
149 if args.command == "list":
150 emit_json(
151 capability_matrix(),
152 output=args.output,
153 root=args.root,
154 force=args.force,
155 )
156 return
157 if args.command != "inspect":
158 raise CliError("unsupported command")
159 max_bytes = bounded_file_limit(args.max_bytes)
160 path = checked_input_file(
161 args.input,
162 root=args.root,
163 max_bytes=max_bytes,
164 )
165 report = inspect_manifest(
166 path,
167 max_bytes=max_bytes,
168 include_sha256=args.sha256,
169 reveal_identifiers=args.reveal_identifiers,
170 )
171 emit_json(
172 report,
173 output=args.output,
174 root=args.root,
175 force=args.force,
176 )
179def main() -> int:
180 return run_cli(_main)
183if __name__ == "__main__":
184 raise SystemExit(main())