@delta-infra/cli 1.1.18 → 1.1.21

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 (77) hide show
  1. package/README.md +507 -513
  2. package/checksums.txt +5 -5
  3. package/package.json +3 -2
  4. package/scripts/install-wizard.js +689 -701
  5. package/scripts/install.js +442 -442
  6. package/scripts/run.js +65 -65
  7. package/skills/delta-protein/AGENTS.md +73 -0
  8. package/skills/delta-protein/SKILL.md +389 -0
  9. package/skills/delta-protein/agents/openai.yaml +4 -0
  10. package/skills/delta-protein/references/bfitter.md +64 -0
  11. package/skills/delta-protein/references/catapro_predict.md +63 -0
  12. package/skills/delta-protein/references/catpred_predict.md +65 -0
  13. package/skills/delta-protein/references/caver_tunnel_analyze.md +63 -0
  14. package/skills/delta-protein/references/consensus_mutation.md +61 -0
  15. package/skills/delta-protein/references/consurf_predict.md +67 -0
  16. package/skills/delta-protein/references/crop_sequence.md +69 -0
  17. package/skills/delta-protein/references/dock.md +82 -0
  18. package/skills/delta-protein/references/dock_score.md +70 -0
  19. package/skills/delta-protein/references/easifa_predict_active_site.md +70 -0
  20. package/skills/delta-protein/references/ephod_predict_mutation.md +66 -0
  21. package/skills/delta-protein/references/ephod_predict_protein.md +61 -0
  22. package/skills/delta-protein/references/esmfold2_predict.md +67 -0
  23. package/skills/delta-protein/references/evcouplings_mutation.md +64 -0
  24. package/skills/delta-protein/references/fasta_to_sequence.md +59 -0
  25. package/skills/delta-protein/references/foldseek.md +61 -0
  26. package/skills/delta-protein/references/generate_msa.md +61 -0
  27. package/skills/delta-protein/references/interpro_analyze.md +63 -0
  28. package/skills/delta-protein/references/mpnn_design_sequence.md +71 -0
  29. package/skills/delta-protein/references/multievolve.md +73 -0
  30. package/skills/delta-protein/references/pisa_query.md +70 -0
  31. package/skills/delta-protein/references/plip_analyze.md +65 -0
  32. package/skills/delta-protein/references/pocket_prediction.md +63 -0
  33. package/skills/delta-protein/references/protein_design.md +65 -0
  34. package/skills/delta-protein/references/protein_evolution.md +77 -0
  35. package/skills/delta-protein/references/protein_sol_predict_mutation.md +66 -0
  36. package/skills/delta-protein/references/protein_sol_predict_protein.md +61 -0
  37. package/skills/delta-protein/references/protenix.md +66 -0
  38. package/skills/delta-protein/references/protrek_search.md +90 -0
  39. package/skills/delta-protein/references/pubchem_search.md +68 -0
  40. package/skills/delta-protein/references/pubmed_search.md +79 -0
  41. package/skills/delta-protein/references/reap_design_primer.md +69 -0
  42. package/skills/delta-protein/references/rfd3_design.md +65 -0
  43. package/skills/delta-protein/references/structure_to_sequence.md +61 -0
  44. package/skills/delta-protein/references/summarize_result.md +68 -0
  45. package/skills/delta-protein/references/surface_charge.md +84 -0
  46. package/skills/delta-protein/references/uniprot_batch_query.md +63 -0
  47. package/skills/delta-protein/references/uniprot_query.md +64 -0
  48. package/skills/delta-protein/references/zero_shot_mutant_candidates.md +76 -0
  49. package/skills/delta-protein/scripts/gen_references.py +167 -0
  50. package/skills/delta-protein/scripts/output_metadata.json +275 -0
  51. package/skills/delta-sandbox/SKILL.md +388 -388
  52. package/skills/delta-sandbox/agents/openai.yaml +4 -4
  53. package/skills/delta-sandbox/references/commands.md +58 -58
  54. package/skills/delta-sandbox/references/lifecycle.md +100 -100
  55. package/skills/delta-sandbox/references/recipes.md +138 -138
  56. package/skills/delta-science/AGENTS.md +47 -47
  57. package/skills/delta-science/SKILL.md +227 -227
  58. package/skills/delta-science/agents/openai.yaml +4 -4
  59. package/skills/delta-science/references/antbo.md +105 -105
  60. package/skills/delta-science/references/delta-bo.md +92 -92
  61. package/skills/delta-science/references/gsasii.md +118 -118
  62. package/skills/delta-science/references/lammps.md +68 -68
  63. package/skills/delta-science/references/ldm-bo.md +132 -132
  64. package/skills/delta-science/references/materials-design.md +130 -130
  65. package/skills/delta-science/references/pubchem.md +128 -128
  66. package/skills/delta-science/references/pymatgen.md +69 -69
  67. package/skills/delta-science/references/qe.md +134 -134
  68. package/skills/delta-science/references/rdkit.md +141 -141
  69. package/skills/delta-science/references/strbo.md +95 -95
  70. package/skills/delta-science/references/synbo.md +201 -201
  71. package/skills/delta-science/references/xrd.md +688 -197
  72. package/skills/delta-science/scripts/catalog.py +92 -92
  73. package/skills/delta-science/scripts/generated_catalog.py +69 -69
  74. package/skills/delta-science/scripts/invoke.py +346 -346
  75. package/skills/delta-shared/SKILL.md +113 -113
  76. package/skills/delta-shared/agents/openai.yaml +4 -4
  77. package/skills/manifest.json +20 -19
