@tiangong-lca/cli 0.0.1

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 (96) hide show
  1. package/README.md +368 -0
  2. package/bin/tiangong.d.ts +1 -0
  3. package/bin/tiangong.js +32 -0
  4. package/dist/scripts/assert-full-coverage.js +77 -0
  5. package/dist/scripts/assert-full-coverage.js.map +1 -0
  6. package/dist/src/cli.js +2740 -0
  7. package/dist/src/cli.js.map +1 -0
  8. package/dist/src/lib/artifacts.js +86 -0
  9. package/dist/src/lib/artifacts.js.map +1 -0
  10. package/dist/src/lib/dotenv.js +36 -0
  11. package/dist/src/lib/dotenv.js.map +1 -0
  12. package/dist/src/lib/env.js +139 -0
  13. package/dist/src/lib/env.js.map +1 -0
  14. package/dist/src/lib/errors.js +38 -0
  15. package/dist/src/lib/errors.js.map +1 -0
  16. package/dist/src/lib/flow-build-alias-map.js +299 -0
  17. package/dist/src/lib/flow-build-alias-map.js.map +1 -0
  18. package/dist/src/lib/flow-get.js +81 -0
  19. package/dist/src/lib/flow-get.js.map +1 -0
  20. package/dist/src/lib/flow-governance.js +164 -0
  21. package/dist/src/lib/flow-governance.js.map +1 -0
  22. package/dist/src/lib/flow-list.js +187 -0
  23. package/dist/src/lib/flow-list.js.map +1 -0
  24. package/dist/src/lib/flow-publish-reviewed-data.js +808 -0
  25. package/dist/src/lib/flow-publish-reviewed-data.js.map +1 -0
  26. package/dist/src/lib/flow-publish-version.js +541 -0
  27. package/dist/src/lib/flow-publish-version.js.map +1 -0
  28. package/dist/src/lib/flow-read.js +310 -0
  29. package/dist/src/lib/flow-read.js.map +1 -0
  30. package/dist/src/lib/flow-regen-product.js +1396 -0
  31. package/dist/src/lib/flow-regen-product.js.map +1 -0
  32. package/dist/src/lib/flow-remediate.js +821 -0
  33. package/dist/src/lib/flow-remediate.js.map +1 -0
  34. package/dist/src/lib/http.js +45 -0
  35. package/dist/src/lib/http.js.map +1 -0
  36. package/dist/src/lib/io.js +30 -0
  37. package/dist/src/lib/io.js.map +1 -0
  38. package/dist/src/lib/kb-search.js +83 -0
  39. package/dist/src/lib/kb-search.js.map +1 -0
  40. package/dist/src/lib/lifecyclemodel-auto-build.js +1328 -0
  41. package/dist/src/lib/lifecyclemodel-auto-build.js.map +1 -0
  42. package/dist/src/lib/lifecyclemodel-orchestrate.js +1556 -0
  43. package/dist/src/lib/lifecyclemodel-orchestrate.js.map +1 -0
  44. package/dist/src/lib/lifecyclemodel-publish-build.js +296 -0
  45. package/dist/src/lib/lifecyclemodel-publish-build.js.map +1 -0
  46. package/dist/src/lib/lifecyclemodel-publish-resulting-process.js +101 -0
  47. package/dist/src/lib/lifecyclemodel-publish-resulting-process.js.map +1 -0
  48. package/dist/src/lib/lifecyclemodel-resulting-process.js +1152 -0
  49. package/dist/src/lib/lifecyclemodel-resulting-process.js.map +1 -0
  50. package/dist/src/lib/lifecyclemodel-validate-build.js +238 -0
  51. package/dist/src/lib/lifecyclemodel-validate-build.js.map +1 -0
  52. package/dist/src/lib/llm.js +383 -0
  53. package/dist/src/lib/llm.js.map +1 -0
  54. package/dist/src/lib/process-auto-build.js +717 -0
  55. package/dist/src/lib/process-auto-build.js.map +1 -0
  56. package/dist/src/lib/process-batch-build.js +409 -0
  57. package/dist/src/lib/process-batch-build.js.map +1 -0
  58. package/dist/src/lib/process-get.js +66 -0
  59. package/dist/src/lib/process-get.js.map +1 -0
  60. package/dist/src/lib/process-publish-build.js +451 -0
  61. package/dist/src/lib/process-publish-build.js.map +1 -0
  62. package/dist/src/lib/process-resume-build.js +364 -0
  63. package/dist/src/lib/process-resume-build.js.map +1 -0
  64. package/dist/src/lib/publish.js +562 -0
  65. package/dist/src/lib/publish.js.map +1 -0
  66. package/dist/src/lib/remote.js +88 -0
  67. package/dist/src/lib/remote.js.map +1 -0
  68. package/dist/src/lib/review-flow.js +1110 -0
  69. package/dist/src/lib/review-flow.js.map +1 -0
  70. package/dist/src/lib/review-lifecyclemodel.js +727 -0
  71. package/dist/src/lib/review-lifecyclemodel.js.map +1 -0
  72. package/dist/src/lib/review-process.js +702 -0
  73. package/dist/src/lib/review-process.js.map +1 -0
  74. package/dist/src/lib/run.js +106 -0
  75. package/dist/src/lib/run.js.map +1 -0
  76. package/dist/src/lib/state-lock.js +150 -0
  77. package/dist/src/lib/state-lock.js.map +1 -0
  78. package/dist/src/lib/supabase-client.js +269 -0
  79. package/dist/src/lib/supabase-client.js.map +1 -0
  80. package/dist/src/lib/supabase-json-ordered-write.js +186 -0
  81. package/dist/src/lib/supabase-json-ordered-write.js.map +1 -0
  82. package/dist/src/lib/supabase-rest.js +138 -0
  83. package/dist/src/lib/supabase-rest.js.map +1 -0
  84. package/dist/src/lib/supabase-session.js +424 -0
  85. package/dist/src/lib/supabase-session.js.map +1 -0
  86. package/dist/src/lib/tidas-sdk-package-validator.js +493 -0
  87. package/dist/src/lib/tidas-sdk-package-validator.js.map +1 -0
  88. package/dist/src/lib/unstructured.js +203 -0
  89. package/dist/src/lib/unstructured.js.map +1 -0
  90. package/dist/src/lib/user-api-key.js +75 -0
  91. package/dist/src/lib/user-api-key.js.map +1 -0
  92. package/dist/src/lib/validation.js +191 -0
  93. package/dist/src/lib/validation.js.map +1 -0
  94. package/dist/src/main.js +38 -0
  95. package/dist/src/main.js.map +1 -0
  96. package/package.json +81 -0
