@lifeaitools/rdc-skills 0.37.7 → 0.37.9
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.
- package/.claude-plugin/plugin.json +73 -1
- package/README.md +1 -0
- package/clauth-plugin.json +1 -1
- package/commands/flow.md +16 -15
- package/commands/mode.md +6 -0
- package/data/rpms-kinds.snapshot.json +2485 -0
- package/git-sha.json +1 -1
- package/package.json +2 -2
- package/scripts/gen-rpms-kind-tables.mjs +210 -0
- package/scripts/lib/plugins/static-call-reference.mjs +109 -0
- package/scripts/lib/plugins/treesitter.mjs +13 -3
- package/skills/co-develop/SKILL.md +5 -0
- package/skills/collab/COLLABORATOR.md +9 -1
- package/skills/collab/SKILL.md +25 -1
- package/skills/new/SKILL.md +154 -0
- package/skills/open/SKILL.md +50 -11
- package/skills/regen-media/SKILL.md +122 -17
- package/tests/lib/jsx-references.test.mjs +113 -0
package/skills/open/SKILL.md
CHANGED
|
@@ -56,9 +56,14 @@ That is a **scoped grant**, not a readout: `permittedActions` and
|
|
|
56
56
|
to "which checkout do I work in" — it already knows pooled repos want a lane.
|
|
57
57
|
|
|
58
58
|
**Unresolved is a result, not an error.** `open regen-root` returns
|
|
59
|
-
`weDoNotHold: ["product_class"]`; `open fsm` returns `unknown_slug`.
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
`weDoNotHold: ["product_class"]`; `open fsm` returns `unknown_slug`. An
|
|
60
|
+
`unknown_slug` — nothing registered at all — means `rdc:new` next: it
|
|
61
|
+
scaffolds and registers a product of the declared class, then hands back to
|
|
62
|
+
this skill to confirm. A `weDoNotHold` entry on an otherwise-known slug is
|
|
63
|
+
narrower — `rdc-harness new <slug> --environment ...` (also wrapped by
|
|
64
|
+
`rdc:new`) fills in just the missing piece. Either way: register the target,
|
|
65
|
+
then open it again — never guess a default and never fall back to hand-rolled
|
|
66
|
+
git.
|
|
62
67
|
|
|
63
68
|
**Two targets at once is two calls.** Each returns its own `bindingId` and its
|
|
64
69
|
own `sourceBoundary`; nothing is exclusive. Working `clauth` and `rdc-harness`
|
|
@@ -156,14 +161,46 @@ Downstream verbs re-resolve from the registry by slug.
|
|
|
156
161
|
2. **Target.** Read `productClass`, `workModel`, `delivery` and `weDoNotHold`
|
|
157
162
|
from the same result. `monorepo_path` NULL means a standalone repo, not in
|
|
158
163
|
this tree. Do not re-query the registry for facts the handle already carries.
|
|
159
|
-
3. **Harness.** Name the shape the target's class implies
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
3. **Harness.** Name the shape the target's class implies. The routes come
|
|
165
|
+
from the RPMS kind registry (`rpms_kinds` → `rpms_usages`); this table is
|
|
166
|
+
**generated from `rpms_kinds`** and never hand-edited. A new kind is one
|
|
167
|
+
row via `register_rpms_kind`, then (in rdc-skills)
|
|
168
|
+
`node scripts/gen-rpms-kind-tables.mjs --from <dump.json>`. Full guide to
|
|
169
|
+
every level, not just product classes: `docs/architecture/KINDS.md` in
|
|
170
|
+
regen-root.
|
|
171
|
+
|
|
172
|
+
<!-- BEGIN GENERATED: rpms-kinds -->
|
|
173
|
+
_Generated from `rpms_kinds` + `rpms_usages` (schema `rpms.kinds.v1`) by `scripts/gen-rpms-kind-tables.mjs` — do not hand-edit. Product-level kinds only; every other level is in `select rpms_kinds_snapshot()`._
|
|
174
|
+
|
|
175
|
+
| kind | home | build · dev (`ship_route`) | runtime | prod (`deploy_route`) |
|
|
176
|
+
|---|---|---|---|---|
|
|
177
|
+
| `app` | `apps/<slug>/` | `pm2-development` | `next` | `declared-promotion` |
|
|
178
|
+
| `artifact` | `artifacts/<slug>/` | `artifact-export` | — | — (no-production-target) |
|
|
179
|
+
| `bpmn-process` | `docs/systems/<system>/bpmn/` | `flowable-dev-release` | `java` | `flowable-release` |
|
|
180
|
+
| `city-tool` | `apps/<slug>/` | `pm2-development` | `next` | `declared-promotion` |
|
|
181
|
+
| `corpus` | `$CORPUS_ROOT/<CO>/docs/` | `content-write` | — | — (content-not-code) |
|
|
182
|
+
| `dmn-decision` _(proposed)_ | `docs/systems/<system>/dmn/` | `dmn-test-cases` | `java` | `flowable-dmn-release` |
|
|
183
|
+
| `documentation` | `docs/<slug>/` | `artifact-export` | — | — (no-production-target) |
|
|
184
|
+
| `dsl` _(proposed)_ | `packages/<pkg>/src/dsl/` | `grammar-suite` | — | — (production-is-registry) |
|
|
185
|
+
| `education-piece` | `artifacts/<slug>/` | `artifact-export` | — | — (no-production-target) |
|
|
186
|
+
| `grammar` | `packages/<pkg>/src/grammar/` | `grammar-suite` | — | — (production-is-registry) |
|
|
187
|
+
| `mcp` | `mcp-servers/<slug>/` | `plugin-registration` | `ts-server` | `declared-service-route` |
|
|
188
|
+
| `model` | `models/<slug>/` | `pm2-development` | `next` | `declared-promotion` |
|
|
189
|
+
| `ontology` | `packages/planetary-ontology/src/` | `grammar-suite` | — | — (production-is-registry) |
|
|
190
|
+
| `package` | `packages/<slug>/` | `registry-release` | — | — (production-is-registry) |
|
|
191
|
+
| `prototype` | `artifacts/<slug>/` | — | — | — (no-production-target) |
|
|
192
|
+
| `share-app` | `apps/<slug>/` | `pm2-development` | `next` | `declared-promotion` |
|
|
193
|
+
| `site-html` | `sites/<slug>/` | `static-development` | `static-html` | `declared-promotion` |
|
|
194
|
+
| `site-ts` | `sites/<slug>/` | `static-development` | `static-html` | `declared-promotion` |
|
|
195
|
+
| `skill` | `skills/<slug>/` | `local-install` | — | `release-tag` |
|
|
196
|
+
| `theme` | — | `content-write` | — | — (content-not-code) |
|
|
197
|
+
| `vendor` | `vendor/<slug>/` | `registry-release` | — | — (production-is-registry) |
|
|
198
|
+
| `worker` | `workers/<slug>/` | `edge-dev` | — | `edge-deploy` |
|
|
199
|
+
<!-- END GENERATED -->
|
|
200
|
+
|
|
201
|
+
The concrete build command is the target's own (`pnpm --filter <pkg>
|
|
202
|
+
build` in a monorepo, its own tooling standalone); the route names what
|
|
203
|
+
kind of ship and deploy it owes.
|
|
167
204
|
|
|
168
205
|
4. **Visual-test contract (for an app/site with UI work).** Read the target
|
|
169
206
|
`package.json` and name its `test:ui` command and Playwright config. A
|
|
@@ -216,6 +253,8 @@ Downstream verbs re-resolve from the registry by slug.
|
|
|
216
253
|
|
|
217
254
|
## Related
|
|
218
255
|
|
|
256
|
+
- `rdc:new` — an `unknown_slug` or a `weDoNotHold` gap routes here: scaffold and
|
|
257
|
+
register a product, or just its deployment environment, then re-open.
|
|
219
258
|
- `guides/work-contract.md` — the contract this skill opens: rows, tiers, proving,
|
|
220
259
|
staleness, and what Stop checks.
|
|
221
260
|
- `rdc:flow` — declares what KIND of work this is. `rdc-work start --type` sets it
|
|
@@ -36,38 +36,143 @@ codex login status
|
|
|
36
36
|
codex features list
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
### Parameters are not interchangeable
|
|
40
|
+
|
|
41
|
+
Checked 2026-09-26 against official documentation, installed Codex 0.153.4,
|
|
42
|
+
the bundled imagegen CLI, and regen-media's `generateLocalSchema`.
|
|
43
|
+
|
|
44
|
+
| Input | Keyless Codex / `generate_local` | Bundled API script |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| Description | `prompt`; describe the requested change for edits | `--prompt` or `--prompt-file` |
|
|
47
|
+
| Style, tones, lighting | Named sections inside `prompt` | `--style`, `--palette`, `--lighting`; these augment the prompt |
|
|
48
|
+
| Corpus information | Read selected authorized sources first; include relevant facts and source references in the brief | Same; `--prompt-file` can contain the assembled brief. No automatic corpus retrieval flag |
|
|
49
|
+
| Image to update | `mode: "edit"`, `source_image`; direct Codex uses `-i` | `edit --image <path>`; repeat for additional references |
|
|
50
|
+
| Size | `size` currently becomes a prompt request, NOT a guaranteed native tool parameter | `--size 3840x2160` is sent as an API parameter |
|
|
51
|
+
| Quality / output format | Only pass native fields if the current built-in tool schema exposes them; do not invent flags | `--quality high --output-format png` |
|
|
52
|
+
| Output location | `generate_local` has `out_name`, not `output_dir`; direct Codex copies its generated artifact to the requested destination | `--out <file>` or `--out-dir <directory>` |
|
|
53
|
+
| Model | `model` / `-m` selects the Codex reasoning model, not an image-model parameter | `--model gpt-image-2` selects the image model |
|
|
54
|
+
|
|
55
|
+
The CLI's `--enable image_generation`, `-m`, `-i`, and `--cd` enable the tool,
|
|
56
|
+
select the reasoning model, attach input images, and choose the workspace.
|
|
57
|
+
They do not establish output dimensions. `--output-last-message` saves text,
|
|
58
|
+
not an image. Do not invent `codex exec --size`, `--quality`, or a TOML image-size
|
|
59
|
+
setting: use only fields exposed by the installed command/tool schema.
|
|
60
|
+
|
|
61
|
+
The former four-step recipe was not a size guarantee. On 2026-09-26, two local
|
|
62
|
+
media calls and a direct `gpt-5.5` call with those exact instructions requested
|
|
63
|
+
3840x2160 but produced 1672x941. The available built-in schema exposed a prompt
|
|
64
|
+
and reference images, not a native `size` field. Treat this as observed behavior
|
|
65
|
+
of that environment, not a permanent limit of all Codex versions.
|
|
66
|
+
|
|
67
|
+
Retain the user's requested dimensions in the brief, inspect the current tool
|
|
68
|
+
schema, then measure the saved output. If a hard size requirement is missed,
|
|
69
|
+
report it as unmet; do not repeat identical requests, silently resample, or
|
|
70
|
+
silently switch to a paid API. A re-render can change composition and is not a
|
|
71
|
+
lossless upscale.
|
|
45
72
|
|
|
46
73
|
Text to image:
|
|
47
74
|
|
|
48
75
|
```bash
|
|
49
|
-
echo
|
|
76
|
+
echo 'Use $imagegen and the built-in image_gen tool directly. Generate the described image, requested size 3840x2160. No API or MCP fallback. Use native size only if exposed by the tool schema. Copy the full-resolution output from this run to .rdc/tmp/imagegen/hero-v2.png. Measure and report actual dimensions; report any size mismatch without resizing. Brief: <description, style, palette, lighting, composition, constraints>.' | codex exec --enable image_generation --cd "$PWD" -m gpt-5.5 -
|
|
50
77
|
```
|
|
51
78
|
|
|
52
79
|
Image edit, re-render, or upscale:
|
|
53
80
|
|
|
54
81
|
```bash
|
|
55
|
-
echo
|
|
82
|
+
echo 'Use $imagegen and the built-in image_gen tool directly to edit the attached image. Change: <requested change>. Preserve: <invariants>. Request 3840x2160; do not claim that size unless measured. No API or MCP fallback. Copy the output to .rdc/tmp/imagegen/hero-v2.png; report path and actual dimensions, without resizing.' | codex exec -i "input/source.png" --enable image_generation --cd "$PWD" -m gpt-5.5 -
|
|
56
83
|
```
|
|
57
84
|
|
|
58
85
|
Notes:
|
|
59
86
|
|
|
60
|
-
-
|
|
61
|
-
-
|
|
87
|
+
- These examples retain configured approval and sandbox settings. Do not disable safeguards merely to generate an image. `gpt-5.5` is an example previously used locally, not a documented requirement for all Codex versions.
|
|
88
|
+
- The keyless path uses the local ChatGPT login, not `OPENAI_API_KEY`, and consumes included Codex usage limits.
|
|
89
|
+
- Built-in output lands under `$CODEX_HOME/generated_images/`; copy the specific output belonging to this run. Never recover the newest image across unrelated sessions.
|
|
62
90
|
- Use a long timeout or background session; generation commonly takes 2-5 minutes.
|
|
63
|
-
-
|
|
91
|
+
- API-supported sizes do not prove that Codex exposes matching controls.
|
|
92
|
+
|
|
93
|
+
## Calling regen-media-local
|
|
94
|
+
|
|
95
|
+
Use its MCP `generate_local` when the caller requested that service. Discover
|
|
96
|
+
its current tool schema rather than assuming local and remote servers expose
|
|
97
|
+
the same tools. The inspected implementation accepts this shape:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"prompt": "Purpose: RDC masthead. Change: replace the boy with a community square. Style: cinematic landscape. Palette: warm amber and deep forest green. Composition: elevated valley view, distant mountains, quiet upper-left copy area. Preserve: river and overall lighting. Context: selected authorized place facts from the brief; do not invent measured outcomes.",
|
|
102
|
+
"mode": "edit",
|
|
103
|
+
"source_image": "C:/path/to/original.png",
|
|
104
|
+
"size": "3840x2160",
|
|
105
|
+
"out_name": "rdc-masthead-town-square-v2",
|
|
106
|
+
"model": "gpt-5.5",
|
|
107
|
+
"auto_upload": false
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This is the tool's argument object, not a shell command. For a fresh image use
|
|
112
|
+
`mode: "text_to_image"` and omit `source_image`. The current service resolves
|
|
113
|
+
output beneath `<CODEX_CD>/output/imagegen/<prefix>/<out_name>.png`; it does not
|
|
114
|
+
accept a per-call output directory. Do not silently change shared `CODEX_CD`.
|
|
115
|
+
Copy a selected result to the caller's authorized project destination afterward.
|
|
116
|
+
Its returned `size` is the request; `width` and `height` are measurements. A
|
|
117
|
+
success message alone does not prove exact size. Keep `auto_upload: false` until
|
|
118
|
+
size and visual checks pass. Do not label a mismatched file as native 4K.
|
|
119
|
+
|
|
120
|
+
## Explicit parameterized API script
|
|
121
|
+
|
|
122
|
+
Codex's installed imagegen skill includes `scripts/image_gen.py`. This is a real
|
|
123
|
+
script accepting a parameter set and an output directory, but it is a separate,
|
|
124
|
+
credentialed API path, not the keyless Codex image tool or `generate_local`.
|
|
125
|
+
Use only after the user chooses that path. Obtain credentials through the
|
|
126
|
+
environment's approved vault mechanism; never ask for keys in chat. A request
|
|
127
|
+
to research parameters or update this skill is not permission for a paid run.
|
|
128
|
+
|
|
129
|
+
Read the installed imagegen CLI reference and `--help` before using it. Example
|
|
130
|
+
PowerShell call, with `--dry-run` so no key, network or generation is needed:
|
|
131
|
+
|
|
132
|
+
```powershell
|
|
133
|
+
$imageGenScript = Join-Path $env:USERPROFILE '.codex/skills/.system/imagegen/scripts/image_gen.py'
|
|
134
|
+
python $imageGenScript edit `
|
|
135
|
+
--model gpt-image-2 `
|
|
136
|
+
--image 'C:/path/to/original.png' `
|
|
137
|
+
--prompt-file 'C:/path/to/approved-brief.txt' `
|
|
138
|
+
--style 'cinematic landscape' `
|
|
139
|
+
--palette 'warm amber, deep forest green, blue mountains' `
|
|
140
|
+
--lighting 'sunset with warm community lights' `
|
|
141
|
+
--composition 'elevated valley, distant mountains, quiet upper-left copy area' `
|
|
142
|
+
--constraints 'preserve river; no boy, text or network lines' `
|
|
143
|
+
--size 3840x2160 --quality high --output-format png `
|
|
144
|
+
--out-dir 'C:/path/to/approved-output-directory' `
|
|
145
|
+
--dry-run
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
For generation, replace `edit` with `generate` and omit `--image`. For a stable
|
|
149
|
+
filename use `--out` instead of `--out-dir`. Respect `CODEX_HOME` if configured;
|
|
150
|
+
the example uses the default Windows installation path. Do not modify the
|
|
151
|
+
bundled script or build another wrapper for parameters it already supports.
|
|
152
|
+
|
|
153
|
+
Corpus content is not an API parameter. Read only the requested sources, select
|
|
154
|
+
relevant authorized facts, retain source references alongside the brief, and
|
|
155
|
+
state which are conceptual rather than observed. Never bulk-send a corpus or
|
|
156
|
+
claim that mentioning a file path makes the generator read its contents.
|
|
157
|
+
|
|
158
|
+
For `gpt-image-2`, size is `auto` or constrained `WIDTHxHEIGHT`: edges divisible
|
|
159
|
+
by 16, maximum edge 3840, ratio at most 3:1, and 655360–8294400 total pixels.
|
|
160
|
+
Above 2560x1440 is experimental. API output still requires dimension and visual
|
|
161
|
+
checks. Style/palette/lighting are prompt augmentation, not separate model
|
|
162
|
+
rendering controls. `input_fidelity` is not supported for `gpt-image-2`.
|
|
163
|
+
|
|
164
|
+
Sources (checked 2026-09-26):
|
|
165
|
+
- [Codex image generation and reference-image guidance](https://learn.chatgpt.com/docs/image-generation)
|
|
166
|
+
- [Codex CLI flags](https://developers.openai.com/codex/cli/reference)
|
|
167
|
+
- [Responses image tool parameters](https://developers.openai.com/api/docs/guides/tools-image-generation)
|
|
168
|
+
- [Image API and resolution constraints](https://developers.openai.com/api/docs/guides/image-generation)
|
|
64
169
|
|
|
65
170
|
## Upload to Regen Media
|
|
66
171
|
|
|
67
172
|
After generation, upload finished images through the project uploader when available:
|
|
68
173
|
|
|
69
174
|
```powershell
|
|
70
|
-
powershell.exe -ExecutionPolicy Bypass -File "{PROJECT_ROOT}/scripts/upload-to-media.ps1" "
|
|
175
|
+
powershell.exe -ExecutionPolicy Bypass -File "{PROJECT_ROOT}/scripts/upload-to-media.ps1" ".rdc/tmp/imagegen/<name>.png"
|
|
71
176
|
```
|
|
72
177
|
|
|
73
178
|
The uploader obtains `regen-media-api` from clauth and posts to Regen Media. Never print credential values. If upload is not required, return the local artifact path and dimensions.
|
|
@@ -81,15 +186,15 @@ Use the deployed regen-media MCP/API generation path only when the local Codex i
|
|
|
81
186
|
1. Classify the task: `generate`, `edit`, `upscale`, or `upload`.
|
|
82
187
|
2. If `RDC_TEST=1`, print the selected path and planned commands without executing external calls.
|
|
83
188
|
3. For generation/edit/upscale, attempt the local keyless Codex preflight.
|
|
84
|
-
4.
|
|
85
|
-
5. Save to
|
|
86
|
-
6. Verify
|
|
189
|
+
4. Use the requested service, keyless Codex command, or explicitly chosen API script; distinguish prompt fields from supported native parameters.
|
|
190
|
+
5. Save/copy non-destructively to the caller-provided path, or `.rdc/tmp/imagegen/` for Regen Root scratch. Preserve the original.
|
|
191
|
+
6. Verify this run's file exists, measure actual pixel dimensions, compare with requested size, and inspect composition. Stop and report unmet constraints on mismatch.
|
|
87
192
|
7. If the artifact should be managed, upload it to Regen Media and return the media URL/slug plus local path.
|
|
88
|
-
8. If local Codex is unavailable,
|
|
193
|
+
8. If local Codex is unavailable or cannot meet a hard requirement, explain the credentialed fallback and obtain the user's choice before invoking it.
|
|
89
194
|
|
|
90
195
|
## Boundaries
|
|
91
196
|
|
|
92
197
|
- Do not write or expose API keys.
|
|
93
198
|
- Do not silently downgrade from local keyless `gpt-image-2` to server-side MCP/API generation.
|
|
94
|
-
- Do not
|
|
199
|
+
- Do not present prompt-only size requests as native parameters or guarantee dimensions from CLI flags alone.
|
|
95
200
|
- Do not run generation or upload in test mode.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { treesitterPlugin } from '../../scripts/lib/plugins/treesitter.mjs';
|
|
4
|
+
import { srp } from '../../scripts/lib/solid-scoring.mjs';
|
|
5
|
+
|
|
6
|
+
let fixture = 0;
|
|
7
|
+
function inspect(body, prefix = 'const registry = { a: A };') {
|
|
8
|
+
const [unit] = treesitterPlugin.extractUnits(`/jsx-reference-${fixture++}.tsx`,
|
|
9
|
+
`function A() { return null; } ${prefix} function B() { ${body} }`);
|
|
10
|
+
return { calls: unit.members.find(m => m.name === 'B').calls, score: srp(unit).score };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test('direct call and opening/self-closing JSX have the same local dependency', () => {
|
|
14
|
+
for (const body of ['return A();', 'return <A/>;', 'return <A></A>;']) {
|
|
15
|
+
const result = inspect(body);
|
|
16
|
+
assert.ok(result.calls.includes('A'));
|
|
17
|
+
assert.equal(result.score, 100);
|
|
18
|
+
assert.equal(result.calls.filter(c => c === 'A').length, 1);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('member JSX and const aliases resolve a statically selected registry entry', () => {
|
|
23
|
+
for (const body of ['return <registry.a/>;', 'const C = registry.a; return <C/>;',
|
|
24
|
+
'const C = registry["a"]; return C();', 'const C = registry.a; const D = C; return <D/>;']) {
|
|
25
|
+
assert.ok(inspect(body).calls.includes('A'), body);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('external member JSX retains receiver; intrinsic tags are not component calls', () => {
|
|
30
|
+
const { calls } = inspect('return <><UI.A/><a/><div/></>;', '');
|
|
31
|
+
assert.deepEqual(calls, ['UI.A']);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('dynamic lookup, spread, reassignment, parameter shadow and cycles stay unresolved', () => {
|
|
35
|
+
const cases = [
|
|
36
|
+
['const C=registry[key]; return <C/>;', 'const registry=getRegistry();'],
|
|
37
|
+
['const C=registry.a; return <C/>;', 'const registry={a:A,...other};'],
|
|
38
|
+
['registry.a=other; const C=registry.a; return <C/>;', 'const registry={a:A};'],
|
|
39
|
+
['const C=registry.a; return <C/>;', 'let registry={a:A};'],
|
|
40
|
+
['const C=registry.a; return <C/>;', 'const registry={a:A,[key]:other};'],
|
|
41
|
+
['const C=registry.a; return <C/>;', 'const registry={a:A,a:other};'],
|
|
42
|
+
['consume(registry); const C=registry.a; return <C/>;', 'const registry={a:A};'],
|
|
43
|
+
['const C=D; const D=C; return <C/>;', ''],
|
|
44
|
+
['function nested(registry){ const C=registry.a; return <C/>; } return nested({});', 'const registry={a:A};'],
|
|
45
|
+
['function nested(A){ const registry={A}; const C=registry.A; return <C/>; } return nested(other);', ''],
|
|
46
|
+
['const {registry}=other; return <registry.a/>;', 'const registry={a:A};'],
|
|
47
|
+
['consume({registry}); return <registry.a/>;', 'const registry={a:A};'],
|
|
48
|
+
['(registry.a)=Other; return <registry.a/>;', 'const registry={a:A};'],
|
|
49
|
+
['[registry.a]=[Other]; return <registry.a/>;', 'const registry={a:A};'],
|
|
50
|
+
['for(const A of others){return <A/>;}', ''],
|
|
51
|
+
['function A(){return null;} return <A/>;', ''],
|
|
52
|
+
['const f=function A(){return <A/>;}; return f();', ''],
|
|
53
|
+
['const f=class A { render(){return <A/>;} }; return f;', ''],
|
|
54
|
+
];
|
|
55
|
+
for (const [body, prefix] of cases) assert.ok(!inspect(body, prefix).calls.includes('A'), body);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('unused registry values do not create call edges', () => {
|
|
59
|
+
assert.equal(inspect('return null;').score, 70);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('const assertions and shorthand registry entries retain the selected reference', () => {
|
|
63
|
+
assert.ok(inspect('return <registry.A/>;', 'const registry={A} as const;').calls.includes('A'));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('JSX parameter shadows do not connect an unrelated module function', () => {
|
|
67
|
+
const [unit] = treesitterPlugin.extractUnits('/jsx-param-shadow.tsx', 'function A(){return null;} function B(A){return <A/>;}');
|
|
68
|
+
assert.deepEqual(unit.members.find(member => member.name === 'B').calls, []);
|
|
69
|
+
assert.equal(srp(unit).score, 70);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('closed registry dispatch records possible call targets, as explicit branches do', () => {
|
|
73
|
+
const prefix = 'function A(){return null;} function B(){return null;} function Fallback(){return null;} const registry={a:A,b:B};';
|
|
74
|
+
for (const body of [
|
|
75
|
+
'const C=registry[key] ?? Fallback; return <C/>;',
|
|
76
|
+
'const C=registry[key] ?? Fallback; return C();',
|
|
77
|
+
]) {
|
|
78
|
+
const [unit] = treesitterPlugin.extractUnits(`/closed-dispatch-${fixture++}.tsx`, `${prefix} function Render(key){${body}}`);
|
|
79
|
+
const calls = unit.members.find(member => member.name === 'Render').calls;
|
|
80
|
+
for (const target of ['A', 'B', 'Fallback']) assert.ok(calls.includes(target), `${body}: ${target}`);
|
|
81
|
+
assert.equal(srp(unit).score, 100);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('literal selection does not connect unselected registry entries', () => {
|
|
86
|
+
const [unit] = treesitterPlugin.extractUnits('/literal-selection.tsx',
|
|
87
|
+
'function A(){return null;} function B(){return null;} const registry={a:A,b:B}; function Render(){const C=registry["a"];return <C/>;}');
|
|
88
|
+
assert.deepEqual(unit.members.find(member => member.name === 'Render').calls, ['A']);
|
|
89
|
+
assert.equal(srp(unit).score, 70);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('dynamic registry analysis preserves mutation, escape, shadow and open-object negatives', () => {
|
|
93
|
+
const negatives = [
|
|
94
|
+
['const registry=getRegistry();', ''],
|
|
95
|
+
['let registry={a:A};', ''],
|
|
96
|
+
['const registry={a:A,...other};', ''],
|
|
97
|
+
['const registry={a:A,[key]:Other};', ''],
|
|
98
|
+
['const registry={a:A,a:Other};', ''],
|
|
99
|
+
['export const registry={a:A};', ''],
|
|
100
|
+
['const registry={a:A};', 'consume({registry});'],
|
|
101
|
+
['const registry={a:A};', '(registry.a)=Other;'],
|
|
102
|
+
['const registry={a:A};', '[registry.a]=[Other];'],
|
|
103
|
+
['const registry={a:A};', 'const {registry}=other;'],
|
|
104
|
+
['A=Other; const registry={a:A};', ''],
|
|
105
|
+
['const registry={a:A};', 'for (registry.a of others) {}'],
|
|
106
|
+
['const registry={a:A};', 'for (registry.a in other) {}'],
|
|
107
|
+
];
|
|
108
|
+
for (const [prefix, before] of negatives) {
|
|
109
|
+
const result = inspect(`${before} const C=registry[key] ?? ExternalFallback; return <C/>;`, prefix);
|
|
110
|
+
assert.ok(!result.calls.includes('A'), `${prefix} ${before}`);
|
|
111
|
+
assert.ok(result.calls.includes('ExternalFallback'));
|
|
112
|
+
}
|
|
113
|
+
});
|