@@ -0,0 +1,63 @@
1
+ # UniProt Batch Query (`huada_protein_tools` / `uniprot_batch_query`)
2
+
3
+ This endpoint is a real endpoint of the `huada_protein_tools` tool. It can only be
4
+ called via `delta-cli science invoke`; the business response described below lives
5
+ under the CLI top-level `data` — read fields as documented in this file, do not
6
+ recursively search. This file only documents the endpoint schema; it does not
7
+ authorize direct HTTP. Endpoint names are authoritative as returned by
8
+ `delta-cli science endpoints list huada_protein_tools`; use them verbatim — never
9
+ guess, truncate, or alias.
10
+
11
+ ## Overview
12
+
13
+ Batch query UniProt. Supports UniProtKB accessions, UniParc IDs, or keyword
14
+ searches. Does not support direct text retrieval.
15
+ - Input: List[str]
16
+ - Output:
17
+ - source: uniprotkb | uniparc
18
+ - resultjson: {query_str: {uniprotkb/uniparc: ..., structure_links: [...], ...}, ...}
19
+ - Use case: query metadata for multiple proteins at once
20
+
21
+ ## Invocation
22
+
23
+ ```text
24
+ delta-cli science invoke --tool huada_protein_tools --endpoint uniprot_batch_query --data JSON
25
+ ```
26
+
27
+ Required input groups (at least one per group; all groups must be satisfied):
28
+
29
+ - `query`
30
+
31
+ Request fields:
32
+
33
+ | Field | Description | Default |
34
+ |---|---|---|
35
+ | `query` | UniProt accession / UniParc ID list | - |
36
+
37
+ ## Response
38
+
39
+ - CLI top-level envelope: a call succeeds only when the exit code is 0 AND `ok` is `true`; the service response lives under `data`.
40
+ - Server-side contract: `status` (success/error/partial), `message`, `tool_type`, `resultjson`, `download_url`; some endpoints also return `total_count`. `resultjson` may be a single object or an array — read only the keys documented in this file, do not recursively search.
41
+ - Binary/file artifacts (such as the PDB/XLSX/FASTA/ZIP files that `download_url` points to) are downloaded and verified with
42
+ `delta-cli science file download --tool huada_protein_tools --endpoint ... --output <path>`;
43
+ do not treat file contents as ordinary JSON results.
44
+
45
+ ## Output Projection
46
+
47
+ > Verification status: derived from ToolSpec/agent templates; pending live calibration
48
+
49
+ - Projection content: Metadata per query id (uniprotkb/uniparc, name, species, sequence length, structure_links)
50
+ - Presentation form: Entry list/table for easy selection
51
+ - Downstream reuse: sequence of the selected entry can be used as downstream input
52
+ ## Output Template
53
+
54
+ - Title: **UniProt candidate match result**
55
+ - One row per candidate entry: Entry/protein name/species/sequence identity/sequence length (table or list)
56
+ - Prompt the user to confirm the selection
57
+
58
+ ## Boundaries & Failure Modes
59
+
60
+ - File-type parameters (`structure_file`/`sequence_file`/`msa_file` etc.) take server-accessible URL/path strings, not local upload paths; a `download_url` or `pdb_url` returned by an upstream endpoint can be backfilled directly as downstream input.
61
+ - Make only the minimal calls needed to complete the task; `--data` carries only the fields declared by this endpoint. Do not invent enum values or branches beyond the required combinations.
62
+ - Do not pass off local tool (RDKit/pymatgen/LAMMPS/self-attached MCP) results as this endpoint's results; do not connect directly to Huada or gateway URLs.
63
+ - On failure, report a short summary of the CLI's raw error; do not blindly retry after remote computation timeouts (the outcome may be unknown because the task may already have happened).
@@ -0,0 +1,64 @@
1
+ # UniProt Protein Query (`huada_protein_tools` / `uniprot_query`)
2
+
3
+ This endpoint is a real endpoint of the `huada_protein_tools` tool. It can only be
4
+ called via `delta-cli science invoke`; the business response described below lives
5
+ under the CLI top-level `data` — read fields as documented in this file, do not
6
+ recursively search. This file only documents the endpoint schema; it does not
7
+ authorize direct HTTP. Endpoint names are authoritative as returned by
8
+ `delta-cli science endpoints list huada_protein_tools`; use them verbatim — never
9
+ guess, truncate, or alias.
10
+
11
+ ## Overview
12
+
13
+ Query UniProt for metadata of known proteins.
14
+ - Input: a single string — a UniProtKB accession, UniParc ID, gene name, or protein name.
15
+ - Not for: open-ended functional/reaction description style database retrieval. Use `protrek_search` for such retrieval.
16
+ - Output:
17
+ - source: uniprotkb | uniparc
18
+ - resultjson: {uniprotkb/uniparc: ..., structure_links: [...], sequence, sequence_length, protein_name, organism}
19
+ - Use case: metadata queries for known protein entries — sequence, name, organism, structure links, etc.
20
+
21
+ ## Invocation
22
+
23
+ ```text
24
+ delta-cli science invoke --tool huada_protein_tools --endpoint uniprot_query --data JSON
25
+ ```
26
+
27
+ Required input groups (at least one per group; all groups must be satisfied):
28
+
29
+ - `query`
30
+
31
+ Request fields:
32
+
33
+ | Field | Description | Default |
34
+ |---|---|---|
35
+ | `query` | UniProt accession / UniParc ID / gene name / protein name; for known-protein metadata queries, not open-ended functional/reaction retrieval | - |
36
+
37
+ ## Response
38
+
39
+ - CLI top-level envelope: a call succeeds only when the exit code is 0 AND `ok` is `true`; the service response lives under `data`.
40
+ - Server-side contract: `status` (success/error/partial), `message`, `tool_type`, `resultjson`, `download_url`; some endpoints also return `total_count`. `resultjson` may be a single object or an array — read only the keys documented in this file, do not recursively search.
41
+ - Binary/file artifacts (such as the PDB/XLSX/FASTA/ZIP files that `download_url` points to) are downloaded and verified with
42
+ `delta-cli science file download --tool huada_protein_tools --endpoint ... --output <path>`;
43
+ do not treat file contents as ordinary JSON results.
44
+
45
+ ## Output Projection
46
+
47
+ > Verification status: derived from ToolSpec/agent templates; pending live calibration
48
+
49
+ - Projection content: protein_name, organism, sequence_length, sequence, structure_links, function-related fields
50
+ - Presentation form: Metadata summary (name/species/length) + sequence inline code + structure links
51
+ - Downstream reuse: sequence -> protein_sequence for interpro_analyze / consensus_mutation
52
+ ## Output Template
53
+
54
+ - Title: **UniProt query result**
55
+ - Lead with the conclusion: protein name, species, sequence length, function annotation and key regions (summarized, not raw JSON key names)
56
+ - Show the full sequence as inline code; structure_links as `[name](full URL)`
57
+ - Note the sequence can serve as downstream protein_sequence input
58
+
59
+ ## Boundaries & Failure Modes
60
+
61
+ - File-type parameters (`structure_file`/`sequence_file`/`msa_file` etc.) take server-accessible URL/path strings, not local upload paths; a `download_url` or `pdb_url` returned by an upstream endpoint can be backfilled directly as downstream input.
62
+ - Make only the minimal calls needed to complete the task; `--data` carries only the fields declared by this endpoint. Do not invent enum values or branches beyond the required combinations.
63
+ - Do not pass off local tool (RDKit/pymatgen/LAMMPS/self-attached MCP) results as this endpoint's results; do not connect directly to Huada or gateway URLs.
64
+ - On failure, report a short summary of the CLI's raw error; do not blindly retry after remote computation timeouts (the outcome may be unknown because the task may already have happened).
@@ -0,0 +1,76 @@
1
+ # Zero-shot Mutant Candidate Prediction (`huada_protein_tools` / `zero_shot_mutant_candidates`)
2
+
3
+ This endpoint is a real endpoint of the `huada_protein_tools` tool. It can only be
4
+ called via `delta-cli science invoke`; the business response described below lives
5
+ under the CLI top-level `data` — read fields as documented in this file, do not
6
+ recursively search. This file only documents the endpoint schema; it does not
7
+ authorize direct HTTP. Endpoint names are authoritative as returned by
8
+ `delta-cli science endpoints list huada_protein_tools`; use them verbatim — never
9
+ guess, truncate, or alias.
10
+
11
+ ## Overview
12
+
13
+ Zero-shot mutant candidate prediction: evaluate the benefit of single-site mutations
14
+ with pretrained models when no experimental data is available.
15
+ - Input: `protein_sequence` / `sequence_file` / `structure_file` (choose one)
16
+ - Optional: `chain` (A), `msa_file`, `model_name` (default ["esmc"]; options: esmc/esm2/saprot/thermompnn/protein_mpnn/soluble_mpnn)
17
+ - Output: mutation score results `download_url`, `resultjson`
18
+ - Use case: predicting beneficial mutations, optimizing protein stability
19
+ - Models that depend on a structure file: thermompnn/saprot/protein_mpnn/soluble_mpnn
20
+ - Model selection advice: thermostability → thermompnn; solubility → soluble_mpnn; activity → esmc/esm2/saprot.
21
+ - Input hints: "thermostability/stability" → `model_name` = `["thermompnn"]`; "solubility/soluble" → `["soluble_mpnn"]`; "activity" → `["esm2","esmc","saprot"]`.
22
+
23
+ ## Invocation
24
+
25
+ ```text
26
+ delta-cli science invoke --tool huada_protein_tools --endpoint zero_shot_mutant_candidates --data JSON
27
+ ```
28
+
29
+ Required input groups (at least one per group; all groups must be satisfied):
30
+
31
+ - `protein_sequence` / `sequence_file` / `structure_file`
32
+
33
+ Server-side default parameters:
34
+
35
+ - `model_name` = `["esmc"]`
36
+
37
+ Request fields:
38
+
39
+ | Field | Description | Default |
40
+ |---|---|---|
41
+ | `protein_sequence` | | - |
42
+ | `sequence_file` | FASTA/FA | - |
43
+ | `structure_file` | PDB/CIF | - |
44
+ | `chain` | Protein chain identifier, default A | A |
45
+ | `msa_file` | A3M MSA | - |
46
+ | `model_name` | Model name list, default ["esmc"] | ["esmc"] |
47
+
48
+ ## Response
49
+
50
+ - CLI top-level envelope: a call succeeds only when the exit code is 0 AND `ok` is `true`; the service response lives under `data`.
51
+ - Server-side contract: `status` (success/error/partial), `message`, `tool_type`, `resultjson`, `download_url`; some endpoints also return `total_count`. `resultjson` may be a single object or an array — read only the keys documented in this file, do not recursively search.
52
+ - Binary/file artifacts (such as the PDB/XLSX/FASTA/ZIP files that `download_url` points to) are downloaded and verified with
53
+ `delta-cli science file download --tool huada_protein_tools --endpoint ... --output <path>`;
54
+ do not treat file contents as ordinary JSON results.
55
+
56
+ ## Output Projection
57
+
58
+ > Verification status: derived from ToolSpec/agent templates; pending live calibration
59
+
60
+ - Projection content: Mutant candidate summary: mutation (keep the tool's original notation), source model, score/confidence, recommendation/rationale; positions overlapping the active site are marked as not recommended
61
+ - Presentation form: Markdown table; score column names restated in natural language, not raw JSON key names
62
+ - Downstream reuse: result_files list -> summarize_result; ConSurf conservation / EasIFA active site / PubMed literature are annotations only, not recommendation criteria
63
+ ## Output Template
64
+
65
+ - Title: **Zero-shot mutant candidate prediction result**
66
+ - Markdown table: `| Mutation | Source model | Score/confidence | Recommendation/rationale |`, keeping the tool's original mutation/model notation; column names adjustable per resultjson
67
+ - Positions overlapping the active site -> mark as "Overlaps active site, not recommended"; conservation is only a note in the rationale column
68
+ - If PubMed reports the mutation: note the PMID and distinguish "reported hotspot" / "new target not yet reported"
69
+ - Never invent evidence not present in the data; do not use raw JSON key names as body text
70
+
71
+ ## Boundaries & Failure Modes
72
+
73
+ - File-type parameters (`structure_file`/`sequence_file`/`msa_file` etc.) take server-accessible URL/path strings, not local upload paths; a `download_url` or `pdb_url` returned by an upstream endpoint can be backfilled directly as downstream input.
74
+ - Make only the minimal calls needed to complete the task; `--data` carries only the fields declared by this endpoint. Do not invent enum values or branches beyond the required combinations.
75
+ - Do not pass off local tool (RDKit/pymatgen/LAMMPS/self-attached MCP) results as this endpoint's results; do not connect directly to Huada or gateway URLs.
76
+ - On failure, report a short summary of the CLI's raw error; do not blindly retry after remote computation timeouts (the outcome may be unknown because the task may already have happened).
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ delta-protein reference generator / checker.
5
+
6
+ Self-contained (stdlib only). Reads scripts/output_metadata.json and, for every
7
+ endpoint, idempotently inserts/updates the `## Output Projection` and
8
+ `## Output Template` sections in references/<endpoint>.md. Run it again after
9
+ metadata changes to regenerate cleanly.
10
+
11
+ Usage:
12
+ python scripts/gen_references.py # regenerate Output Projection/Template sections
13
+ python scripts/gen_references.py --check # validate all references + SKILL links
14
+ python scripts/gen_references.py --sync <dir> # copy references + SKILL.md to an install dir
15
+ """
16
+ import json
17
+ import os
18
+ import re
19
+ import sys
20
+
21
+ BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # skills/delta-protein
22
+ REFS = os.path.join(BASE, "references")
23
+ SKILL = os.path.join(BASE, "SKILL.md")
24
+ META = os.path.join(BASE, "scripts", "output_metadata.json")
25
+
26
+ EXPECTED_SECTIONS = ["## Overview", "## Invocation", "## Response", "## Output Projection", "## Output Template", "## Boundaries & Failure Modes"]
27
+
28
+ DEFAULT_TEMPLATE = (
29
+ "- Title: `**{tool_display_name} analysis result**`\n"
30
+ "- Lead with the conclusion: summarize key results from resultjson and what they mean for the user's question; present data in natural language or friendly table column names, not raw JSON key names.\n"
31
+ "- Downloads: when a `download_url` is present, give `[name](full URL)` in the \"Result files & downloads\" section."
32
+ )
33
+
34
+
35
+ def load_meta():
36
+ with open(META, encoding="utf-8") as f:
37
+ return json.load(f)
38
+
39
+
40
+ def build_projection(meta):
41
+ prov = "verified by a real `delta-cli` invocation" if meta["provenance"] == "tested" else "derived from ToolSpec/agent templates; pending live calibration"
42
+ return "\n".join([
43
+ "## Output Projection",
44
+ "",
45
+ f"> Verification status: {prov}",
46
+ "",
47
+ f"- Projection content: {meta['project']}",
48
+ f"- Presentation form: {meta['form']}",
49
+ f"- Downstream reuse: {meta['downstream']}",
50
+ "",
51
+ ])
52
+
53
+
54
+ def build_template(meta):
55
+ tpl = meta.get("output_template")
56
+ if not tpl:
57
+ tpl = DEFAULT_TEMPLATE.replace("{tool_display_name}", meta.get("display_name", "Endpoint"))
58
+ tpl = tpl.strip()
59
+ return "## Output Template\n\n" + tpl + "\n\n"
60
+
61
+
62
+ def upsert_projection(lines, block):
63
+ idx = next((i for i, l in enumerate(lines) if l.strip() == "## Output Projection"), None)
64
+ if idx is None:
65
+ idx = next((i for i, l in enumerate(lines) if l.strip() == "## Output Template"), None)
66
+ if idx is not None:
67
+ # Walk from the generated-block heading and cut at the first heading that
68
+ # does NOT belong to the generated block (## Output Projection /
69
+ # ## Output Template). This keeps regeneration idempotent: the whole
70
+ # generated region is replaced instead of leaving a stale duplicate.
71
+ end = next(
72
+ (i for i in range(idx + 1, len(lines))
73
+ if lines[i].startswith("## ") and lines[i].strip() not in ("## Output Projection", "## Output Template")),
74
+ len(lines),
75
+ )
76
+ return lines[:idx] + block.splitlines(True) + lines[end:]
77
+ insert_at = next((i for i, l in enumerate(lines) if l.startswith("## Boundaries & Failure Modes")), len(lines))
78
+ return lines[:insert_at] + block.splitlines(True) + lines[insert_at:]
79
+
80
+
81
+ def regen():
82
+ meta = load_meta()
83
+ n = 0
84
+ for ep, m in sorted(meta.items()):
85
+ path = os.path.join(REFS, ep + ".md")
86
+ if not os.path.exists(path):
87
+ print(f"MISSING reference: {ep}.md", file=sys.stderr)
88
+ continue
89
+ with open(path, encoding="utf-8") as f:
90
+ text = f.read()
91
+ lines = upsert_projection(text.splitlines(True), build_projection(m) + build_template(m))
92
+ if "".join(lines) != text:
93
+ with open(path, "w", encoding="utf-8", newline="\n") as f:
94
+ f.write("".join(lines))
95
+ n += 1
96
+ print(f"regenerated {n} of {len(meta)} references")
97
+
98
+
99
+ def check():
100
+ meta = load_meta()
101
+ errors = []
102
+
103
+ for ep, m in sorted(meta.items()):
104
+ path = os.path.join(REFS, ep + ".md")
105
+ if not os.path.exists(path):
106
+ errors.append(f"missing reference {ep}.md")
107
+ continue
108
+ text = open(path, encoding="utf-8").read()
109
+ lines = [l.strip() for l in text.splitlines()]
110
+ for sec in EXPECTED_SECTIONS:
111
+ if sec not in lines:
112
+ errors.append(f"{ep}.md missing section {sec}")
113
+ for sec in ("## Output Projection", "## Output Template"):
114
+ if lines.count(sec) != 1:
115
+ errors.append(f"{ep}.md section {sec} appears {lines.count(sec)} times (expected exactly once)")
116
+ first = lines[0] if lines else ""
117
+ if ep not in first:
118
+ errors.append(f"{ep}.md title does not contain endpoint {ep}")
119
+ if "Verification status:" not in text:
120
+ errors.append(f"{ep}.md missing verification-status marker")
121
+ prov = "verified by a real `delta-cli` invocation" if m["provenance"] == "tested" else "derived from ToolSpec/agent templates; pending live calibration"
122
+ if prov not in text:
123
+ errors.append(f"{ep}.md provenance marker mismatch ({prov})")
124
+
125
+ extras = [f for f in os.listdir(REFS) if f.endswith(".md") and f[:-3] not in meta]
126
+ for e in extras:
127
+ errors.append(f"reference not in metadata: {e}")
128
+
129
+ if os.path.exists(SKILL):
130
+ skill = open(SKILL, encoding="utf-8").read()
131
+ for mref in re.findall(r"references/([a-z0-9_]+\.md)", skill):
132
+ if not os.path.exists(os.path.join(REFS, os.path.basename(mref))):
133
+ errors.append(f"SKILL.md broken link: {mref}")
134
+
135
+ if errors:
136
+ print("CHECK FAILED:")
137
+ for e in errors:
138
+ print(" - " + e)
139
+ return 1
140
+ print(f"CHECK OK: {len(meta)} references, sections + provenance + links valid")
141
+ return 0
142
+
143
+
144
+ def sync(target):
145
+ for ep in load_meta():
146
+ src = os.path.join(REFS, ep + ".md")
147
+ if os.path.exists(src):
148
+ shutil_copy(src, os.path.join(target, "references", ep + ".md"))
149
+ if os.path.exists(SKILL):
150
+ shutil_copy(SKILL, os.path.join(target, "SKILL.md"))
151
+ print(f"synced references + SKILL.md -> {target}")
152
+
153
+
154
+ def shutil_copy(src, dst):
155
+ import shutil
156
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
157
+ shutil.copyfile(src, dst)
158
+
159
+
160
+ if __name__ == "__main__":
161
+ args = sys.argv[1:]
162
+ if "--check" in args:
163
+ sys.exit(check())
164
+ if "--sync" in args:
165
+ sync(args[args.index("--sync") + 1])
166
+ sys.exit(0)
167
+ regen()