package/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # TianGong LCA CLI
2
+
3
+ `tiangong-lca-cli` is the unified TianGong command-line entrypoint.
4
+
5
+ Current implementation choices:
6
+
7
+ - TypeScript on Node 24
8
+ - ship built JavaScript artifacts from `dist/`
9
+ - direct REST / Edge Function calls instead of MCP
10
+ - file-first input and JSON-first output
11
+ - one stable command surface for humans, agents, CI, and skills
12
+ - explicit npm production runtime dependencies only where they reduce long-term maintenance risk: native `@supabase/supabase-js` and direct `@tiangong-lca/tidas-sdk`
13
+
14
+ ## MCP replacement policy
15
+
16
+ The CLI replaces MCP with two explicit strategies:
17
+
18
+ - strategy 1: call domain APIs directly through `tiangong-lca-edge-functions` (Edge Functions / REST)
19
+ - strategy 2: access Supabase directly without MCP through the native `@supabase/supabase-js` client, while keeping deterministic query semantics and stable artifact/report contracts inside the CLI
20
+
21
+ This prevents reintroducing a generic MCP transport layer into the CLI runtime.
22
+
23
+ ## Implemented commands
24
+
25
+ - `tiangong doctor`
26
+ - `tiangong search flow`
27
+ - `tiangong search process`
28
+ - `tiangong search lifecyclemodel`
29
+ - `tiangong process get`
30
+ - `tiangong process auto-build`
31
+ - `tiangong process resume-build`
32
+ - `tiangong process publish-build`
33
+ - `tiangong process batch-build`
34
+ - `tiangong lifecyclemodel auto-build`
35
+ - `tiangong lifecyclemodel validate-build`
36
+ - `tiangong lifecyclemodel publish-build`
37
+ - `tiangong lifecyclemodel build-resulting-process`
38
+ - `tiangong lifecyclemodel publish-resulting-process`
39
+ - `tiangong lifecyclemodel orchestrate`
40
+ - `tiangong review process`
41
+ - `tiangong review flow`
42
+ - `tiangong review lifecyclemodel`
43
+ - `tiangong flow get`
44
+ - `tiangong flow list`
45
+ - `tiangong flow remediate`
46
+ - `tiangong flow publish-version`
47
+ - `tiangong flow publish-reviewed-data`
48
+ - `tiangong flow build-alias-map`
49
+ - `tiangong flow scan-process-flow-refs`
50
+ - `tiangong flow plan-process-flow-repairs`
51
+ - `tiangong flow apply-process-flow-repairs`
52
+ - `tiangong flow regen-product`
53
+ - `tiangong flow validate-processes`
54
+ - `tiangong publish run`
55
+ - `tiangong validation run`
56
+ - `tiangong admin embedding-run`
57
+
58
+ ## Remaining planned command surface
59
+
60
+ The remaining planned placeholders in the documented surface are now limited to `auth *` and `job *`.
61
+
62
+ The stable launcher is `bin/tiangong.js`. It loads the compiled runtime at `dist/src/main.js`, while `npm start -- ...` rebuilds and dogfoods the same launcher path.
63
+
64
+ ## Quality gate
65
+
66
+ The repository enforces:
67
+
68
+ - `npm run lint`
69
+ - `npm run prettier`
70
+ - `npm test`
71
+ - `npm run test:coverage`
72
+ - `npm run test:coverage:assert-full`
73
+ - `npm run prepush:gate`
74
+
75
+ `npm run lint` is the required local gate. It runs `eslint`, deprecated API diagnostics, `prettier --check`, a coverage-ignore guard, and `tsc`. Coverage is enforced at 100% for `src/**/*.ts`. Launcher smoke tests remain in the normal test suite, and coverage-ignore pragmas are forbidden as a substitute for test coverage.
76
+
77
+ ## Releases
78
+
79
+ The public npm package name for this repository is `@tiangong-lca/cli`.
80
+
81
+ Normal releases are tag-driven and published by GitHub Actions:
82
+
83
+ - open a release-prep PR from `main`
84
+ - bump only the CLI package version in `package.json`
85
+ - after merge, `.github/workflows/tag-release-from-merge.yml` creates `cli-vX.Y.Z`
86
+ - `.github/workflows/publish.yml` publishes from that immutable tag through npm Trusted Publishing
87
+
88
+ One-time repository and npm Trusted Publisher setup lives in [docs/release-setup.md](./docs/release-setup.md).
89
+
90
+ ## Quick start
91
+
92
+ Install Node.js `24.x` with any platform-native path you already use. The CLI only requires a working Node 24 runtime; it does not depend on `bash`, `nvm`, or other Unix-only setup tools. Examples that work well:
93
+
94
+ - Windows: the official Node.js `24.x` installer
95
+ - macOS: the official Node.js `24.x` installer, `fnm`, or `nvm`
96
+ - Linux: your preferred Node 24 package/install method
97
+
98
+ ```bash
99
+ npm ci
100
+ npm run build
101
+ ```
102
+
103
+ Once the package is published, install it with:
104
+
105
+ ```bash
106
+ npm install --global @tiangong-lca/cli
107
+ ```
108
+
109
+ Initialize `.env` by duplicating `.env.example` to `.env` with your editor or file manager. Any equivalent copy action is fine on macOS, Linux, or Windows.
110
+
111
+ Recommended cross-platform launchers:
112
+
113
+ - `npm exec tiangong -- ...`
114
+ - `node ./bin/tiangong.js ...`
115
+ - `node ./dist/src/main.js ...`
116
+
117
+ `npm start -- ...` is still available for local CLI development, but it is a rebuild-and-run convenience wrapper rather than the canonical public entrypoint.
118
+
119
+ Repository-local quality gate:
120
+
121
+ ```bash
122
+ npm run lint
123
+ npm test
124
+ npm run test:coverage
125
+ npm run test:coverage:assert-full
126
+ npm run prepush:gate
127
+ ```
128
+
129
+ Current public CLI env contract:
130
+
131
+ ```bash
132
+ TIANGONG_LCA_API_BASE_URL=
133
+ TIANGONG_LCA_API_KEY=
134
+ TIANGONG_LCA_REGION=us-east-1
135
+ TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY=
136
+ TIANGONG_LCA_SESSION_FILE=
137
+ TIANGONG_LCA_DISABLE_SESSION_CACHE=false
138
+ TIANGONG_LCA_FORCE_REAUTH=false
139
+ ```
140
+
141
+ `TIANGONG_LCA_API_KEY` is the TianGong user API key generated from the account page, not a Supabase project key. The CLI uses it only as a bootstrap credential, exchanges it for a user session with `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY`, then reuses the resolved access token for both Edge Functions and direct Supabase access.
142
+
143
+ Optional review-only env block: ignore it unless `tiangong review process --enable-llm` or `tiangong review flow --enable-llm` is enabled. `TIANGONG_LCA_REVIEW_LLM_BASE_URL` must point to an OpenAI-compatible Responses API base URL; the CLI calls `<base_url>/responses`.
144
+
145
+ ```bash
146
+ TIANGONG_LCA_REVIEW_LLM_BASE_URL=
147
+ TIANGONG_LCA_REVIEW_LLM_API_KEY=
148
+ TIANGONG_LCA_REVIEW_LLM_MODEL=
149
+ ```
150
+
151
+ Internal/preparatory env surface already normalized in the repo, but not consumed by any current public `tiangong` command:
152
+
153
+ ```bash
154
+ TIANGONG_LCA_KB_SEARCH_API_BASE_URL=
155
+ TIANGONG_LCA_KB_SEARCH_API_KEY=
156
+ TIANGONG_LCA_KB_SEARCH_REGION=us-east-1
157
+
158
+ TIANGONG_LCA_UNSTRUCTURED_API_BASE_URL=
159
+ TIANGONG_LCA_UNSTRUCTURED_API_KEY=
160
+ TIANGONG_LCA_UNSTRUCTURED_PROVIDER=
161
+ TIANGONG_LCA_UNSTRUCTURED_MODEL=
162
+ TIANGONG_LCA_UNSTRUCTURED_CHUNK_TYPE=false
163
+ TIANGONG_LCA_UNSTRUCTURED_RETURN_TXT=true
164
+ ```
165
+
166
+ No extra generic `SUPABASE_URL`, `SUPABASE_KEY`, or `TIANGONG_LCA_TIDAS_SDK_DIR` env is required. The CLI derives the native `@supabase/supabase-js` client from `TIANGONG_LCA_API_BASE_URL`, bootstraps the user session from `TIANGONG_LCA_API_KEY` plus `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY`, and loads `@tiangong-lca/tidas-sdk` directly from `package.json` dependencies.
167
+
168
+ Command-level env reality:
169
+
170
+ | Command group | Required env |
171
+ | --- | --- |
172
+ | `doctor` | none |
173
+ | `search *` | `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY`, optional `TIANGONG_LCA_REGION` |
174
+ | `admin embedding-run` | `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY`, optional `TIANGONG_LCA_REGION` |
175
+ | `process get` | `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY` |
176
+ | `process auto-build` | none |
177
+ | `process resume-build` | none |
178
+ | `process publish-build` | none |
179
+ | `process batch-build` | none |
180
+ | `lifecyclemodel auto-build` | none |
181
+ | `lifecyclemodel validate-build` | none |
182
+ | `lifecyclemodel publish-build` | none |
183
+ | `lifecyclemodel orchestrate` | none |
184
+ | `lifecyclemodel build-resulting-process` | none for local-only runs; `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, and `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY` when `process_sources.allow_remote_lookup=true` |
185
+ | `lifecyclemodel publish-resulting-process` | none |
186
+ | `review process` | none for rule-only review; optional `TIANGONG_LCA_REVIEW_LLM_BASE_URL`, `TIANGONG_LCA_REVIEW_LLM_API_KEY`, and `TIANGONG_LCA_REVIEW_LLM_MODEL` when `--enable-llm` is set |
187
+ | `review flow` | none for rule-only review; optional `TIANGONG_LCA_REVIEW_LLM_BASE_URL`, `TIANGONG_LCA_REVIEW_LLM_API_KEY`, and `TIANGONG_LCA_REVIEW_LLM_MODEL` when `--enable-llm` is set |
188
+ | `review lifecyclemodel` | none |
189
+ | `flow get` | `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY` |
190
+ | `flow list` | `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY` |
191
+ | `flow remediate` | none |
192
+ | `flow publish-version` | `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY` |
193
+ | `flow publish-reviewed-data` | none for local dry-run; `TIANGONG_LCA_API_BASE_URL`, `TIANGONG_LCA_API_KEY`, and `TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY` when `--commit` publishes prepared flow/process rows |
194
+ | `flow build-alias-map` | none |
195
+ | `flow scan-process-flow-refs` | none |
196
+ | `flow plan-process-flow-repairs` | none |
197
+ | `flow apply-process-flow-repairs` | none |
198
+ | `flow regen-product` | none |
199
+ | `flow validate-processes` | none |
200
+ | `publish run` | none |
201
+ | `validation run` | none |
202
+
203
+ This CLI does not currently require KB, TianGong unstructured service, MCP, or `OPENAI_*` env keys for any public command. Optional semantic review now goes through the review-only `TIANGONG_LCA_REVIEW_LLM_*` keys instead of legacy provider-specific names. Those variables are optional as a block and only apply when `--enable-llm` is set on supported review commands. The repo already contains internal helper modules for KB and TianGong unstructured integrations; their env keys are listed in `.env.example` as internal/preparatory only, not as a public command contract.
204
+
205
+ Run the CLI:
206
+
207
+ ```bash
208
+ npm exec tiangong -- --help
209
+ npm exec tiangong -- doctor
210
+ npm exec tiangong -- doctor --json
211
+ npm exec tiangong -- search flow --input ./request.json --dry-run
212
+ npm exec tiangong -- process get --id <process-id> --version <version> --json
213
+ npm exec tiangong -- process auto-build --input ./examples/process-auto-build.request.json --json
214
+ npm exec tiangong -- process resume-build --run-id <run-id> --json
215
+ npm exec tiangong -- process publish-build --run-id <run-id> --json
216
+ npm exec tiangong -- process batch-build --input ./examples/process-batch-build.request.json --json
217
+ npm exec tiangong -- lifecyclemodel auto-build --input ./examples/lifecyclemodel-auto-build.request.json --json
218
+ npm exec tiangong -- lifecyclemodel validate-build --run-dir ./artifacts/lifecyclemodel_auto_build/<run_id> --json
219
+ npm exec tiangong -- lifecyclemodel publish-build --run-dir ./artifacts/lifecyclemodel_auto_build/<run_id> --json
220
+ npm exec tiangong -- lifecyclemodel orchestrate plan --input ./lifecyclemodel-orchestrate.request.json --out-dir ./artifacts/lifecyclemodel_recursive/<run_id> --json
221
+ npm exec tiangong -- lifecyclemodel build-resulting-process --input ./request.json --json
222
+ npm exec tiangong -- lifecyclemodel publish-resulting-process --run-dir ./runs/example --publish-processes --publish-relations --json
223
+ npm exec tiangong -- review process --run-root ./artifacts/process_from_flow/<run_id> --run-id <run_id> --out-dir ./review --json
224
+ npm exec tiangong -- review flow --rows-file ./flows.json --out-dir ./flow-review --json
225
+ npm exec tiangong -- review lifecyclemodel --run-dir ./artifacts/lifecyclemodel_auto_build/<run_id> --out-dir ./lifecyclemodel-review --json
226
+ npm exec tiangong -- flow get --id <flow-id> --version <version> --json
227
+ npm exec tiangong -- flow list --id <flow-id> --state-code 100 --limit 20 --json
228
+ npm exec tiangong -- flow remediate --input-file ./invalid-flows.jsonl --out-dir ./flow-remediation --json
229
+ npm exec tiangong -- flow publish-version --input-file ./ready-flows.jsonl --out-dir ./flow-publish --dry-run --json
230
+ npm exec tiangong -- flow publish-reviewed-data --flow-rows-file ./reviewed-flows.jsonl --original-flow-rows-file ./original-flows.jsonl --out-dir ./flow-publish-reviewed --dry-run --json
231
+ npm exec tiangong -- flow build-alias-map --old-flow-file ./old-flows.jsonl --new-flow-file ./new-flows.jsonl --out-dir ./flow-alias-map --json
232
+ npm exec tiangong -- flow scan-process-flow-refs --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --out-dir ./flow-scan --json
233
+ npm exec tiangong -- flow plan-process-flow-repairs --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --scan-findings ./flow-scan/scan-findings.json --out-dir ./flow-repair-plan --json
234
+ npm exec tiangong -- flow apply-process-flow-repairs --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --scan-findings ./flow-scan/scan-findings.json --out-dir ./flow-repair-apply --json
235
+ npm exec tiangong -- flow regen-product --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --out-dir ./flow-regen --apply --json
236
+ npm exec tiangong -- flow validate-processes --original-processes-file ./before.jsonl --patched-processes-file ./after.jsonl --scope-flow-file ./flows.jsonl --out-dir ./flow-validate --json
237
+ npm exec tiangong -- publish run --input ./examples/publish-run.request.json --dry-run
238
+ npm exec tiangong -- validation run --input-dir ./tidas-package --engine auto
239
+ npm exec tiangong -- admin embedding-run --input ./jobs.json --dry-run
240
+ ```
241
+
242
+ ## Process build scaffold
243
+
244
+ `tiangong process get` is the CLI-owned read-only process detail surface. It derives a deterministic Supabase read target from `TIANGONG_LCA_API_BASE_URL`, executes through the native `@supabase/supabase-js` client, resolves one process row by `id/version` with a latest-version fallback, and returns one structured JSON payload without reintroducing MCP, Python, or a generic transport layer.
245
+
246
+ `tiangong process auto-build` is the first migrated `process_from_flow` slice. It reads one request JSON from `--input`, loads the referenced ILCD flow dataset from `flow_file`, preserves the old run id contract (`pfw_<flow_code>_<flow_uuid8>_<operation>_<UTC_TIMESTAMP>`), and writes a local run scaffold under `artifacts/process_from_flow/<run_id>/` or `--out-dir`.
247
+
248
+ The command keeps the legacy per-run layout that later stages still expect, including `input/`, `exports/processes/`, `exports/sources/`, `cache/process_from_flow_state.json`, and `cache/agent_handoff_summary.json`. It also adds CLI-owned manifests such as the normalized request snapshot, flow summary, assembly plan, lineage manifest, invocation index, run manifest, and a compact report artifact.
249
+
250
+ `tiangong process resume-build` is the second migrated `process_from_flow` slice. It reopens one existing local run by `--run-id` or `--run-dir`, validates the required run artifacts, takes the local state lock, clears any persisted `stop_after` checkpoint, records `resume-metadata.json` and `resume-history.jsonl`, updates `invocation-index.json`, rewrites `agent_handoff_summary.json`, and emits `process-resume-build-report.json`.
251
+
252
+ `tiangong process publish-build` is the third migrated `process_from_flow` slice. It reopens one existing local run by `--run-id` or `--run-dir`, validates `process_from_flow_state.json`, `agent_handoff_summary.json`, `run-manifest.json`, and `invocation-index.json`, collects canonical process/source datasets from `exports/` or state fallbacks, writes `stage_outputs/10_publish/publish-bundle.json`, `publish-request.json`, `publish-intent.json`, rewrites `agent_handoff_summary.json`, updates the run state and invocation index, and emits `process-publish-build-report.json`.
253
+
254
+ `tiangong process batch-build` is the fourth migrated `process_from_flow` slice. It reads one batch manifest, prepares a self-contained batch root, fans out multiple local `process auto-build` runs through the CLI-owned contract, writes a structured per-item aggregate report, and preserves deterministic item-level artifact paths for downstream `resume-build` or `publish-build` steps.
255
+
256
+ `resume-build`, `publish-build`, and `batch-build` all still stop at local handoff boundaries. They do not execute remote publish CRUD or commit mode themselves; that boundary remains in `tiangong publish run`.
257
+
258
+ `tiangong lifecyclemodel auto-build` is the first migrated `lifecyclemodel_automated_builder` slice. It reads one local-run manifest from `--input`, resolves one or more `process-automated-builder` run directories, infers the process graph from shared flow UUIDs, chooses one reference process, computes `@multiplicationFactor`, writes native `json_ordered` lifecyclemodel datasets plus `run-plan.json`, `resolved-manifest.json`, `selection/selection-brief.md`, `discovery/reference-model-summary.json`, `models/**/summary.json`, `connections.json`, and `process-catalog.json`, and keeps the run local-only and read-only.
259
+
260
+ The canonical `tiangong-lca-skills/lifecyclemodel-automated-builder` entrypoint now uses a native Node `.mjs` wrapper that delegates directly to `tiangong lifecyclemodel auto-build`. The canonical build path no longer routes through Bash, Python, or MCP.
261
+
262
+ `tiangong lifecyclemodel validate-build` is the second migrated `lifecyclemodel_automated_builder` slice. It reopens one existing local lifecyclemodel build run by `--run-dir`, scans `models/*/tidas_bundle/lifecyclemodels/*.json`, runs the unified validation module against each model bundle, writes per-model reports under `reports/model-validations/`, updates `manifests/invocation-index.json`, and emits `reports/lifecyclemodel-validate-build-report.json`.
263
+
264
+ `tiangong lifecyclemodel publish-build` is the third migrated `lifecyclemodel_automated_builder` slice. It reopens one existing local lifecyclemodel build run by `--run-dir`, collects native lifecyclemodel payloads from `models/*/tidas_bundle/lifecyclemodels/*.json`, reads the aggregate validation summary when present, writes `stage_outputs/10_publish/publish-bundle.json`, `publish-request.json`, and `publish-intent.json`, updates `manifests/invocation-index.json`, and emits `reports/lifecyclemodel-publish-build-report.json`.
265
+
266
+ The current lifecyclemodel build family intentionally keeps three boundaries out of the auto-build slice:
267
+
268
+ - no remote lifecyclemodel CRUD
269
+ - no reference-model discovery against MCP / KB / LLM services
270
+ - no automatic chaining into validate-build or publish-build
271
+
272
+ `tiangong lifecyclemodel build-resulting-process` remains local-first, but it no longer hard-fails when a request explicitly enables `process_sources.allow_remote_lookup`. In that mode the CLI derives a deterministic Supabase read target from `TIANGONG_LCA_API_BASE_URL`, resolves missing process datasets by exact `id/version` with a latest-version fallback through the native `@supabase/supabase-js` client, and keeps the same local artifact contract instead of routing through MCP or semantic search.
273
+
274
+ `tiangong lifecyclemodel orchestrate` is the native recursive assembly command for multi-node product-system runs. `plan` writes `assembly-plan.json`, `graph-manifest.json`, `lineage-manifest.json`, and `boundary-report.json`; `execute` invokes only native CLI-backed builder slices and records per-invocation results under `invocations/`; `publish` reopens one orchestrator run and prepares `publish-bundle.json` plus `publish-summary.json` from prior local artifacts. The `process_builder` request surface is now intentionally narrow: only CLI-native local-build fields are accepted, and extra builder knobs are rejected during request normalization.
275
+
276
+ `tiangong review process` is the first migrated review slice. It reopens one local `process_from_flow` run under `exports/processes/`, replays the existing artifact-first review contract, writes bilingual markdown findings plus structured JSON reports, and keeps optional semantic review behind the CLI-owned `TIANGONG_LCA_REVIEW_LLM_*` abstraction instead of direct `OPENAI_*` calls in a skill script.
277
+
278
+ `tiangong review flow` is the flow-side local governance review slice. It accepts exactly one of `--rows-file`, `--flows-dir`, or `--run-root`, materializes explicit local flow snapshots when needed, writes `rule_findings.jsonl`, `llm_findings.jsonl`, `findings.jsonl`, `flow_summaries.jsonl`, `similarity_pairs.jsonl`, `flow_review_summary.json`, `flow_review_zh.md`, `flow_review_en.md`, `flow_review_timing.md`, and `flow_review_report.json`, and keeps optional semantic review behind the same CLI-owned `TIANGONG_LCA_REVIEW_LLM_*` abstraction. The current CLI slice is intentionally local-first and does not implement `--with-reference-context` or local registry enrichment yet.
279
+
280
+ `tiangong review lifecyclemodel` is the lifecyclemodel-side local review slice. It reopens one existing lifecyclemodel build run by `--run-dir`, scans `models/*/tidas_bundle/lifecyclemodels/*.json`, reuses `summary.json`, `connections.json`, `process-catalog.json`, and the aggregate `reports/lifecyclemodel-validate-build-report.json` when present, writes `model_summaries.jsonl`, `findings.jsonl`, `lifecyclemodel_review_summary.json`, `lifecyclemodel_review_zh.md`, `lifecyclemodel_review_en.md`, `lifecyclemodel_review_timing.md`, and `lifecyclemodel_review_report.json`, and stays local-first without introducing Python, LangGraph, or skill-local review runtimes.
281
+
282
+ `tiangong flow get` is the CLI-owned read-only flow detail surface. It derives a deterministic Supabase read target from `TIANGONG_LCA_API_BASE_URL`, resolves one visible flow row by `id` plus optional `version` / `user_id` / `state_code` through the native `@supabase/supabase-js` client, falls back to the latest visible version when an exact version lookup misses, and rejects ambiguous visible matches instead of guessing.
283
+
284
+ `tiangong flow list` is the CLI-owned deterministic flow enumeration surface. It queries `flows` through the native `@supabase/supabase-js` client while preserving stable `/rest/v1/flows` filter semantics such as repeated `--id`, `--state-code`, and `--type-of-dataset`, defaults to `order=id.asc,version.asc`, and can fetch all matching rows through explicit offset pagination via `--all --page-size <n>` without reintroducing MCP or skill-local transport code.
285
+
286
+ `tiangong flow remediate` is the first CLI-owned remediation slice for flow governance. It reads one invalid-flow JSON or JSONL input, applies deterministic round1 local remediation, and writes the historical remediation artifacts under one output directory without reintroducing Python or MCP.
287
+
288
+ `tiangong flow publish-version` is the first CLI-owned remote write slice for flow governance. It reads one ready-for-publish JSON or JSONL input, derives a deterministic Supabase write target from `TIANGONG_LCA_API_BASE_URL`, performs dry-run or commit mode through the native `@supabase/supabase-js` client against `/rest/v1/flows`, and preserves the historical success-list, remote-failure, and sync-report artifact names for downstream follow-up. It still does not implement round2 retry; post-governance product-side regeneration now lives in `tiangong flow regen-product`.
289
+
290
+ `tiangong flow publish-reviewed-data` is the CLI-owned reviewed publish preparation slice for flow governance. It reads reviewed flow rows and/or reviewed process rows from local JSON or JSONL inputs, can use `--original-flow-rows-file` to skip unchanged flow rows before planning publish, supports `skip | append_only_bump | upsert_current_version`, writes `prepared-flow-rows.json`, `prepared-process-rows.json`, `flow-version-map.json`, `skipped-unchanged-flow-rows.json`, `process-flow-ref-rewrite-evidence.jsonl`, and `publish-report.json`, and preserves the historical success-list / remote-failure / sync-report files for downstream follow-up. The native CLI path now covers local process-row preparation, optional process flow-ref rewrites, and commit-time process publish through the same `@supabase/supabase-js` writer layer; no legacy fallback path remains for reviewed process rows.
291
+
292
+ `tiangong flow build-alias-map` is the CLI-owned deterministic alias-map slice for flow governance. It reads one or more old flow snapshots plus one or more new flow snapshots, optionally consumes a seed alias map, writes `alias-plan.json`, `alias-plan.jsonl`, `flow-alias-map.json`, `manual-review-queue.jsonl`, and `alias-summary.json`, and keeps the boundary local-only instead of routing through skill-local Python.
293
+
294
+ `tiangong flow scan-process-flow-refs` is the CLI-owned standalone process-reference scan slice for flow governance. It reads one local process row set plus one or more local scope/catalog flow row sets, classifies every `referenceToFlowDataSet`, optionally excludes emergy-named processes up front, and writes stable `emergy-excluded-processes.json`, `scan-summary.json`, `scan-findings.json`, and `scan-findings.jsonl` artifacts without falling back to skill-local Python.
295
+
296
+ `tiangong flow plan-process-flow-repairs` is the CLI-owned standalone deterministic repair-planning slice for flow governance. It reads one local process row set plus one or more local scope flow row sets, optionally consumes prior `scan-findings`, applies the explicit `disabled | alias-only | alias-or-unique-name` auto-patch boundary, and writes `repair-plan.json`, `repair-plan.jsonl`, `manual-review-queue.jsonl`, and `repair-summary.json`.
297
+
298
+ `tiangong flow apply-process-flow-repairs` is the CLI-owned standalone deterministic repair-apply slice for flow governance. It reuses the same local process/scope/alias/scan contract as the planning command, applies only the deterministic subset, emits per-process patch evidence under `process-patches/`, writes `patched-processes.json`, and can sync exact-version rows back into a local `process-pool-file`.
299
+
300
+ `tiangong flow regen-product` is the CLI-owned local product-side regeneration slice for flow governance. It reads one local process row set plus one or more local scope/catalog flow row sets, runs `scan -> repair plan -> optional apply -> optional validate` under one run root, writes stable `scan/`, `repair/`, `repair-apply/`, `validate/`, and `flow-regen-product-report.json` artifacts, and keeps exit code `1` reserved for validation failures after `--apply`.
301
+
302
+ `tiangong flow validate-processes` is the CLI-owned standalone validation slice for locally patched process rows after governance repair. It reads one original process snapshot, one patched process snapshot, and one or more scope flow snapshots, verifies that only `referenceToFlowDataSet` paths changed, keeps quantitative references stable, optionally runs local TIDAS validation through the CLI-owned validator assembled from the direct `@tiangong-lca/tidas-sdk` dependency, and writes `validation-report.json` plus `validation-failures.jsonl` without falling back to skill-local Python.
303
+
304
+ ## Publish and validation
305
+
306
+ `tiangong process publish-build` is the process-side local publish handoff command. It prepares the local bundle/request/intent artifacts expected by `tiangong publish run` without reintroducing Python, MCP, or legacy remote writers into the CLI.
307
+
308
+ `tiangong process batch-build` is the process-side local batch orchestration command. It keeps batch execution file-first and JSON-first, reuses the single-run `process auto-build` contract for each item, and emits one batch report instead of pushing shell loops or Python coordinators back onto the caller.
309
+
310
+ `tiangong lifecyclemodel validate-build` is the lifecyclemodel-side local validation command. It keeps validation file-first, reuses the unified `tiangong validation run` module under the hood, and writes both per-model reports and one aggregate build report without reintroducing Python or MCP into the CLI.
311
+
312
+ `tiangong lifecyclemodel publish-build` is the lifecyclemodel-side local publish handoff command. It prepares the local bundle/request/intent artifacts expected by `tiangong publish run` without reintroducing Python, MCP, or legacy remote writers into the CLI.
313
+
314
+ `tiangong lifecyclemodel publish-resulting-process` is the lifecyclemodel-side local publish handoff command. It reads a prior resulting-process run, writes `publish-bundle.json` and `publish-intent.json`, and preserves the old builder's artifact contract without reintroducing Python or MCP into the CLI.
315
+
316
+ `tiangong publish run` is the CLI-side publish contract boundary. It normalizes publish requests, ingests upstream `publish-bundle.json` inputs, writes `normalized-request.json`, `collected-inputs.json`, `relation-manifest.json`, and `publish-report.json`, and keeps commit-mode execution behind explicit executors instead of reintroducing MCP-specific logic into the CLI.
317
+
318
+ `tiangong validation run` is the CLI-side validation boundary. It standardizes local TIDAS package validation through one JSON report shape, supports `--engine auto|sdk`, and keeps the local package-validation path inside the CLI-owned validator assembled from the direct `@tiangong-lca/tidas-sdk` dependency instead of shelling out to `tidas-tools` or loading sibling-repo artifacts.
319
+
320
+ Run the built artifact directly:
321
+
322
+ ```bash
323
+ node ./bin/tiangong.js doctor
324
+ node ./bin/tiangong.js process get --id <process-id> --json
325
+ node ./bin/tiangong.js flow get --id <flow-id> --json
326
+ node ./bin/tiangong.js flow list --state-code 100 --limit 20 --json
327
+ node ./bin/tiangong.js flow publish-reviewed-data --flow-rows-file ./reviewed-flows.jsonl --out-dir ./flow-publish-reviewed --dry-run --json
328
+ node ./bin/tiangong.js flow build-alias-map --old-flow-file ./old-flows.jsonl --new-flow-file ./new-flows.jsonl --out-dir ./flow-alias-map --json
329
+ node ./bin/tiangong.js flow scan-process-flow-refs --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --out-dir ./flow-scan --json
330
+ node ./bin/tiangong.js flow plan-process-flow-repairs --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --scan-findings ./flow-scan/scan-findings.json --out-dir ./flow-repair-plan --json
331
+ node ./bin/tiangong.js flow apply-process-flow-repairs --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --scan-findings ./flow-scan/scan-findings.json --out-dir ./flow-repair-apply --json
332
+ node ./bin/tiangong.js flow regen-product --processes-file ./processes.jsonl --scope-flow-file ./flows.jsonl --out-dir ./flow-regen --apply --json
333
+ node ./bin/tiangong.js process auto-build --input ./examples/process-auto-build.request.json --json
334
+ node ./bin/tiangong.js process resume-build --run-id <run-id> --json
335
+ node ./bin/tiangong.js process publish-build --run-id <run-id> --json
336
+ node ./bin/tiangong.js process batch-build --input ./examples/process-batch-build.request.json --json
337
+ node ./bin/tiangong.js lifecyclemodel auto-build --input ./examples/lifecyclemodel-auto-build.request.json --json
338
+ node ./bin/tiangong.js lifecyclemodel validate-build --run-dir ./artifacts/lifecyclemodel_auto_build/<run_id> --json
339
+ node ./bin/tiangong.js lifecyclemodel publish-build --run-dir ./artifacts/lifecyclemodel_auto_build/<run_id> --json
340
+ node ./bin/tiangong.js lifecyclemodel orchestrate plan --input ./lifecyclemodel-orchestrate.request.json --out-dir ./artifacts/lifecyclemodel_recursive/<run_id> --json
341
+ node ./bin/tiangong.js flow publish-version --input-file ./ready-flows.jsonl --out-dir ./flow-publish --dry-run --json
342
+ node ./bin/tiangong.js publish run --input ./examples/publish-run.request.json --dry-run --json
343
+ node ./bin/tiangong.js validation run --input-dir ./package --engine auto --json
344
+ node ./dist/src/main.js doctor --json
345
+ ```
346
+
347
+ ## Examples
348
+
349
+ Minimal example requests are available under `examples/`:
350
+
351
+ - `examples/process-auto-build.request.json`
352
+ - `examples/process-batch-build.request.json`
353
+ - `examples/lifecyclemodel-auto-build.request.json`
354
+ - `examples/publish-run.request.json`
355
+
356
+ ## Workspace usage
357
+
358
+ `tiangong-lca-skills` should stay as thin Node `.mjs` wrappers over this CLI instead of keeping parallel runtimes. The current contract is:
359
+
360
+ - skills call `tiangong` as the stable entrypoint
361
+ - deleted Python, MCP, and OpenClaw runtimes are not supported compatibility paths
362
+ - new capability must land as a native `tiangong <noun> <verb>` command before a skill depends on it
363
+
364
+ ## Docs
365
+
366
+ - Chinese setup guide: [DEV_CN.md](./DEV_CN.md)
367
+ - Detailed implementation guide: [docs/IMPLEMENTATION_GUIDE_CN.md](./docs/IMPLEMENTATION_GUIDE_CN.md)
368
+ - Skills migration checklist: [docs/SKILLS_TO_CLI_MIGRATION_CHECKLIST_CN.md](./docs/SKILLS_TO_CLI_MIGRATION_CHECKLIST_CN.md)
@@ -0,0 +1 @@
1
+ export function runFromBin(argv?: string[], env?: NodeJS.ProcessEnv): Promise<number>;
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath, pathToFileURL } from 'node:url';
7
+
8
+ const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const entryPath = path.join(rootDir, 'dist', 'src', 'main.js');
10
+
11
+ export async function runFromBin(argv = process.argv.slice(2), env = process.env) {
12
+ if (!existsSync(entryPath)) {
13
+ throw new Error(
14
+ "Missing built CLI artifacts at 'dist/src/main.js'. Run 'npm run build' or reinstall dependencies to regenerate dist.",
15
+ );
16
+ }
17
+
18
+ const entryUrl = pathToFileURL(entryPath).href;
19
+ const { main } = await import(entryUrl);
20
+ return main(argv, env);
21
+ }
22
+
23
+ const invokedUrl = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : null;
24
+
25
+ if (invokedUrl && import.meta.url === invokedUrl) {
26
+ try {
27
+ process.exitCode = await runFromBin();
28
+ } catch (error) {
29
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
30
+ process.exitCode = 1;
31
+ }
32
+ }
@@ -0,0 +1,77 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ const COVERAGE_METRIC_KEYS = ['lines', 'statements', 'functions', 'branches'];
4
+ function listTrackedSourceFiles(rootDir, relativeDir) {
5
+ const absoluteDir = path.join(rootDir, relativeDir);
6
+ const entries = readdirSync(absoluteDir, { withFileTypes: true });
7
+ const files = [];
8
+ for (const entry of entries) {
9
+ const relativePath = path.join(relativeDir, entry.name);
10
+ if (entry.isDirectory()) {
11
+ files.push(...listTrackedSourceFiles(rootDir, relativePath));
12
+ continue;
13
+ }
14
+ if (relativePath.endsWith('.d.ts')) {
15
+ continue;
16
+ }
17
+ if (relativePath.endsWith('.ts') || relativePath.endsWith('.js')) {
18
+ files.push(path.join(rootDir, relativePath));
19
+ }
20
+ }
21
+ return files;
22
+ }
23
+ function isCoverageMetric(value) {
24
+ return Boolean(value &&
25
+ typeof value === 'object' &&
26
+ typeof value.total === 'number' &&
27
+ typeof value.covered === 'number' &&
28
+ typeof value.skipped === 'number' &&
29
+ typeof value.pct === 'number');
30
+ }
31
+ function isCoverageFileSummary(value) {
32
+ if (!value || typeof value !== 'object') {
33
+ return false;
34
+ }
35
+ return COVERAGE_METRIC_KEYS.every((key) => isCoverageMetric(value[key]));
36
+ }
37
+ function aggregateCoverage(entries) {
38
+ return Object.fromEntries(COVERAGE_METRIC_KEYS.map((key) => {
39
+ const total = entries.reduce((sum, entry) => sum + entry[key].total, 0);
40
+ const covered = entries.reduce((sum, entry) => sum + entry[key].covered, 0);
41
+ const skipped = entries.reduce((sum, entry) => sum + entry[key].skipped, 0);
42
+ return [
43
+ key,
44
+ {
45
+ total,
46
+ covered,
47
+ skipped,
48
+ pct: total === 0 ? 100 : (covered / total) * 100,
49
+ },
50
+ ];
51
+ }));
52
+ }
53
+ const summaryPath = path.join(process.cwd(), 'coverage', 'coverage-summary.json');
54
+ const summary = JSON.parse(readFileSync(summaryPath, 'utf8'));
55
+ const coveredFiles = new Set(Object.keys(summary).filter((key) => key !== 'total'));
56
+ const expectedFiles = listTrackedSourceFiles(process.cwd(), 'src');
57
+ const expectedCoverageEntries = expectedFiles.map((filePath) => {
58
+ const coverageEntry = summary[filePath];
59
+ if (!isCoverageFileSummary(coverageEntry)) {
60
+ throw new Error(`Expected coverage summary to include ${filePath}, but it was missing.`);
61
+ }
62
+ return coverageEntry;
63
+ });
64
+ const aggregate = aggregateCoverage(expectedCoverageEntries);
65
+ for (const key of COVERAGE_METRIC_KEYS) {
66
+ const value = aggregate[key];
67
+ if (value.covered !== value.total || value.pct !== 100) {
68
+ throw new Error(`Expected ${key} coverage across src files to equal 100 but received ${value.pct} (${value.covered}/${value.total}).`);
69
+ }
70
+ }
71
+ for (const filePath of expectedFiles) {
72
+ if (!coveredFiles.has(filePath)) {
73
+ throw new Error(`Expected coverage summary to include ${filePath}, but it was missing.`);
74
+ }
75
+ }
76
+ process.stdout.write('Coverage assertion passed: 100% on lines, statements, functions, and branches across src files.\n');
77
+ //# sourceMappingURL=assert-full-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert-full-coverage.js","sourceRoot":"","sources":["../../scripts/assert-full-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAiB7B,MAAM,oBAAoB,GAAwB,CAAC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;AAEnG,SAAS,sBAAsB,CAAC,OAAe,EAAE,WAAmB;IAClE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;YAC7D,SAAS;QACX,CAAC;QACD,IAAI,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,SAAS;QACX,CAAC;QACD,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,OAAO,OAAO,CACZ,KAAK;QACL,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAQ,KAAwB,CAAC,KAAK,KAAK,QAAQ;QACnD,OAAQ,KAAwB,CAAC,OAAO,KAAK,QAAQ;QACrD,OAAQ,KAAwB,CAAC,OAAO,KAAK,QAAQ;QACrD,OAAQ,KAAwB,CAAC,GAAG,KAAK,QAAQ,CAClD,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IAC3C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAE,KAA6B,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC;AAED,SAAS,iBAAiB,CAAC,OAA8B;IACvD,OAAO,MAAM,CAAC,WAAW,CACvB,oBAAoB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5E,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5E,OAAO;YACL,GAAG;YACH;gBACE,KAAK;gBACL,OAAO;gBACP,OAAO;gBACP,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG;aACjD;SACF,CAAC;IACJ,CAAC,CAAC,CACoB,CAAC;AAC3B,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,uBAAuB,CAAC,CAAC;AAClF,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAoB,CAAC;AACjF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC;AACpF,MAAM,aAAa,GAAG,sBAAsB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;AACnE,MAAM,uBAAuB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;IAC7D,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAExC,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,uBAAuB,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC,CAAC;AACH,MAAM,SAAS,GAAG,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;AAE7D,KAAK,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,YAAY,GAAG,wDAAwD,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,IAAI,CACtH,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,uBAAuB,CAAC,CAAC;IAC3F,CAAC;AACH,CAAC;AAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mGAAmG,CACpG,CAAC","sourcesContent":["import { readdirSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\n\ntype CoverageMetric = {\n total: number;\n covered: number;\n skipped: number;\n pct: number;\n};\n\ntype CoverageMetricKey = 'lines' | 'statements' | 'functions' | 'branches';\n\ntype CoverageFileSummary = Record<CoverageMetricKey, CoverageMetric>;\n\ntype CoverageSummary = {\n total: CoverageFileSummary;\n} & Record<string, unknown>;\n\nconst COVERAGE_METRIC_KEYS: CoverageMetricKey[] = ['lines', 'statements', 'functions', 'branches'];\n\nfunction listTrackedSourceFiles(rootDir: string, relativeDir: string): string[] {\n const absoluteDir = path.join(rootDir, relativeDir);\n const entries = readdirSync(absoluteDir, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n const relativePath = path.join(relativeDir, entry.name);\n if (entry.isDirectory()) {\n files.push(...listTrackedSourceFiles(rootDir, relativePath));\n continue;\n }\n if (relativePath.endsWith('.d.ts')) {\n continue;\n }\n if (relativePath.endsWith('.ts') || relativePath.endsWith('.js')) {\n files.push(path.join(rootDir, relativePath));\n }\n }\n\n return files;\n}\n\nfunction isCoverageMetric(value: unknown): value is CoverageMetric {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as CoverageMetric).total === 'number' &&\n typeof (value as CoverageMetric).covered === 'number' &&\n typeof (value as CoverageMetric).skipped === 'number' &&\n typeof (value as CoverageMetric).pct === 'number',\n );\n}\n\nfunction isCoverageFileSummary(value: unknown): value is CoverageFileSummary {\n if (!value || typeof value !== 'object') {\n return false;\n }\n\n return COVERAGE_METRIC_KEYS.every((key) => isCoverageMetric((value as CoverageFileSummary)[key]));\n}\n\nfunction aggregateCoverage(entries: CoverageFileSummary[]): CoverageFileSummary {\n return Object.fromEntries(\n COVERAGE_METRIC_KEYS.map((key) => {\n const total = entries.reduce((sum, entry) => sum + entry[key].total, 0);\n const covered = entries.reduce((sum, entry) => sum + entry[key].covered, 0);\n const skipped = entries.reduce((sum, entry) => sum + entry[key].skipped, 0);\n return [\n key,\n {\n total,\n covered,\n skipped,\n pct: total === 0 ? 100 : (covered / total) * 100,\n },\n ];\n }),\n ) as CoverageFileSummary;\n}\n\nconst summaryPath = path.join(process.cwd(), 'coverage', 'coverage-summary.json');\nconst summary = JSON.parse(readFileSync(summaryPath, 'utf8')) as CoverageSummary;\nconst coveredFiles = new Set(Object.keys(summary).filter((key) => key !== 'total'));\nconst expectedFiles = listTrackedSourceFiles(process.cwd(), 'src');\nconst expectedCoverageEntries = expectedFiles.map((filePath) => {\n const coverageEntry = summary[filePath];\n\n if (!isCoverageFileSummary(coverageEntry)) {\n throw new Error(`Expected coverage summary to include ${filePath}, but it was missing.`);\n }\n\n return coverageEntry;\n});\nconst aggregate = aggregateCoverage(expectedCoverageEntries);\n\nfor (const key of COVERAGE_METRIC_KEYS) {\n const value = aggregate[key];\n if (value.covered !== value.total || value.pct !== 100) {\n throw new Error(\n `Expected ${key} coverage across src files to equal 100 but received ${value.pct} (${value.covered}/${value.total}).`,\n );\n }\n}\n\nfor (const filePath of expectedFiles) {\n if (!coveredFiles.has(filePath)) {\n throw new Error(`Expected coverage summary to include ${filePath}, but it was missing.`);\n }\n}\n\nprocess.stdout.write(\n 'Coverage assertion passed: 100% on lines, statements, functions, and branches across src files.\\n',\n);\n"]}