@chemx/starter-kit 26.10.8-344 → 26.10.10-75
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/AGENTS.md +135 -32
- package/README.md +253 -199
- package/blueprints/atoms/a-button.svelte +11 -9
- package/blueprints/atoms/a-button.vue +2 -1
- package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.svelte +8 -4
- package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.tsx +8 -4
- package/blueprints/molecule-capsule/m-chemx-badge/m-chemx-badge.vue +8 -4
- package/blueprints/molecule-capsule/m-sample-card.svelte +2 -1
- package/blueprints/molecule-capsule/m-sample-card.tsx +2 -1
- package/blueprints/molecule-capsule/m-sample-card.vue +4 -2
- package/blueprints/workflows/chemx-audit.yml +15 -46
- package/cli/agent-json.js +40 -0
- package/cli/apply-edits.js +195 -0
- package/cli/audit/ai-slop-detector.js +62 -35
- package/cli/audit/ast-passes.js +52 -0
- package/cli/audit/ast-visitors.js +142 -83
- package/cli/audit/audit-plain.js +26 -0
- package/cli/audit/audit-summary.js +16 -3
- package/cli/audit/autofix-content.js +122 -0
- package/cli/audit/autofix.js +74 -159
- package/cli/audit/budget-rules.js +84 -0
- package/cli/audit/catch-predicates.js +51 -0
- package/cli/audit/chemx-dir.js +76 -0
- package/cli/audit/clone-detector.js +4 -2
- package/cli/audit/comment-regions.js +83 -0
- package/cli/audit/coverage.js +48 -0
- package/cli/audit/csharp-analyzer.js +47 -22
- package/cli/audit/discussion-store.js +9 -4
- package/cli/audit/extended-text-patterns.js +104 -54
- package/cli/audit/extended-visitors.js +26 -12
- package/cli/audit/feed.d.ts +77 -0
- package/cli/audit/feed.js +146 -0
- package/cli/audit/gate-delta.js +91 -0
- package/cli/audit/gate-verdict.js +25 -3
- package/cli/audit/history.d.ts +14 -2
- package/cli/audit/history.js +89 -101
- package/cli/audit/hook-shape-validator.js +18 -9
- package/cli/audit/lexicon-predicates.js +125 -0
- package/cli/audit/lifecycle-predicates.js +106 -0
- package/cli/audit/line-budgets.js +154 -0
- package/cli/audit/metrics.js +56 -37
- package/cli/audit/mutable-ref-predicate.js +54 -0
- package/cli/audit/parseable-code.js +11 -0
- package/cli/audit/pattern-detector.js +56 -28
- package/cli/audit/project-figures.js +28 -0
- package/cli/audit/prompt-rule-lines.js +46 -0
- package/cli/audit/prompts.js +74 -46
- package/cli/audit/prop-surface.js +74 -0
- package/cli/audit/ratchet.js +112 -23
- package/cli/audit/render-depth.js +79 -0
- package/cli/audit/reporter-banner.js +7 -4
- package/cli/audit/reporter-grades.js +52 -34
- package/cli/audit/reporter-grouping.js +10 -5
- package/cli/audit/reporter-markdown.js +16 -8
- package/cli/audit/reporter-sections.js +31 -16
- package/cli/audit/reporter-summary.js +28 -16
- package/cli/audit/reporter-utils.js +54 -30
- package/cli/audit/roadmap.js +20 -15
- package/cli/audit/rule-overrides.js +78 -0
- package/cli/audit/rule-pillars.js +43 -0
- package/cli/audit/rule-revisions.js +42 -0
- package/cli/audit/rules-helpers.js +13 -63
- package/cli/audit/rules-predicates.d.ts +1 -0
- package/cli/audit/rules-predicates.js +84 -64
- package/cli/audit/rules-registry.js +94 -10
- package/cli/audit/rules.js +92 -171
- package/cli/audit/shallow-catch-csharp.js +49 -0
- package/cli/audit/shallow-catch-escalation.js +157 -0
- package/cli/audit/shallow-catch-reads.js +116 -0
- package/cli/audit/social-gh.js +18 -9
- package/cli/audit/social-git.js +82 -40
- package/cli/audit/social-http.js +17 -9
- package/cli/audit/social-publisher.js +14 -7
- package/cli/audit/social.js +39 -30
- package/cli/audit/staged-delta.js +67 -0
- package/cli/audit/status-file.js +67 -0
- package/cli/audit/structural-weight-evaluator.js +17 -29
- package/cli/audit/structural-weight.js +30 -13
- package/cli/audit/suppressions.js +130 -0
- package/cli/audit/template-rules.js +82 -0
- package/cli/audit/types.d.ts +24 -1
- package/cli/audit-engine.js +128 -0
- package/cli/audit-preflight-git.js +15 -1
- package/cli/audit-preflight.js +6 -4
- package/cli/audit-scan.js +180 -0
- package/cli/audit-scope.js +31 -14
- package/cli/audit.js +6 -201
- package/cli/babel-lazy.js +41 -0
- package/cli/badge.js +9 -6
- package/cli/banner.js +37 -0
- package/cli/build/child-registry.js +57 -0
- package/cli/build/executor.js +122 -48
- package/cli/build.js +75 -25
- package/cli/cli-args.js +107 -0
- package/cli/columnar.js +6 -3
- package/cli/commands/cmd-audit-each.js +164 -0
- package/cli/commands/cmd-audit-feed.js +78 -0
- package/cli/commands/cmd-audit.js +136 -57
- package/cli/commands/cmd-batch.js +60 -0
- package/cli/commands/cmd-check.js +74 -0
- package/cli/commands/cmd-project.js +18 -9
- package/cli/commands/cmd-router.js +148 -32
- package/cli/commands/cmd-show.js +56 -0
- package/cli/commands/cmd-staged-delta.js +27 -0
- package/cli/commands/cmd-wrappers-files.js +77 -0
- package/cli/commands/cmd-wrappers-git.js +122 -0
- package/cli/commands/cmd-wrappers-json.js +101 -0
- package/cli/commands/cmd-wrappers.js +7 -322
- package/cli/commands/unknown-flags.js +228 -0
- package/cli/commands-schema-badge.js +73 -0
- package/cli/commands-schema-commit.js +21 -0
- package/cli/commands-schema-edit.js +106 -0
- package/cli/commands-schema-host.js +65 -0
- package/cli/commands-schema-ops.js +127 -0
- package/cli/commands-schema-patterns.js +69 -0
- package/cli/commands-schema-report.js +18 -0
- package/cli/commands-schema-search.js +121 -0
- package/cli/commands-schema-setup.js +30 -0
- package/cli/commands-schema-verify.js +89 -0
- package/cli/commands-schema-wrappers.js +131 -0
- package/cli/commands-schema.js +26 -408
- package/cli/comment-ranges.js +108 -0
- package/cli/commit/commit-args.js +73 -0
- package/cli/commit/commit-cli.js +25 -0
- package/cli/commit/commit-facts.js +49 -0
- package/cli/commit/commit-git.js +98 -0
- package/cli/commit/commit-index.js +35 -0
- package/cli/commit/commit-message.js +52 -0
- package/cli/commit/commit-record.js +68 -0
- package/cli/commit/commit-run.js +160 -0
- package/cli/commit/commit-validate.js +62 -0
- package/cli/config/index.js +54 -8
- package/cli/config/loader.js +77 -8
- package/cli/config/profiles.js +7 -0
- package/cli/conflicts-cli.js +118 -0
- package/cli/conflicts.js +104 -0
- package/cli/create.js +11 -6
- package/cli/db-project-stamp.js +124 -0
- package/cli/docs-check/command-tree.js +27 -0
- package/cli/docs-check/extract.js +151 -0
- package/cli/docs-check/resolve.js +85 -0
- package/cli/docs-check/run.js +88 -0
- package/cli/doctor/check-env.js +43 -0
- package/cli/doctor/check-host.js +115 -0
- package/cli/doctor/check-index.js +93 -0
- package/cli/doctor/check-mcp.js +78 -0
- package/cli/doctor/doctor-cli.js +60 -0
- package/cli/doctor/kit-locate.js +40 -0
- package/cli/doctor/proc-scan.js +66 -0
- package/cli/edit-atomic.js +75 -0
- package/cli/edit-diff.js +98 -0
- package/cli/edit-guardrails.js +77 -0
- package/cli/edit-locks.js +128 -0
- package/cli/embeddings/vectorizer.js +9 -5
- package/cli/errors/catcher.js +45 -52
- package/cli/errors/formatter.js +14 -10
- package/cli/errors/policy.js +26 -0
- package/cli/errors/publisher.js +5 -1
- package/cli/errors/report-output.js +67 -0
- package/cli/errors/sanitizer.js +52 -3
- package/cli/errors/storage.js +17 -5
- package/cli/explode-importers.js +102 -0
- package/cli/explode-path-args.js +115 -0
- package/cli/explode-plan.js +153 -0
- package/cli/explode-prologue.js +43 -0
- package/cli/explode-relocate.js +73 -0
- package/cli/exploder.js +205 -193
- package/cli/forge/anchors.js +50 -0
- package/cli/forge/audit-forge.js +58 -0
- package/cli/forge/binding-walk.js +84 -0
- package/cli/forge/bindings.js +238 -0
- package/cli/forge/blueprint-cli.js +153 -0
- package/cli/forge/blueprint-context.js +69 -0
- package/cli/forge/blueprint-members.js +82 -0
- package/cli/forge/blueprint-store.js +106 -0
- package/cli/forge/blueprint-target.js +44 -0
- package/cli/forge/blueprint.js +276 -0
- package/cli/forge/body-end-cache.js +34 -0
- package/cli/forge/body-ends.js +86 -0
- package/cli/forge/canon-inline.js +342 -0
- package/cli/forge/canon-logic.js +106 -0
- package/cli/forge/canon-nodes.js +270 -0
- package/cli/forge/canon-print.js +120 -0
- package/cli/forge/canonicalize.js +49 -0
- package/cli/forge/conventions.js +51 -0
- package/cli/forge/drift.js +120 -0
- package/cli/forge/exclusions.js +21 -0
- package/cli/forge/exits.js +76 -0
- package/cli/forge/facets.js +86 -0
- package/cli/forge/file-units.js +53 -0
- package/cli/forge/fingerprint-file.js +52 -0
- package/cli/forge/fingerprint-session.js +215 -0
- package/cli/forge/fingerprint-sync.js +74 -0
- package/cli/forge/fingerprint-visitors.js +66 -0
- package/cli/forge/fixtures/heal/a7-dry-run.diff.txt +179 -0
- package/cli/forge/fixtures/heal/cli/commit/commit-facts.js.txt +49 -0
- package/cli/forge/fixtures/heal/cli/doctor/check-host.js.txt +117 -0
- package/cli/forge/fixtures/heal/cli/doctor/kit-locate.js.txt +42 -0
- package/cli/forge/fixtures/heal/cli/hooks/guard-paths.js.txt +80 -0
- package/cli/forge/fixtures/heal/cli/hooks/project-status.js.txt +62 -0
- package/cli/forge/fixtures/heal/cli/main.js.txt +169 -0
- package/cli/forge/fixtures/heal/cli/mcp/context.js.txt +81 -0
- package/cli/forge/fixtures/heal/cli/mcp/installer-report.js.txt +47 -0
- package/cli/forge/fixtures/heal/cli/path-scope.js.txt +169 -0
- package/cli/forge/fixtures/heal/cli/project-detector.js.txt +364 -0
- package/cli/forge/fixtures/heal/cli/project-markers.js.txt +42 -0
- package/cli/forge/fixtures/heal/cli/team/coordination-root.js.txt +161 -0
- package/cli/forge/fixtures/heal/cli/team/usage-reader.js.txt +109 -0
- package/cli/forge/fixtures/heal/cli/test-slots.js.txt +129 -0
- package/cli/forge/fixtures/social-gh.js.txt +131 -0
- package/cli/forge/fixtures/team-flags.js.txt +214 -0
- package/cli/forge/fold.js +199 -0
- package/cli/forge/forge-groups.js +200 -0
- package/cli/forge/forge-report.js +73 -0
- package/cli/forge/fuzz/binding-hook.js +36 -0
- package/cli/forge/fuzz/choices.js +40 -0
- package/cli/forge/fuzz/classes.js +41 -0
- package/cli/forge/fuzz/coverage.js +26 -0
- package/cli/forge/fuzz/gen-alias.js +61 -0
- package/cli/forge/fuzz/gen-control.js +73 -0
- package/cli/forge/fuzz/gen-kit.js +75 -0
- package/cli/forge/fuzz/gen-logic.js +77 -0
- package/cli/forge/fuzz/gen-mixed.js +204 -0
- package/cli/forge/fuzz/gen-modules.js +80 -0
- package/cli/forge/fuzz/gen-objects.js +92 -0
- package/cli/forge/fuzz/gen-scope.js +131 -0
- package/cli/forge/fuzz/gen-templates.js +87 -0
- package/cli/forge/fuzz/harness.js +314 -0
- package/cli/forge/fuzz/pair-check.js +115 -0
- package/cli/forge/fuzz/resolve.js +32 -0
- package/cli/forge/fuzz/run.js +121 -0
- package/cli/forge/fuzz/sandbox.js +108 -0
- package/cli/forge/fuzz/seeds.js +119 -0
- package/cli/forge/fuzz/slow-shard.js +60 -0
- package/cli/forge/fuzz/template-oracle.js +148 -0
- package/cli/forge/fuzz/transpile.js +61 -0
- package/cli/forge/gates.js +115 -0
- package/cli/forge/group-shape.js +137 -0
- package/cli/forge/group-store.js +160 -0
- package/cli/forge/group.js +280 -0
- package/cli/forge/gt-sandbox.js +83 -0
- package/cli/forge/gt-scaffold.js +110 -0
- package/cli/forge/hash-labels.js +117 -0
- package/cli/forge/hash.js +119 -0
- package/cli/forge/heal-apply.js +170 -0
- package/cli/forge/heal-cli.js +132 -0
- package/cli/forge/heal-leases.js +52 -0
- package/cli/forge/heal-locate.js +70 -0
- package/cli/forge/heal-match.js +208 -0
- package/cli/forge/heal-ops.js +197 -0
- package/cli/forge/heal-plan.js +143 -0
- package/cli/forge/heal-sandbox.js +126 -0
- package/cli/forge/heal-specs.js +80 -0
- package/cli/forge/heal-store.js +80 -0
- package/cli/forge/heal-text.js +60 -0
- package/cli/forge/heal-typecheck.js +108 -0
- package/cli/forge/heal-undo.js +74 -0
- package/cli/forge/heal-verify.js +148 -0
- package/cli/forge/hole-kinds.js +126 -0
- package/cli/forge/inline-mode.js +6 -0
- package/cli/forge/inline-on.setup.js +4 -0
- package/cli/forge/lgg-stage.js +125 -0
- package/cli/forge/lgg.js +197 -0
- package/cli/forge/library-match.js +74 -0
- package/cli/forge/murmur.js +86 -0
- package/cli/forge/naming.js +101 -0
- package/cli/forge/pattern-schema-ddl.js +157 -0
- package/cli/forge/placement.js +165 -0
- package/cli/forge/rank.js +174 -0
- package/cli/forge/refine.js +93 -0
- package/cli/forge/rejected-members.js +92 -0
- package/cli/forge/rejects.js +77 -0
- package/cli/forge/root-shapes.js +84 -0
- package/cli/forge/run-cache.js +145 -0
- package/cli/forge/sibling-blocks.js +41 -0
- package/cli/forge/siblings.js +140 -0
- package/cli/forge/store.js +180 -0
- package/cli/forge/template-normalize.js +209 -0
- package/cli/forge/template-roles.js +86 -0
- package/cli/forge/template-units.js +148 -0
- package/cli/forge/templates.js +42 -0
- package/cli/forge/unify-step.js +65 -0
- package/cli/forge/unit-floor.js +74 -0
- package/cli/forge/unit-hash.js +229 -0
- package/cli/forge/unit-trees.js +153 -0
- package/cli/forge/units.js +261 -0
- package/cli/forge/windows.js +129 -0
- package/cli/friction/friction-cli.js +71 -0
- package/cli/friction/friction-export.js +54 -0
- package/cli/friction/friction-log.js +54 -0
- package/cli/friction/usage-classify.js +56 -0
- package/cli/friction/usage-report.js +86 -0
- package/cli/friction/wrong-call.js +12 -0
- package/cli/fs-json.js +17 -0
- package/cli/generator-compact.js +55 -0
- package/cli/generator-help.js +16 -15
- package/cli/generator-indexer.js +2 -1
- package/cli/generator-jig-cli.js +11 -7
- package/cli/generator-jig.js +38 -33
- package/cli/generator-templates/archetypes/index.js +30 -13
- package/cli/generator-templates/archetypes/vector-matcher.js +8 -4
- package/cli/generator-templates/compact.js +4 -2
- package/cli/generator-templates/controller-converters.js +23 -14
- package/cli/generator-templates/controller.js +10 -5
- package/cli/generator-templates/hooks.js +4 -2
- package/cli/generator-templates/naming.js +2 -1
- package/cli/generator-templates/react.js +2 -1
- package/cli/generator-templates/specs.js +4 -2
- package/cli/generator-templates/svelte.js +4 -2
- package/cli/generator-templates/types.js +6 -3
- package/cli/generator-templates/views.js +4 -2
- package/cli/generator-templates/vue.js +4 -2
- package/cli/generator-writes.js +24 -0
- package/cli/generator.d.ts +1 -1
- package/cli/generator.js +86 -127
- package/cli/git-file-listing.js +69 -0
- package/cli/help-args.js +18 -0
- package/cli/help.d.ts +7 -3
- package/cli/help.js +94 -149
- package/cli/hooks/bypass-log.js +44 -0
- package/cli/hooks/claude-post-edit.js +79 -0
- package/cli/hooks/claude-pre-tool.js +178 -0
- package/cli/hooks/claude-settings-merge.js +101 -0
- package/cli/hooks/dispatch-identity.js +125 -0
- package/cli/hooks/entry.js +181 -0
- package/cli/hooks/foreign-identity.js +60 -0
- package/cli/hooks/guard-args.js +55 -0
- package/cli/hooks/guard-cases.js +94 -0
- package/cli/hooks/guard-config.js +43 -0
- package/cli/hooks/guard-invocation.js +106 -0
- package/cli/hooks/guard-paths.js +80 -0
- package/cli/hooks/guard-route.js +211 -0
- package/cli/hooks/guard-rules-nudge.js +37 -0
- package/cli/hooks/guard-rules-reads.js +66 -0
- package/cli/hooks/guard-rules-shell.js +239 -0
- package/cli/hooks/guard-rules.js +114 -0
- package/cli/hooks/hook-commands.js +21 -0
- package/cli/hooks/install-hooks-apply.js +36 -0
- package/cli/hooks/install-hooks-cli.js +100 -0
- package/cli/hooks/install-hooks-config.js +48 -0
- package/cli/hooks/install-hooks-pins.js +66 -0
- package/cli/hooks/install-hooks-plan.js +56 -0
- package/cli/hooks/install-hooks-status.js +7 -0
- package/cli/hooks/interpreter-writes.js +195 -0
- package/cli/hooks/launcher.js +60 -0
- package/cli/hooks/mcp-json-merge.js +39 -0
- package/cli/hooks/native-edit-lock.js +40 -0
- package/cli/hooks/native-tool-policy.js +152 -0
- package/cli/hooks/out-of-band-edits.js +141 -0
- package/cli/hooks/post-edit-scope.js +43 -0
- package/cli/hooks/project-status.js +59 -0
- package/cli/hooks/repo-membership.js +32 -0
- package/cli/hooks/run-hook.js +79 -0
- package/cli/hooks/session-identity.js +153 -0
- package/cli/hooks/session-start.js +50 -0
- package/cli/hooks/shell-lexer-readers.js +76 -0
- package/cli/hooks/shell-lexer-state.js +59 -0
- package/cli/hooks/shell-lexer.js +110 -0
- package/cli/hooks/shell-parse.js +232 -0
- package/cli/hooks/statusline.js +27 -0
- package/cli/hooks/xatoms-hints.js +38 -0
- package/cli/host-protocol.js +109 -0
- package/cli/host-shims.js +9 -8
- package/cli/index-admission.js +41 -0
- package/cli/index-freshness.js +83 -0
- package/cli/index-row-check.js +58 -0
- package/cli/index.js +52 -150
- package/cli/installer-templates.js +67 -75
- package/cli/installer.d.ts +16 -1
- package/cli/installer.js +159 -58
- package/cli/languages.js +8 -4
- package/cli/lattice-directives.js +24 -0
- package/cli/lattice-payload.js +34 -0
- package/cli/{tesseract-state.js → lattice-state.js} +8 -4
- package/cli/lease-view.js +48 -0
- package/cli/library/entry-fp.js +42 -0
- package/cli/library/entry-schema.js +90 -0
- package/cli/library/library-tasks.js +25 -0
- package/cli/library/registry.js +56 -0
- package/cli/library/reverify.js +56 -0
- package/cli/library/verify-checks.js +89 -0
- package/cli/library/verify.js +81 -0
- package/cli/license-config.js +124 -0
- package/cli/license.js +168 -135
- package/cli/line-count.js +23 -0
- package/cli/literal-replace.js +107 -0
- package/cli/main.js +166 -0
- package/cli/mcp/antigravity.js +11 -9
- package/cli/mcp/batch.js +59 -0
- package/cli/mcp/call-args.js +75 -0
- package/cli/mcp/call-effects.js +57 -0
- package/cli/mcp/call-scope.js +70 -62
- package/cli/mcp/context.js +81 -0
- package/cli/mcp/envelope.js +62 -0
- package/cli/mcp/fingerprint.js +26 -0
- package/cli/mcp/fresh-entry.js +34 -0
- package/cli/mcp/fresh-protocol.js +37 -0
- package/cli/mcp/fresh-runner.js +40 -0
- package/cli/mcp/help.js +53 -0
- package/cli/mcp/index.js +9 -5
- package/cli/mcp/inflight.js +47 -0
- package/cli/mcp/installer-jsonc.js +64 -0
- package/cli/mcp/installer-package.js +54 -0
- package/cli/mcp/installer-report.js +47 -0
- package/cli/mcp/installer-write.js +117 -0
- package/cli/mcp/installer.js +88 -187
- package/cli/mcp/manifests-analysis.js +156 -0
- package/cli/mcp/manifests-runners.js +48 -0
- package/cli/mcp/manifests-team.js +100 -0
- package/cli/mcp/manifests.js +69 -309
- package/cli/mcp/offload-worker.js +11 -0
- package/cli/mcp/offload.js +18 -0
- package/cli/mcp/patterns-cli.js +76 -0
- package/cli/mcp/patterns-forge-cli.js +120 -0
- package/cli/mcp/prompts.js +13 -10
- package/cli/mcp/resources.js +8 -35
- package/cli/mcp/roots.js +46 -0
- package/cli/mcp/scorecard-worker.js +7 -0
- package/cli/mcp/scorecard.js +42 -0
- package/cli/mcp/server-info.js +28 -0
- package/cli/mcp/server.js +91 -291
- package/cli/mcp/spec-harness.js +62 -0
- package/cli/mcp/stale-exec.js +48 -0
- package/cli/mcp/staleness.js +26 -0
- package/cli/mcp/stdio.js +141 -0
- package/cli/mcp/tool-call.js +73 -0
- package/cli/mcp/tools-audit-feed.js +33 -0
- package/cli/mcp/tools-audit.js +13 -7
- package/cli/mcp/tools-generate.js +3 -3
- package/cli/mcp/tools-lazy.js +39 -0
- package/cli/mcp/tools-patch.js +93 -21
- package/cli/mcp/tools-patterns.js +6 -3
- package/cli/mcp/tools-project.js +8 -4
- package/cli/mcp/tools-q-modes.js +15 -14
- package/cli/mcp/tools-q.js +94 -33
- package/cli/mcp/tools-read.js +30 -80
- package/cli/mcp/tools-team-feed.js +10 -8
- package/cli/mcp/tools-team-locks.js +22 -10
- package/cli/mcp/tools-team-mailbox.js +8 -7
- package/cli/mcp/tools-team-tasks.js +125 -38
- package/cli/mcp/tools-team.js +14 -7
- package/cli/mcp/tools-verify.js +44 -15
- package/cli/mcp/tools-write.js +11 -16
- package/cli/mcp/tools.js +202 -68
- package/cli/mcp/worker-run.js +24 -0
- package/cli/missing-imports.js +52 -0
- package/cli/mutation-args.js +96 -0
- package/cli/mutators.js +164 -61
- package/cli/navigator-actions.js +19 -13
- package/cli/navigator-banner-helpers.js +1 -26
- package/cli/navigator-banner.js +5 -3
- package/cli/navigator-conversion.js +23 -12
- package/cli/navigator-grades.js +70 -35
- package/cli/navigator-guide.js +13 -11
- package/cli/navigator-menu.js +80 -166
- package/cli/navigator-paged.js +15 -8
- package/cli/navigator-sections.js +32 -0
- package/cli/navigator-share.js +53 -39
- package/cli/navigator.d.ts +25 -5
- package/cli/navigator.js +14 -41
- package/cli/network-policy.js +72 -0
- package/cli/network-recorder.fixture.mjs +35 -0
- package/cli/outline/ast-outline.js +120 -0
- package/cli/outline/style-outline.js +42 -0
- package/cli/outline/vue-outline.js +88 -0
- package/cli/patcher-cli.js +255 -0
- package/cli/patcher.d.ts +55 -24
- package/cli/patcher.js +167 -358
- package/cli/path-scope.js +78 -22
- package/cli/patterns/fixtures/gt/A1.txt +109 -0
- package/cli/patterns/fixtures/gt/A10.txt +13 -0
- package/cli/patterns/fixtures/gt/A11.txt +10 -0
- package/cli/patterns/fixtures/gt/A12.txt +29 -0
- package/cli/patterns/fixtures/gt/A13.txt +39 -0
- package/cli/patterns/fixtures/gt/A14.txt +45 -0
- package/cli/patterns/fixtures/gt/A15.txt +70 -0
- package/cli/patterns/fixtures/gt/A16.txt +59 -0
- package/cli/patterns/fixtures/gt/A17.txt +29 -0
- package/cli/patterns/fixtures/gt/A18.txt +9 -0
- package/cli/patterns/fixtures/gt/A19.txt +29 -0
- package/cli/patterns/fixtures/gt/A2.txt +28 -0
- package/cli/patterns/fixtures/gt/A20.txt +83 -0
- package/cli/patterns/fixtures/gt/A21.txt +138 -0
- package/cli/patterns/fixtures/gt/A22.txt +32 -0
- package/cli/patterns/fixtures/gt/A23.txt +37 -0
- package/cli/patterns/fixtures/gt/A24.txt +67 -0
- package/cli/patterns/fixtures/gt/A25.txt +41 -0
- package/cli/patterns/fixtures/gt/A26.txt +29 -0
- package/cli/patterns/fixtures/gt/A3.txt +17 -0
- package/cli/patterns/fixtures/gt/A4.txt +44 -0
- package/cli/patterns/fixtures/gt/A5.txt +32 -0
- package/cli/patterns/fixtures/gt/A6.txt +15 -0
- package/cli/patterns/fixtures/gt/A7.txt +79 -0
- package/cli/patterns/fixtures/gt/A8.txt +41 -0
- package/cli/patterns/fixtures/gt/A9.txt +42 -0
- package/cli/patterns/fixtures/gt/B1.txt +11 -0
- package/cli/patterns/fixtures/gt/B10.txt +41 -0
- package/cli/patterns/fixtures/gt/B11.txt +7 -0
- package/cli/patterns/fixtures/gt/B2.txt +19 -0
- package/cli/patterns/fixtures/gt/B3.txt +70 -0
- package/cli/patterns/fixtures/gt/B4.txt +55 -0
- package/cli/patterns/fixtures/gt/B5.txt +21 -0
- package/cli/patterns/fixtures/gt/B6.txt +15 -0
- package/cli/patterns/fixtures/gt/B7.txt +24 -0
- package/cli/patterns/fixtures/gt/B8.txt +40 -0
- package/cli/patterns/fixtures/gt/B9.txt +18 -0
- package/cli/patterns/fixtures/gt/C1.txt +26 -0
- package/cli/patterns/fixtures/gt/C2.txt +33 -0
- package/cli/patterns/fixtures/gt/C3.txt +26 -0
- package/cli/patterns/fixtures/gt/C4.txt +7 -0
- package/cli/patterns/fixtures/gt/C5.txt +29 -0
- package/cli/patterns/fixtures/gt/C6.txt +7 -0
- package/cli/patterns/fixtures/gt/C7.txt +18 -0
- package/cli/patterns/fixtures/gt/labels.json +1837 -0
- package/cli/patterns/gt-build.js +55 -0
- package/cli/patterns/gt-locations.js +76 -0
- package/cli/patterns/gt-resolve.js +66 -0
- package/cli/patterns/gt-score-cli.js +80 -0
- package/cli/patterns/gt-score.js +188 -0
- package/cli/patterns/gt-text.js +79 -0
- package/cli/pillars-wizard.js +53 -24
- package/cli/pillars-write-guard.js +4 -1
- package/cli/postinstall.js +22 -19
- package/cli/project-detector.js +60 -35
- package/cli/project-markers.js +42 -0
- package/cli/project-scope.js +20 -0
- package/cli/read-revision.js +70 -0
- package/cli/reader-cards-gate.js +29 -0
- package/cli/reader-cards.js +153 -0
- package/cli/reader-cli.js +45 -18
- package/cli/reader-format.js +48 -0
- package/cli/reader-logic.js +128 -250
- package/cli/reader.js +210 -221
- package/cli/request-context.js +9 -0
- package/cli/result-status.js +23 -0
- package/cli/rules.js +81 -0
- package/cli/scaffold.js +80 -51
- package/cli/scan-ignore.js +12 -0
- package/cli/search-args.js +102 -0
- package/cli/search-ast-module.js +103 -0
- package/cli/search-ast.js +75 -112
- package/cli/search-commands-blast.js +70 -0
- package/cli/search-commands-graph.js +57 -105
- package/cli/search-commands-hazards.js +105 -0
- package/cli/search-commands-literal.js +102 -0
- package/cli/search-commands-semantic.js +39 -27
- package/cli/search-commands.js +93 -186
- package/cli/search-db.js +52 -252
- package/cli/search-debug.js +13 -0
- package/cli/search-graph-edges.js +112 -0
- package/cli/search-index-meta.js +66 -0
- package/cli/search-index-write.js +153 -0
- package/cli/search-literal-files.js +64 -0
- package/cli/search-literal.js +150 -0
- package/cli/search-output.js +88 -0
- package/cli/search-queries-graph.js +120 -193
- package/cli/search-queries-semantic.js +41 -14
- package/cli/search-queries-similar.js +2 -1
- package/cli/search-queries.js +46 -21
- package/cli/search-query-view.js +67 -0
- package/cli/search-rank.js +54 -0
- package/cli/search-replace-blocks.js +120 -0
- package/cli/search-resolve.js +52 -0
- package/cli/search-root.js +78 -0
- package/cli/search-scan.js +102 -0
- package/cli/search-schema-ddl.js +172 -0
- package/cli/search-schema.js +135 -220
- package/cli/search-session.js +11 -0
- package/cli/search-sync-paths.js +78 -0
- package/cli/search-sync-plan.js +63 -0
- package/cli/search-sync-rows.js +96 -0
- package/cli/search-sync.js +128 -0
- package/cli/search-tokenizer.js +8 -4
- package/cli/search.d.ts +26 -5
- package/cli/search.js +156 -410
- package/cli/sfc/component-resolver.js +96 -0
- package/cli/sfc/import-coverage.js +24 -0
- package/cli/sfc/module-aliases.js +102 -0
- package/cli/sfc/script-asts.js +51 -0
- package/cli/sfc/sfc-metadata.js +104 -0
- package/cli/sfc/sfc-parse.js +101 -0
- package/cli/sfc/svelte-parse.js +46 -0
- package/cli/sfc/template-walk.js +78 -0
- package/cli/sfc-scripts.js +60 -0
- package/cli/share-consent.js +49 -0
- package/cli/share-outcome.js +57 -0
- package/cli/silence-warnings.js +2 -1
- package/cli/source-parse.js +130 -0
- package/cli/spec-isolated-home.js +17 -0
- package/cli/spec-support/import-trace.mjs +22 -0
- package/cli/spec-support/run-cli.js +58 -0
- package/cli/sqlite-memory.js +47 -0
- package/cli/status-command.js +107 -0
- package/cli/symbol-locator.js +121 -0
- package/cli/team/agent-identity.js +58 -0
- package/cli/team/audit-run-adoption.js +117 -0
- package/cli/team/audit-run-bypass.js +177 -0
- package/cli/team/audit-run-calls.js +108 -0
- package/cli/team/audit-run-db.js +181 -0
- package/cli/team/audit-run-identity.js +80 -0
- package/cli/team/audit-run-invocations.js +130 -0
- package/cli/team/audit-run-protocol.js +258 -0
- package/cli/team/audit-run-render.js +107 -0
- package/cli/team/audit-run-shas.js +179 -0
- package/cli/team/audit-run.js +203 -0
- package/cli/team/claim-ignore-deps.js +16 -0
- package/cli/team/coordination-busy.js +164 -0
- package/cli/team/coordination-db.js +85 -0
- package/cli/team/coordination-fixture.js +53 -0
- package/cli/team/coordination-repos.js +72 -0
- package/cli/team/coordination-root.js +161 -0
- package/cli/team/coordination-target.js +114 -0
- package/cli/team/dispatch-templates/dispatch-v1.js +228 -0
- package/cli/team/edit-shas.js +45 -0
- package/cli/team/lease-activity.js +130 -0
- package/cli/team/lease-cap.js +67 -0
- package/cli/team/lease-contention.js +30 -0
- package/cli/team/lease-key.js +46 -0
- package/cli/team/lease-lapse.js +83 -0
- package/cli/team/lease-reacquire.js +99 -0
- package/cli/team/lease-renew.js +97 -0
- package/cli/team/lease-roots.js +30 -0
- package/cli/team/lease-waiter-notice.js +48 -0
- package/cli/team/staged-leases.js +64 -0
- package/cli/team/task-completion-output.js +78 -0
- package/cli/team/task-detail-sections.js +86 -0
- package/cli/team/task-list-view.js +10 -1
- package/cli/team/task-ref.js +73 -0
- package/cli/team/task-target.js +53 -0
- package/cli/team/task-tier.js +17 -0
- package/cli/team/team-brief.js +78 -0
- package/cli/team/team-commands-board.js +118 -0
- package/cli/team/team-commands-dispatch.js +85 -0
- package/cli/team/team-commands-lock-staged.js +87 -0
- package/cli/team/team-commands-lock-views.js +144 -0
- package/cli/team/team-commands-lock.js +115 -26
- package/cli/team/team-commands-migrate.js +171 -0
- package/cli/team/team-commands-profile.js +44 -0
- package/cli/team/team-commands-repo.js +114 -0
- package/cli/team/team-commands-task.js +312 -0
- package/cli/team/team-commands-tokens.js +57 -0
- package/cli/team/team-commands-triage.js +43 -0
- package/cli/team/team-commands-vds.js +2 -1
- package/cli/team/team-commands.js +108 -463
- package/cli/team/team-db-agents.js +12 -6
- package/cli/team/team-db-feed.js +25 -13
- package/cli/team/team-db-lock-promotion.js +95 -51
- package/cli/team/team-db-locks.js +167 -35
- package/cli/team/team-db-mailbox.js +15 -8
- package/cli/team/team-db-readonly.js +76 -0
- package/cli/team/team-db-task-helpers.js +68 -19
- package/cli/team/team-db-tasks.js +21 -9
- package/cli/team/team-db-transaction.js +20 -5
- package/cli/team/team-db.js +11 -4
- package/cli/team/team-dispatch-batches.js +271 -0
- package/cli/team/team-dispatch-prompts.js +152 -0
- package/cli/team/team-dispatch-render.js +154 -0
- package/cli/team/team-dispatch-run-cli.js +154 -0
- package/cli/team/team-dispatch-runs.js +155 -0
- package/cli/team/team-dispatch-script.js +67 -0
- package/cli/team/team-dispatch-select.js +220 -0
- package/cli/team/team-dispatch-v2-fixture.js +62 -0
- package/cli/team/team-dispatch-v2.js +207 -0
- package/cli/team/team-dispatch.js +229 -0
- package/cli/team/team-dispatcher-authority.js +43 -0
- package/cli/team/team-flags.js +222 -48
- package/cli/team/team-format.js +80 -26
- package/cli/team/team-handoff-hint.js +9 -0
- package/cli/team/team-migrate-agents.js +52 -0
- package/cli/team/team-migrate-apply.js +178 -0
- package/cli/team/team-migrate-junk.js +55 -0
- package/cli/team/team-migrate-metadata.js +80 -0
- package/cli/team/team-migrate-paths.js +50 -0
- package/cli/team/team-migrate-plan.js +85 -0
- package/cli/team/team-migrate-report.js +89 -0
- package/cli/team/team-migrate-tables.js +85 -0
- package/cli/team/team-migrate.js +193 -0
- package/cli/team/team-needs.js +99 -0
- package/cli/team/team-profile-format.js +126 -0
- package/cli/team/team-profile.js +144 -0
- package/cli/team/team-projects-coordinator.js +54 -33
- package/cli/team/team-projects-format.js +8 -4
- package/cli/team/team-projects-memory.js +6 -3
- package/cli/team/team-projects.js +2 -1
- package/cli/team/team-release-train.js +2 -1
- package/cli/team/team-route.js +132 -0
- package/cli/team/team-schema-repo.js +48 -0
- package/cli/team/team-schema-usage.js +25 -0
- package/cli/team/team-schema-vds.js +7 -3
- package/cli/team/team-schema.js +24 -4
- package/cli/team/team-task-close.js +132 -0
- package/cli/team/team-task-guard.js +24 -0
- package/cli/team/team-task-handoff.js +122 -0
- package/cli/team/team-task-ownership.js +74 -0
- package/cli/team/team-task-rules.js +21 -0
- package/cli/team/team-task-uncommitted.js +49 -0
- package/cli/team/team-telemetry.js +44 -32
- package/cli/team/team-tokens.js +4 -1
- package/cli/team/team-triage-generate.js +212 -0
- package/cli/team/team-triage-log.js +11 -0
- package/cli/team/team-triage-verify.js +153 -0
- package/cli/team/team-triage.js +92 -339
- package/cli/team/team-vds.js +2 -1
- package/cli/team/usage-compute.js +70 -0
- package/cli/team/usage-pricing.js +62 -0
- package/cli/team/usage-reader.js +106 -0
- package/cli/team/usage-render.js +72 -0
- package/cli/team/usage-store.js +56 -0
- package/cli/team/usage-transcript.js +127 -0
- package/cli/team/write-lock-guard.js +48 -0
- package/cli/telemetry/call-ledger.js +264 -0
- package/cli/telemetry/call-schema.js +53 -0
- package/cli/telemetry/savings-dbs.js +111 -0
- package/cli/telemetry/savings-render.js +68 -0
- package/cli/telemetry/savings-report.js +101 -0
- package/cli/telemetry/savings-routing.js +66 -0
- package/cli/telemetry/savings-tooling.js +140 -0
- package/cli/terminal.js +38 -40
- package/cli/tesseract-manifesto.js +2 -17
- package/cli/tesseract.js +8 -21
- package/cli/test-audit.js +218 -0
- package/cli/test-changes.js +60 -0
- package/cli/test-command.js +201 -0
- package/cli/test-depth.js +43 -0
- package/cli/test-failures.js +170 -0
- package/cli/test-graph-open.js +150 -0
- package/cli/test-graph.js +188 -0
- package/cli/test-lanes.js +117 -0
- package/cli/test-output.js +155 -0
- package/cli/test-paths.js +51 -0
- package/cli/test-profile.js +64 -0
- package/cli/test-report.js +112 -0
- package/cli/test-run.js +53 -0
- package/cli/test-scope.js +55 -0
- package/cli/test-select.js +107 -0
- package/cli/test-slots.js +276 -0
- package/cli/test-workers.js +45 -0
- package/cli/test-workspace.js +83 -0
- package/cli/theme.js +6 -3
- package/cli/timers.js +6 -0
- package/cli/trend.js +10 -5
- package/cli/tsconfig-shape.js +20 -0
- package/cli/tty-banner.js +11 -0
- package/cli/typecheck-audit.js +146 -0
- package/cli/typecheck-command.js +94 -0
- package/cli/typecheck-sandbox.js +93 -0
- package/cli/ui-actions-codebase.js +20 -7
- package/cli/ui-actions-helpers.js +9 -0
- package/cli/ui-actions-studio.js +11 -7
- package/cli/ui-actions-tasks.js +3 -2
- package/cli/ui-actions-vds.js +2 -1
- package/cli/ui-actions.js +4 -2
- package/cli/ui-attention.js +28 -12
- package/cli/ui-auth-hosts.js +46 -0
- package/cli/ui-auth.js +89 -0
- package/cli/ui-client-cert.js +2 -1
- package/cli/ui-client-filetree.js +2 -2
- package/cli/ui-db-studio.js +23 -33
- package/cli/ui-dev-watcher.js +2 -1
- package/cli/ui-forum-data.js +45 -46
- package/cli/ui-forum-topics.js +6 -3
- package/cli/ui-fresh-index.js +11 -0
- package/cli/ui-handlers.js +6 -6
- package/cli/ui-html.js +6 -12
- package/cli/ui-index.html +3 -3
- package/cli/ui-route-helpers.js +10 -0
- package/cli/ui-server-routes.js +15 -17
- package/cli/ui-server.js +49 -50
- package/cli/ui-sql-guard.js +76 -0
- package/cli/ui-sql-mask.js +61 -0
- package/cli/ui-sse.js +5 -4
- package/cli/ui-template-filetree.js +4 -4
- package/cli/ui-template-kanban.js +4 -7
- package/cli/verify-changed.js +43 -0
- package/cli/verify-helpers.js +21 -207
- package/cli/verify-lint.js +54 -20
- package/cli/verify-report.js +117 -0
- package/cli/verify-steps.js +71 -0
- package/cli/verify.js +111 -372
- package/cli/wait-command.js +147 -0
- package/cli/workspace-run.js +153 -0
- package/cli/workspace.js +147 -0
- package/cli/write-append.js +28 -0
- package/library/node-js/is-path-inside/entry.json +57 -0
- package/library/node-js/is-path-inside/negative/path-scope.js.txt +13 -0
- package/library/node-js/is-path-inside/piece.js +15 -0
- package/library/node-js/is-path-inside/piece.spec.js +38 -0
- package/library/node-js/read-json-or/entry.json +70 -0
- package/library/node-js/read-json-or/negative/ratchet.js.txt +14 -0
- package/library/node-js/read-json-or/negative/workspace.js.txt +8 -0
- package/library/node-js/read-json-or/piece.js +17 -0
- package/library/node-js/read-json-or/piece.spec.js +36 -0
- package/library/vue-ts/nullable-timer-handle/ambient.d.ts +5 -0
- package/library/vue-ts/nullable-timer-handle/entry.json +84 -0
- package/library/vue-ts/nullable-timer-handle/negative/const-alias.ts.txt +17 -0
- package/library/vue-ts/nullable-timer-handle/negative/inline-guard.ts.txt +15 -0
- package/library/vue-ts/nullable-timer-handle/piece.spec.js +48 -0
- package/library/vue-ts/nullable-timer-handle/piece.ts +41 -0
- package/package.json +9 -3
- package/scripts/check-framework-generation.mjs +32 -12
- package/scripts/direct-run.mjs +9 -0
- package/scripts/pre-commit.sh +48 -74
- package/scripts/publish-both.mjs +24 -16
- package/scripts/publish-summary.mjs +14 -0
- package/scripts/postinstall.mjs +0 -27
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// Binding index for Forge canonicalization: one entry per Identifier node in a parsed file, saying
|
|
2
|
+
// whether it declares or references a binding, which binding (an opaque per-file id, never hashed) and
|
|
3
|
+
// where that binding comes from: 'import' (module binding), 'global' (no binding in the file),
|
|
4
|
+
// 'local' (declared somewhere in the file) or 'name' (a property name or key, not a binding at all).
|
|
5
|
+
// describeIdentifier is exported so a shared audit traversal can fill the same index in its own pass.
|
|
6
|
+
// Every describer reads a site { node, parent, grandparent, scope }: createBindingVisitors builds one
|
|
7
|
+
// from each NodePath of a Babel traverse (the audit's merged pass), and fillBindingIndex from a plain
|
|
8
|
+
// walk over the scopes Babel crawled (binding-walk.js, #5911), so both fill the same index.
|
|
9
|
+
// An import entry also carries importRef '<source>#<imported>' (default, * or the exported name), so
|
|
10
|
+
// `get` from 'lodash' and `get` from './api' never share a label or an anchor. A relative source is
|
|
11
|
+
// resolved against the file's directory when filePath is known, and a `node:` prefix is dropped for
|
|
12
|
+
// builtins that also exist without it, so the same module reads the same from every file. Only the
|
|
13
|
+
// .js/.jsx/.ts/.tsx family is dropped (a TS import of './a.js' means a.ts); .mjs, .cjs, .vue and the
|
|
14
|
+
// rest are distinct files and keep their extension (#2586).
|
|
15
|
+
// The index also holds two non-Identifier kinds of entry (#2586): a JSX component name (`<Foo>`, the
|
|
16
|
+
// root of `<foo.Bar>`) resolved like any reference, and the string source of a dynamic import() or a
|
|
17
|
+
// global require(), as an import entry with importRef '<source>#*'.
|
|
18
|
+
import { builtinModules } from 'node:module';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { lazyTypes, traverse } from '../babel-lazy.js';
|
|
21
|
+
import { walkBindingSites } from './binding-walk.js';
|
|
22
|
+
|
|
23
|
+
const NAME_ENTRY = Object.freeze({ origin: 'name', bindingId: null, isDecl: false, importRef: null });
|
|
24
|
+
const INTERCHANGEABLE_EXTENSION = /\.(js|jsx|ts|tsx)$/;
|
|
25
|
+
const UNPREFIXED_BUILTINS = new Set(builtinModules.filter((name) => !name.startsWith('node:')));
|
|
26
|
+
const JSX_NAME_PARENTS = new Set(['JSXOpeningElement', 'JSXClosingElement']);
|
|
27
|
+
const INTRINSIC_JSX_NAME = /^[a-z]|-/;
|
|
28
|
+
const IMPORTED_NAME = {
|
|
29
|
+
ImportDefaultSpecifier: () => 'default',
|
|
30
|
+
ImportNamespaceSpecifier: () => '*',
|
|
31
|
+
ImportSpecifier: (specifier) => specifier.imported.name ?? specifier.imported.value
|
|
32
|
+
};
|
|
33
|
+
const importRefCache = new WeakMap();
|
|
34
|
+
const ALWAYS_INITIALIZED = new Set(['var', 'param', 'hoisted']);
|
|
35
|
+
const LEXICAL_KINDS = new Set(['let', 'const']);
|
|
36
|
+
const RELATIVE_PATH = /^\.\.?\//;
|
|
37
|
+
const MODULE_SOURCE_PARENTS = new Set(['ImportDeclaration', 'ExportNamedDeclaration', 'ExportAllDeclaration']);
|
|
38
|
+
|
|
39
|
+
/** Module identity of an import source as seen from filePath (null: unknown file, kept verbatim). */
|
|
40
|
+
export const normalizeImportSource = (source, filePath = null) => {
|
|
41
|
+
const unprefixed = source.replace(/^node:/, '');
|
|
42
|
+
const isSharedBuiltin = source.startsWith('node:') && UNPREFIXED_BUILTINS.has(unprefixed);
|
|
43
|
+
const bare = isSharedBuiltin ? unprefixed : source;
|
|
44
|
+
const isResolvable = bare.startsWith('.') && Boolean(filePath);
|
|
45
|
+
if (!isResolvable) return bare;
|
|
46
|
+
return path.posix.join(path.posix.dirname(filePath), bare).replace(INTERCHANGEABLE_EXTENSION, '');
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const computeImportRef = (binding, filePath) => {
|
|
50
|
+
const specifier = binding.path.node;
|
|
51
|
+
const declaration = binding.path.parent;
|
|
52
|
+
const isImportDeclaration = declaration?.type === 'ImportDeclaration';
|
|
53
|
+
if (!isImportDeclaration) return null;
|
|
54
|
+
const imported = IMPORTED_NAME[specifier.type]?.(specifier) ?? specifier.local?.name ?? '';
|
|
55
|
+
return `${normalizeImportSource(declaration.source.value, filePath)}#${imported}`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const importRefOf = (binding, filePath) => {
|
|
59
|
+
const isCached = importRefCache.has(binding);
|
|
60
|
+
if (isCached) return importRefCache.get(binding);
|
|
61
|
+
const ref = computeImportRef(binding, filePath);
|
|
62
|
+
importRefCache.set(binding, ref);
|
|
63
|
+
return ref;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const originOf = (binding) => {
|
|
67
|
+
if (!binding) return 'global';
|
|
68
|
+
return binding.kind === 'module' ? 'import' : 'local';
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const idFor = (binding, ids) => {
|
|
72
|
+
if (!binding) return null;
|
|
73
|
+
const isKnown = ids.has(binding);
|
|
74
|
+
if (isKnown) return ids.get(binding);
|
|
75
|
+
const next = ids.size + 1;
|
|
76
|
+
ids.set(binding, next);
|
|
77
|
+
return next;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* True when a read of the binding here can never hit its TDZ (#2594): a var, param or function binding,
|
|
82
|
+
* or a let/const/class declared earlier in the same function, outside a switch case (a case can be
|
|
83
|
+
* jumped over). Imports, class-expression names and reads from a nested function are never certain.
|
|
84
|
+
*/
|
|
85
|
+
const isInitializedAt = (binding, site) => {
|
|
86
|
+
const kind = binding?.kind;
|
|
87
|
+
const isAlwaysInitialized = ALWAYS_INITIALIZED.has(kind);
|
|
88
|
+
if (isAlwaysInitialized) return true;
|
|
89
|
+
const isLexical = LEXICAL_KINDS.has(kind);
|
|
90
|
+
if (!isLexical) return false;
|
|
91
|
+
const declaration = binding.path;
|
|
92
|
+
const end = declaration.node?.end;
|
|
93
|
+
const endsBefore = typeof end === 'number' && typeof site.node.start === 'number' && end <= site.node.start;
|
|
94
|
+
const isSameFunction = binding.scope.getFunctionParent() === site.scope.getFunctionParent();
|
|
95
|
+
const statement = declaration.isVariableDeclarator() ? declaration.parentPath : declaration;
|
|
96
|
+
const isInCase = Boolean(statement?.parentPath?.isSwitchCase());
|
|
97
|
+
return endsBefore && isSameFunction && !isInCase;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
let babelTests = null;
|
|
101
|
+
const identifierTests = () => {
|
|
102
|
+
babelTests = babelTests ?? { isReferenced: lazyTypes.isReferenced, isBinding: lazyTypes.isBinding };
|
|
103
|
+
return babelTests;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** The site of a NodePath: what every describer reads (grandparent as NodePath#isReferencedIdentifier reads it). */
|
|
107
|
+
export const siteOfPath = (nodePath) => ({ node: nodePath.node, parent: nodePath.parent, grandparent: nodePath.parentPath?.parent, scope: nodePath.scope });
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Describes one Identifier site ({ node, parent, grandparent, scope }, see siteOfPath). `ids` maps
|
|
111
|
+
* Babel binding objects to small per-file integers; filePath (project-relative) resolves relative
|
|
112
|
+
* import sources. isInitialized (references only) says the read cannot throw a TDZ ReferenceError
|
|
113
|
+
* (alias inlining relies on it). isRef and isDecl are t.isReferenced and t.isBinding, which is what
|
|
114
|
+
* NodePath#isReferencedIdentifier and #isBindingIdentifier compute for an Identifier.
|
|
115
|
+
*/
|
|
116
|
+
export const describeIdentifier = (site, ids, filePath = null) => {
|
|
117
|
+
const { node, parent, grandparent } = site;
|
|
118
|
+
const { isReferenced, isBinding } = identifierTests();
|
|
119
|
+
// Babel reports some references (the operand of `!`) as binding identifiers too: a reference wins.
|
|
120
|
+
const isRef = isReferenced(node, parent, grandparent);
|
|
121
|
+
const isDecl = !isRef && isBinding(node, parent, grandparent);
|
|
122
|
+
const isNameOnly = !isDecl && !isRef;
|
|
123
|
+
if (isNameOnly) return NAME_ENTRY;
|
|
124
|
+
const binding = site.scope.getBinding(node.name);
|
|
125
|
+
const origin = originOf(binding);
|
|
126
|
+
const importRef = origin === 'import' ? importRefOf(binding, filePath) : null;
|
|
127
|
+
const isInitialized = isRef && isInitializedAt(binding, site);
|
|
128
|
+
return { origin, bindingId: idFor(binding, ids), isDecl, importRef, isInitialized };
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/** Entry for a JSX element name that names a value (component or member root), else null. */
|
|
132
|
+
const describeJsxName = (site, ids, filePath) => {
|
|
133
|
+
const { node, parent } = site;
|
|
134
|
+
const isElementName = JSX_NAME_PARENTS.has(parent.type) && parent.name === node;
|
|
135
|
+
const isComponentName = isElementName && !INTRINSIC_JSX_NAME.test(node.name);
|
|
136
|
+
const isMemberRoot = parent.type === 'JSXMemberExpression' && parent.object === node;
|
|
137
|
+
const isReference = isComponentName || isMemberRoot;
|
|
138
|
+
if (!isReference) return null;
|
|
139
|
+
const binding = site.scope.getBinding(node.name);
|
|
140
|
+
const origin = originOf(binding);
|
|
141
|
+
const importRef = origin === 'import' ? importRefOf(binding, filePath) : null;
|
|
142
|
+
return { origin, bindingId: idFor(binding, ids), isDecl: false, importRef };
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/** The string source of import('x') or a global require('x'), else null. */
|
|
146
|
+
const dynamicSourceOf = (site) => {
|
|
147
|
+
const { node } = site;
|
|
148
|
+
const callee = node.callee;
|
|
149
|
+
const isImportCall = callee?.type === 'Import';
|
|
150
|
+
const isRequire = callee?.type === 'Identifier' && callee.name === 'require' && !site.scope.getBinding('require');
|
|
151
|
+
const source = node.type === 'ImportExpression' ? node.source : node.arguments?.[0];
|
|
152
|
+
const isCall = isImportCall || isRequire || node.type === 'ImportExpression';
|
|
153
|
+
const isStatic = source?.type === 'StringLiteral';
|
|
154
|
+
return isCall && isStatic ? source : null;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const indexDynamicSource = (site, index, filePath) => {
|
|
158
|
+
const source = dynamicSourceOf(site);
|
|
159
|
+
if (!source) return;
|
|
160
|
+
const importRef = `${normalizeImportSource(source.value, filePath)}#*`;
|
|
161
|
+
index.set(source, { origin: 'import', bindingId: null, isDecl: false, importRef });
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Any './' or '../' string (or expression-free template) is a file-relative path anchor (#2594):
|
|
166
|
+
* createRequire's require, require.resolve, import.meta.resolve/glob, new URL(rel, import.meta.url) and
|
|
167
|
+
* vi.mock all resolve it against the file, so the same text in two directories names two files.
|
|
168
|
+
* Static import/export sources are left to their bindings.
|
|
169
|
+
*/
|
|
170
|
+
const indexRelativeString = (site, value, index, filePath) => {
|
|
171
|
+
const isRelative = Boolean(filePath) && typeof value === 'string' && RELATIVE_PATH.test(value);
|
|
172
|
+
const isModuleSource = MODULE_SOURCE_PARENTS.has(site.parent?.type) && site.parent.source === site.node;
|
|
173
|
+
const isAnchor = isRelative && !isModuleSource && !index.has(site.node);
|
|
174
|
+
if (!isAnchor) return;
|
|
175
|
+
const importRef = `${normalizeImportSource(value, filePath)}#*`;
|
|
176
|
+
index.set(site.node, { origin: 'import', bindingId: null, isDecl: false, importRef });
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/** Site handlers by node type that fill `index` (Map<node, entry>); `ids` numbers bindings per file. */
|
|
180
|
+
const createSiteHandlers = (index, ids, filePath) => {
|
|
181
|
+
const onCall = (site) => indexDynamicSource(site, index, filePath);
|
|
182
|
+
return {
|
|
183
|
+
Identifier(site) {
|
|
184
|
+
index.set(site.node, describeIdentifier(site, ids, filePath));
|
|
185
|
+
},
|
|
186
|
+
JSXIdentifier(site) {
|
|
187
|
+
const entry = describeJsxName(site, ids, filePath);
|
|
188
|
+
if (entry) index.set(site.node, entry);
|
|
189
|
+
},
|
|
190
|
+
CallExpression: onCall,
|
|
191
|
+
ImportExpression: onCall,
|
|
192
|
+
StringLiteral(site) {
|
|
193
|
+
indexRelativeString(site, site.node.value, index, filePath);
|
|
194
|
+
},
|
|
195
|
+
TemplateLiteral(site) {
|
|
196
|
+
const isPlain = site.node.expressions.length === 0 && site.parent?.type !== 'TaggedTemplateExpression';
|
|
197
|
+
if (isPlain) indexRelativeString(site, site.node.quasis[0]?.value.cooked, index, filePath);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Babel visitors that fill `index` (Map<node, entry>); `ids` numbers bindings per file. The audit's
|
|
204
|
+
* merged traverse uses these; fillBindingIndex builds the same index without a visitor traverse.
|
|
205
|
+
*/
|
|
206
|
+
export const createBindingVisitors = (index, ids, filePath = null) => {
|
|
207
|
+
const visitors = {};
|
|
208
|
+
for (const [type, handle] of Object.entries(createSiteHandlers(index, ids, filePath))) visitors[type] = (nodePath) => handle(siteOfPath(nodePath));
|
|
209
|
+
return visitors;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Fills `index` and `ids` for one Babel AST as createBindingVisitors would in a traverse of it, from a
|
|
214
|
+
* walk over the scopes Babel crawled (binding-walk.js). When the walk cannot finish (a scopable node
|
|
215
|
+
* without a crawled scope, or a root that is not a File), nothing it found is kept and a real
|
|
216
|
+
* traverse fills the index instead.
|
|
217
|
+
*/
|
|
218
|
+
export const fillBindingIndex = (ast, index, ids, filePath = null) => {
|
|
219
|
+
const walkedIndex = new Map();
|
|
220
|
+
const walkedIds = new Map(ids);
|
|
221
|
+
const isWalked = walkBindingSites(ast, createSiteHandlers(walkedIndex, walkedIds, filePath));
|
|
222
|
+
if (!isWalked) {
|
|
223
|
+
traverse(ast, createBindingVisitors(index, ids, filePath));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
for (const [node, entry] of walkedIndex) index.set(node, entry);
|
|
227
|
+
for (const [binding, id] of walkedIds) ids.set(binding, id);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/** Builds Map<IdentifierNode, entry> for a whole Babel File/Program AST. */
|
|
231
|
+
export const buildBindingIndex = (ast, { filePath = null } = {}) => {
|
|
232
|
+
const index = new Map();
|
|
233
|
+
fillBindingIndex(ast, index, new Map(), filePath);
|
|
234
|
+
return index;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/** Entry for an Identifier the index never saw (synthesized nodes): treated as a global name. */
|
|
238
|
+
export const UNKNOWN_IDENTIFIER = Object.freeze({ origin: 'global', bindingId: null, isDecl: false, importRef: null });
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// `chemx blueprint <group-id|bp-id> | --item=A7 [--json]`, `chemx blueprint holes <target>` and
|
|
2
|
+
// `chemx blueprint fill <target> id=value ... --as=@handle` (design doc, Surfaces). The target is a group
|
|
3
|
+
// id prefix (from `chemx patterns --forge`), a stored blueprint id, or a ground-truth item. Showing a
|
|
4
|
+
// blueprint refreshes the ledger, builds it from the group and stores it by id; the same group in the same
|
|
5
|
+
// state always yields the same bytes.
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { openIndexDb } from '../search-schema.js';
|
|
9
|
+
import { resolveIndexRoot } from '../search-root.js';
|
|
10
|
+
import { syncFingerprints } from './fingerprint-sync.js';
|
|
11
|
+
import { runForgeGroups, readLedger } from './forge-groups.js';
|
|
12
|
+
import { createFacetResolver } from './facets.js';
|
|
13
|
+
import { createBlueprintContext } from './blueprint-context.js';
|
|
14
|
+
import { buildBlueprint, canonicalJson, withFills } from './blueprint.js';
|
|
15
|
+
import { loadMatchableEntries } from './library-match.js';
|
|
16
|
+
import { groupByPrefix, groupByItem } from './blueprint-target.js';
|
|
17
|
+
import { saveBlueprint, readBlueprint, readFills, validateFill, saveFill } from './blueprint-store.js';
|
|
18
|
+
|
|
19
|
+
const SHOWN_REJECTED = 8;
|
|
20
|
+
const SUBCOMMANDS = new Set(['holes', 'fill']);
|
|
21
|
+
|
|
22
|
+
const flagValue = (args, name) => args.find((arg) => arg.startsWith(`--${name}=`))?.slice(name.length + 3);
|
|
23
|
+
|
|
24
|
+
const write = (lines) => process.stdout.write(`${lines.join('\n')}\n`);
|
|
25
|
+
|
|
26
|
+
/** { sub, target, item, pairs, json, agent } from argv. */
|
|
27
|
+
export const parseBlueprintArgs = (args) => {
|
|
28
|
+
const sub = SUBCOMMANDS.has(args[0]) ? args[0] : 'show';
|
|
29
|
+
const rest = sub === 'show' ? args : args.slice(1);
|
|
30
|
+
const positional = rest.filter((arg) => !arg.startsWith('-'));
|
|
31
|
+
const pairs = sub === 'fill' ? positional.filter((arg) => arg.includes('=')) : [];
|
|
32
|
+
const target = positional.find((arg) => !arg.includes('=')) ?? null;
|
|
33
|
+
return { sub, target, item: flagValue(args, 'item') ?? null, pairs, json: args.includes('--json'), agent: flagValue(args, 'as') ?? process.env.CHEMX_AGENT_ID ?? '' };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const fileReaderAt = (root) => (relativePath) => {
|
|
37
|
+
try {
|
|
38
|
+
return fs.readFileSync(path.join(root, relativePath), 'utf-8');
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const contextFor = (db, cwd, groups = []) => {
|
|
45
|
+
const root = resolveIndexRoot(cwd);
|
|
46
|
+
const resolver = createFacetResolver(root);
|
|
47
|
+
return createBlueprintContext({ root, ledger: readLedger(db), groups, readFile: fileReaderAt(root), entries: loadMatchableEntries(), packageRootOfFile: resolver.packageRootOfFile });
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const siteLine = (site) => ` ${site.file}:${site.range[0]}-${site.range[1]}`;
|
|
51
|
+
|
|
52
|
+
const holeSummary = (hole) => `${hole.id} (${hole.tier}${hole.default === null ? ', open' : `, default ${JSON.stringify(hole.default)}`})`;
|
|
53
|
+
|
|
54
|
+
/** Human lines for a blueprint with its fills applied. */
|
|
55
|
+
export const blueprintLines = (blueprint) => {
|
|
56
|
+
const piece = blueprint.piece;
|
|
57
|
+
const where = piece.module ? `${piece.module}${piece.moduleIsNew ? ' (new)' : ''}` : 'no module';
|
|
58
|
+
const head = [blueprint.id, blueprint.kind, piece.name, `-> ${where}`].join(' ');
|
|
59
|
+
const counts = `${blueprint.evidence.instances} sites/${blueprint.evidence.files} files`;
|
|
60
|
+
const rejected = blueprint.evidence.rejectedMembers;
|
|
61
|
+
return [
|
|
62
|
+
`${head} | ${counts} | needs ${blueprint.needs}${blueprint.autoApplicable ? ' auto' : ''} | group ${blueprint.group}`,
|
|
63
|
+
...(piece.fromPiece ? [` library piece ${piece.fromPiece}${piece.existing ? ` (already in the project: ${piece.existing})` : ''}`] : []),
|
|
64
|
+
` placement: ${piece.placementReason}`,
|
|
65
|
+
' sites:', ...blueprint.callSites.map(siteLine),
|
|
66
|
+
...(rejected.length > 0 ? [' rejected members (left as they are):', ...rejected.slice(0, SHOWN_REJECTED).map((entry) => ` ${entry.at} ${entry.reason}`), ...(rejected.length > SHOWN_REJECTED ? [` +${rejected.length - SHOWN_REJECTED} more (--json lists all)`] : [])] : []),
|
|
67
|
+
...(blueprint.drift.length > 0 ? [' drift (similar spans that differ):', ...blueprint.drift.map((span) => ` ${span.at}`)] : []),
|
|
68
|
+
` holes: ${blueprint.holes.map(holeSummary).join(', ')}`,
|
|
69
|
+
` next: chemx blueprint holes ${blueprint.id}; chemx blueprint fill ${blueprint.id} <hole>=<value> --as=@you`
|
|
70
|
+
];
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const holeLines = (blueprint) => blueprint.holes.flatMap((hole) => [
|
|
74
|
+
`${hole.id} ${hole.kind} ${hole.tier}${hole.default === null ? ' open' : ''}`,
|
|
75
|
+
...(hole.default === null ? [] : [` default: ${hole.default}`]),
|
|
76
|
+
...(hole.candidates.length > 0 ? [` candidates: ${hole.candidates.join(' | ')}`] : []),
|
|
77
|
+
` constraints: ${JSON.stringify(hole.constraints)}`
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
// A group id builds the blueprint; a stored blueprint id reads it back (fills applied by the caller).
|
|
81
|
+
// options: { target, item }. Shared with `chemx heal` (heal-cli.js).
|
|
82
|
+
export const resolveBlueprint = (db, cwd, options) => {
|
|
83
|
+
const isStoredId = options.target?.startsWith('bp_');
|
|
84
|
+
if (isStoredId) return readBlueprint(db, options.target);
|
|
85
|
+
syncFingerprints(cwd, { targetDir: null, includeTests: false });
|
|
86
|
+
const grouped = runForgeGroups(cwd, {});
|
|
87
|
+
const hasRun = Boolean(grouped);
|
|
88
|
+
if (!hasRun) return { error: 'no index db here (run chemx q once to create it)' };
|
|
89
|
+
const found = options.item ? groupByItem(grouped.groups, options.item) : groupByPrefix(grouped.groups, options.target);
|
|
90
|
+
const isUnresolved = Boolean(found.error);
|
|
91
|
+
if (isUnresolved) return { error: found.error };
|
|
92
|
+
const blueprint = buildBlueprint(found.group, contextFor(db, cwd, grouped.groups));
|
|
93
|
+
saveBlueprint(db, blueprint);
|
|
94
|
+
return { blueprint };
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const fillContextOf = (db, cwd, blueprint) => {
|
|
98
|
+
const context = contextFor(db, cwd);
|
|
99
|
+
return { declaredIn: context.declaredIn, files: blueprint.locks };
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const runFill = (db, cwd, blueprint, options) => {
|
|
103
|
+
const isAnonymous = !options.agent.startsWith('@');
|
|
104
|
+
if (isAnonymous) return { ok: false, code: 1, error: 'usage: chemx blueprint fill <target> <hole>=<value> --as=@handle' };
|
|
105
|
+
const fillContext = fillContextOf(db, cwd, blueprint);
|
|
106
|
+
const results = options.pairs.map((pair) => {
|
|
107
|
+
const at = pair.indexOf('=');
|
|
108
|
+
const holeId = pair.slice(0, at);
|
|
109
|
+
const value = pair.slice(at + 1);
|
|
110
|
+
const verdict = validateFill(blueprint, holeId, value, fillContext);
|
|
111
|
+
const isValid = verdict.ok;
|
|
112
|
+
if (isValid) saveFill(db, blueprint.id, holeId, value, `human:${options.agent}`);
|
|
113
|
+
return { holeId, value, ...verdict };
|
|
114
|
+
});
|
|
115
|
+
const refused = results.filter((entry) => !entry.ok);
|
|
116
|
+
write(results.map((entry) => (entry.ok ? `filled ${entry.holeId} = ${entry.value}` : `refused ${entry.holeId}: ${entry.reason}`)));
|
|
117
|
+
return { ok: refused.length === 0, code: refused.length === 0 ? 0 : 1, results };
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const PRINTERS = {
|
|
121
|
+
holes: (blueprint) => holeLines(blueprint).join('\n'),
|
|
122
|
+
show: (blueprint, options) => (options.json ? canonicalJson(blueprint).trimEnd() : blueprintLines(blueprint).join('\n'))
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/** Runs the command; returns { ok, ... } and prints. */
|
|
126
|
+
export const runBlueprintCli = (args, cwd = process.cwd()) => {
|
|
127
|
+
const options = parseBlueprintArgs(args);
|
|
128
|
+
const hasTarget = Boolean(options.target) || Boolean(options.item);
|
|
129
|
+
const isMissingTarget = !hasTarget;
|
|
130
|
+
if (isMissingTarget) {
|
|
131
|
+
write(['usage: chemx blueprint <group-id|bp-id> | --item=<A7> [--json]', ' chemx blueprint holes <target>', ' chemx blueprint fill <target> <hole>=<value> --as=@handle']);
|
|
132
|
+
return { ok: false, code: 1, error: 'no target' };
|
|
133
|
+
}
|
|
134
|
+
const db = openIndexDb(cwd);
|
|
135
|
+
const hasDb = Boolean(db);
|
|
136
|
+
if (!hasDb) {
|
|
137
|
+
write(['blueprint: no index db here (run chemx q once to create it)']);
|
|
138
|
+
return { ok: false, code: 1, error: 'no index db' };
|
|
139
|
+
}
|
|
140
|
+
const resolved = resolveBlueprint(db, cwd, options);
|
|
141
|
+
const isFailed = Boolean(resolved.error);
|
|
142
|
+
if (isFailed) {
|
|
143
|
+
write([`blueprint: ${resolved.error}`]);
|
|
144
|
+
return { ok: false, code: 1, error: resolved.error };
|
|
145
|
+
}
|
|
146
|
+
const { blueprint } = resolved;
|
|
147
|
+
const isFill = options.sub === 'fill';
|
|
148
|
+
if (isFill) return runFill(db, cwd, blueprint, options);
|
|
149
|
+
const filled = withFills(blueprint, readFills(db, blueprint.id));
|
|
150
|
+
const text = PRINTERS[options.sub](filled, options);
|
|
151
|
+
process.stdout.write(`${text}\n`);
|
|
152
|
+
return { ok: true, blueprint: filled };
|
|
153
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Everything a blueprint reads, built once per run from the ledger (never from the clock or the input
|
|
2
|
+
// order): rows in file and line order, the tree reader the near-miss search uses, enclosing and declared
|
|
3
|
+
// names, sibling specs and the matchable library entries.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { createTreeReader } from './unit-trees.js';
|
|
7
|
+
import { createUbiquityIndex } from './gates.js';
|
|
8
|
+
import { byCodePoint } from './group-shape.js';
|
|
9
|
+
|
|
10
|
+
const byRowOrder = (a, b) => byCodePoint(a.file_path, b.file_path) || a.start_line - b.start_line || a.start - b.start || a.id - b.id;
|
|
11
|
+
|
|
12
|
+
const SPEC_SUFFIXES = ['spec', 'test'];
|
|
13
|
+
|
|
14
|
+
const siblingSpecsOf = (file) => {
|
|
15
|
+
const extension = path.posix.extname(file);
|
|
16
|
+
const stem = file.slice(0, file.length - extension.length);
|
|
17
|
+
return SPEC_SUFFIXES.map((suffix) => `${stem}.${suffix}${extension}`);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Context over a ledger ({ rows, contentHashes }). options: { root, readFile(relative) => text | null,
|
|
22
|
+
* groups (the run's accepted groups, for drift reported on overlapping groups), entries (library-match.js loadMatchableEntries), fileExists, packageRootOfFile }.
|
|
23
|
+
*/
|
|
24
|
+
export const createBlueprintContext = ({ root, ledger, groups = [], readFile, entries = [], fileExists = (relative) => fs.existsSync(path.join(root, relative)), packageRootOfFile = () => '.' }) => {
|
|
25
|
+
const rows = [...ledger.rows].sort(byRowOrder);
|
|
26
|
+
const rowsById = new Map(rows.map((row) => [row.id, row]));
|
|
27
|
+
const treeReader = createTreeReader(readFile, rows, { contentHashes: ledger.contentHashes });
|
|
28
|
+
const ubiquitousOf = createUbiquityIndex(rows);
|
|
29
|
+
const fnsByFile = new Map();
|
|
30
|
+
for (const row of rows.filter((candidate) => candidate.kind === 'fn' && candidate.decl_name)) {
|
|
31
|
+
fnsByFile.set(row.file_path, [...(fnsByFile.get(row.file_path) ?? []), row]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const enclosingNameOf = (instance) => {
|
|
35
|
+
const row = rowsById.get(instance.unitIds[0]);
|
|
36
|
+
const isNamedFunction = row?.kind === 'fn' && Boolean(row.decl_name);
|
|
37
|
+
if (isNamedFunction) return row.decl_name;
|
|
38
|
+
const around = (fnsByFile.get(instance.file) ?? []).filter((fn) => fn.start <= instance.start && fn.end >= instance.end);
|
|
39
|
+
const innermost = around.sort((a, b) => (a.end - a.start) - (b.end - b.start) || a.id - b.id)[0];
|
|
40
|
+
return innermost?.decl_name ?? null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const declaredIn = (files) => new Set(files.flatMap((file) => (fnsByFile.get(file) ?? []).map((row) => row.decl_name)));
|
|
44
|
+
|
|
45
|
+
const instancesByFile = new Map();
|
|
46
|
+
for (const group of groups) {
|
|
47
|
+
for (const instance of group.instances) instancesByFile.set(instance.file, [...(instancesByFile.get(instance.file) ?? []), { group, instance }]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const isOverlapping = (a, b) => a.file === b.file && a.startLine <= b.endLine && a.endLine >= b.startLine;
|
|
51
|
+
|
|
52
|
+
// Drift spans other accepted groups report over the same code (a window around an expression idiom
|
|
53
|
+
// sees spans the expression group's own search cannot), minus spans inside the group itself.
|
|
54
|
+
const relatedDriftOf = (group) => {
|
|
55
|
+
const others = group.instances.flatMap((instance) => (instancesByFile.get(instance.file) ?? [])
|
|
56
|
+
.filter((entry) => entry.group.id !== group.id && isOverlapping(entry.instance, instance))
|
|
57
|
+
.map((entry) => entry.group));
|
|
58
|
+
const spans = [...new Map(others.sort((a, b) => byCodePoint(a.id, b.id)).map((other) => [other.id, other])).values()]
|
|
59
|
+
.flatMap((other) => (other.drift ?? []).map((span) => ({ ...span, reason: `reported by overlapping group ${other.id}` })));
|
|
60
|
+
return spans.filter((span) => !group.instances.some((instance) => isOverlapping(span, instance)));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const coveringSpecsOf = (file) => siblingSpecsOf(file).filter(fileExists);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
root, rows, rowsById, contentHashes: ledger.contentHashes, entries, fileExists, packageRootOfFile, readFile,
|
|
67
|
+
treeOf: treeReader.treeOf, ubiquitousOf, enclosingNameOf, declaredIn, coveringSpecsOf, relatedDriftOf
|
|
68
|
+
};
|
|
69
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Members of a group whose LGG has no holes (N1 groups): what the piece must take and where the members
|
|
2
|
+
// disagree. Text-level and conservative: a name is a parameter when the home member reads it and no import
|
|
3
|
+
// or module-level declaration of its file binds it. A member that is the negation of the home member is a
|
|
4
|
+
// polarity difference, reported as behaviorDelta and a variant hole, never merged silently.
|
|
5
|
+
import { byCodePoint } from './group-shape.js';
|
|
6
|
+
|
|
7
|
+
const KEYWORDS = new Set(['await', 'break', 'case', 'catch', 'const', 'continue', 'default', 'delete', 'do', 'else', 'export', 'false', 'finally', 'for', 'function', 'if', 'in', 'instanceof', 'let', 'new', 'null', 'of', 'return', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'undefined', 'var', 'void', 'while', 'yield', 'async']);
|
|
8
|
+
const IDENTIFIER_G = /(?<![\w$.])[A-Za-z_$][\w$]*/g;
|
|
9
|
+
|
|
10
|
+
const withoutLiterals = (text) => text
|
|
11
|
+
.replace(/\/\*[\s\S]*?\*\//g, ' ')
|
|
12
|
+
.replace(/\/\/[^\n]*/g, ' ')
|
|
13
|
+
.replace(/'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`/g, '""');
|
|
14
|
+
|
|
15
|
+
const moduleBindingsOf = (fileText) => {
|
|
16
|
+
const names = new Set();
|
|
17
|
+
for (const match of fileText.matchAll(/^import\s+([^;]+?)\s+from\s+['"]/gm)) {
|
|
18
|
+
for (const part of match[1].replace(/[{}*]/g, ',').split(',')) {
|
|
19
|
+
const binding = part.trim().split(/\s+as\s+/).pop().trim();
|
|
20
|
+
const isBinding = binding !== '' && binding !== 'type';
|
|
21
|
+
if (isBinding) names.add(binding);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
for (const match of fileText.matchAll(/^(?:export\s+)?(?:async\s+)?(?:const|let|var|function\*?|class)\s+([A-Za-z_$][\w$]*)/gm)) names.add(match[1]);
|
|
25
|
+
return names;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const localsOf = (code) => {
|
|
29
|
+
const names = new Set();
|
|
30
|
+
for (const match of code.matchAll(/\b(?:const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/g)) names.add(match[1]);
|
|
31
|
+
for (const match of code.matchAll(/\(([^()]*)\)\s*=>|\b([A-Za-z_$][\w$]*)\s*=>/g)) {
|
|
32
|
+
for (const name of (match[1] ?? match[2]).split(',')) names.add(name.trim().replace(/^\.\.\./, '').split('=')[0].trim());
|
|
33
|
+
}
|
|
34
|
+
return names;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Names the member text reads but does not bind (module bindings, globals and its own locals excluded), in first-read order. */
|
|
38
|
+
export const freeNamesOf = (text, fileText = '') => {
|
|
39
|
+
const code = withoutLiterals(text);
|
|
40
|
+
const bound = new Set([...moduleBindingsOf(fileText), ...localsOf(code)]);
|
|
41
|
+
const names = [];
|
|
42
|
+
for (const [name] of code.matchAll(IDENTIFIER_G)) {
|
|
43
|
+
const isFree = !KEYWORDS.has(name) && !bound.has(name) && !(name in globalThis) && !names.includes(name);
|
|
44
|
+
const afterKey = new RegExp(`[{,]\\s*${name.replace(/\$/g, '\\$')}\\s*:`).test(code);
|
|
45
|
+
const isParam = isFree && !afterKey;
|
|
46
|
+
if (isParam) names.push(name);
|
|
47
|
+
}
|
|
48
|
+
return names;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** 'negated' when the member expression is a leading `!` form (or a chain of them), else 'plain'. */
|
|
52
|
+
export const polarityOf = (text) => (/^\s*\(*\s*!/.test(text) ? 'negated' : 'plain');
|
|
53
|
+
|
|
54
|
+
const sliceOf = (instance, context) => {
|
|
55
|
+
const fileText = context.readFile(instance.file) ?? '';
|
|
56
|
+
const hasOffsets = Number.isInteger(instance.start) && Number.isInteger(instance.end) && instance.end > instance.start;
|
|
57
|
+
return { fileText, text: hasOffsets ? fileText.slice(instance.start, instance.end) : '' };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* { params, sites, polarity, behaviorDelta } for ordered instances (the first is the home member).
|
|
62
|
+
* sites: per instance { names (its own free names), polarity }. polarity: { plain: [at], negated: [at] },
|
|
63
|
+
* behaviorDelta lists the members whose polarity differs from the majority (ties keep plain).
|
|
64
|
+
*/
|
|
65
|
+
export const deriveMembers = (instances, context) => {
|
|
66
|
+
const sites = instances.map((instance) => {
|
|
67
|
+
const { fileText, text } = sliceOf(instance, context);
|
|
68
|
+
return { at: `${instance.file}:${instance.startLine}-${instance.endLine}`, names: freeNamesOf(text, fileText), polarity: text ? polarityOf(text) : 'plain' };
|
|
69
|
+
});
|
|
70
|
+
const home = sites[0]?.names ?? [];
|
|
71
|
+
const params = home.map((name) => ({ name, kind: 'value', type: null, source: 'home-member-free-variable' }));
|
|
72
|
+
const polarity = { plain: [], negated: [] };
|
|
73
|
+
for (const site of sites) polarity[site.polarity].push(site.at);
|
|
74
|
+
polarity.plain.sort(byCodePoint);
|
|
75
|
+
polarity.negated.sort(byCodePoint);
|
|
76
|
+
const majority = polarity.negated.length > polarity.plain.length ? 'negated' : 'plain';
|
|
77
|
+
const minority = majority === 'plain' ? 'negated' : 'plain';
|
|
78
|
+
const behaviorDelta = polarity[minority].length > 0 && polarity[majority].length > 0
|
|
79
|
+
? polarity[minority].map((at) => ({ at, kind: 'polarity', detail: `member is the ${minority === 'negated' ? 'negation' : 'complement'} of the ${majority} members; calling the piece there needs a leading !` }))
|
|
80
|
+
: [];
|
|
81
|
+
return { params, sites, polarity, majority, behaviorDelta };
|
|
82
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Blueprints in index.db (design doc, Data model: blueprints, blueprint_fills) and the fill rules.
|
|
2
|
+
// A blueprint row holds the canonical JSON of the unfilled blueprint; fills are separate rows, so
|
|
3
|
+
// (blueprint, fills) is the whole input a heal replays. updated_seq is a counter, not a clock.
|
|
4
|
+
import { withIndexTransaction } from '../search-index-write.js';
|
|
5
|
+
import { canonicalJson } from './blueprint.js';
|
|
6
|
+
import { isIdentifier } from './naming.js';
|
|
7
|
+
|
|
8
|
+
const DDL = `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS blueprints (
|
|
10
|
+
id TEXT PRIMARY KEY, group_id TEXT NOT NULL, kind TEXT NOT NULL, needs TEXT NOT NULL,
|
|
11
|
+
auto_applicable INTEGER NOT NULL DEFAULT 0, json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'planned',
|
|
12
|
+
task_id INTEGER, piece_ref TEXT, updated_seq INTEGER NOT NULL DEFAULT 0
|
|
13
|
+
);
|
|
14
|
+
CREATE TABLE IF NOT EXISTS blueprint_fills (
|
|
15
|
+
blueprint_id TEXT NOT NULL, hole_id TEXT NOT NULL, value TEXT NOT NULL, filled_by TEXT NOT NULL,
|
|
16
|
+
model TEXT, valid INTEGER NOT NULL DEFAULT 1, reason TEXT, attempts INTEGER NOT NULL DEFAULT 1,
|
|
17
|
+
PRIMARY KEY (blueprint_id, hole_id)
|
|
18
|
+
);
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_blueprints_group ON blueprints(group_id);
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const SQL = {
|
|
23
|
+
nextSeq: 'SELECT COALESCE(MAX(updated_seq), 0) + 1 AS seq FROM blueprints',
|
|
24
|
+
existing: 'SELECT id FROM blueprints WHERE id = ?',
|
|
25
|
+
insert: `INSERT INTO blueprints (id, group_id, kind, needs, auto_applicable, json, status, piece_ref, updated_seq)
|
|
26
|
+
VALUES (?, ?, ?, ?, ?, ?, 'planned', ?, ?)`,
|
|
27
|
+
byPrefix: 'SELECT id, json, status FROM blueprints WHERE id LIKE ? ORDER BY id LIMIT 2',
|
|
28
|
+
fills: 'SELECT hole_id, value FROM blueprint_fills WHERE blueprint_id = ? AND valid = 1 ORDER BY hole_id',
|
|
29
|
+
fill: `INSERT INTO blueprint_fills (blueprint_id, hole_id, value, filled_by, valid, attempts)
|
|
30
|
+
VALUES (?, ?, ?, ?, 1, 1)
|
|
31
|
+
ON CONFLICT(blueprint_id, hole_id) DO UPDATE SET value = excluded.value, filled_by = excluded.filled_by,
|
|
32
|
+
valid = 1, reason = NULL, attempts = blueprint_fills.attempts + 1`
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const ensureBlueprintTables = (db) => db.exec(DDL);
|
|
36
|
+
|
|
37
|
+
/** Stores the blueprint when its id is new (a known id keeps its status, fills and task). */
|
|
38
|
+
export const saveBlueprint = (db, blueprint) => {
|
|
39
|
+
ensureBlueprintTables(db);
|
|
40
|
+
return withIndexTransaction(db, () => {
|
|
41
|
+
const isKnown = Boolean(db.prepare(SQL.existing).get(blueprint.id));
|
|
42
|
+
if (isKnown) return { id: blueprint.id, isNew: false };
|
|
43
|
+
const { seq } = db.prepare(SQL.nextSeq).get();
|
|
44
|
+
db.prepare(SQL.insert).run(blueprint.id, blueprint.group, blueprint.kind, blueprint.needs, blueprint.autoApplicable ? 1 : 0, canonicalJson(blueprint), blueprint.piece.fromPiece, seq);
|
|
45
|
+
return { id: blueprint.id, isNew: true };
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** The stored blueprint whose id starts with idOrPrefix: { blueprint, status } or { error }. */
|
|
50
|
+
export const readBlueprint = (db, idOrPrefix) => {
|
|
51
|
+
ensureBlueprintTables(db);
|
|
52
|
+
const isUsable = /^bp_[0-9a-f]{4,12}$/.test(idOrPrefix ?? '');
|
|
53
|
+
if (!isUsable) return { error: 'a blueprint id is bp_ and 4 to 12 hex characters' };
|
|
54
|
+
const rows = db.prepare(SQL.byPrefix).all(`${idOrPrefix}%`);
|
|
55
|
+
const isUnique = rows.length === 1;
|
|
56
|
+
if (isUnique) return { blueprint: JSON.parse(rows[0].json), status: rows[0].status };
|
|
57
|
+
return { error: rows.length === 0 ? `no stored blueprint ${idOrPrefix}` : `blueprint id ${idOrPrefix} is ambiguous` };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** Valid fills of a blueprint: Map holeId -> value. */
|
|
61
|
+
export const readFills = (db, blueprintId) => {
|
|
62
|
+
ensureBlueprintTables(db);
|
|
63
|
+
return new Map(db.prepare(SQL.fills).all(blueprintId).map((row) => [row.hole_id, row.value]));
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const EM_DASH = String.fromCharCode(0x2014);
|
|
67
|
+
|
|
68
|
+
const FILL_RULES = {
|
|
69
|
+
name: (hole, value, context) => {
|
|
70
|
+
const isBad = !isIdentifier(value);
|
|
71
|
+
if (isBad) return 'a name is one identifier (letters, digits, _ and $; no leading digit)';
|
|
72
|
+
const isTaken = context.declaredIn(context.files).has(value) && value !== hole.default;
|
|
73
|
+
return isTaken ? `${value} is already declared in the piece module or a member file` : null;
|
|
74
|
+
},
|
|
75
|
+
wording: (hole, value) => {
|
|
76
|
+
const limit = hole.constraints.maxLen;
|
|
77
|
+
const problems = [
|
|
78
|
+
[value.trim().length === 0, 'the wording is empty'],
|
|
79
|
+
[value.length > limit, `the wording is ${value.length} characters; the limit is ${limit}`],
|
|
80
|
+
[value.includes(EM_DASH), 'the wording has an em dash (TYPOGRAPHY_EM_DASH)']
|
|
81
|
+
];
|
|
82
|
+
return problems.find(([isProblem]) => isProblem)?.[1] ?? null;
|
|
83
|
+
},
|
|
84
|
+
decision: (hole, value) => (hole.candidates.includes(value) ? null : `choose one of: ${hole.candidates.join(', ')}`),
|
|
85
|
+
variant: (hole, value) => (value.trim().length > 0 ? null : 'the variant is empty')
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Checks a fill against its hole. context: { files (the piece module and the member files), declaredIn }.
|
|
90
|
+
* Returns { ok: true } or { ok: false, reason }; a hole kind with no rule (design) takes no fill.
|
|
91
|
+
*/
|
|
92
|
+
export const validateFill = (blueprint, holeId, value, context) => {
|
|
93
|
+
const hole = blueprint.holes.find((entry) => entry.id === holeId);
|
|
94
|
+
const isMissing = !hole;
|
|
95
|
+
if (isMissing) return { ok: false, reason: `no hole ${holeId} (holes: ${blueprint.holes.map((entry) => entry.id).join(', ')})` };
|
|
96
|
+
const rule = FILL_RULES[hole.kind];
|
|
97
|
+
const takesNoFill = !rule;
|
|
98
|
+
if (takesNoFill) return { ok: false, reason: `a ${hole.kind} hole is a design decision and takes no fill` };
|
|
99
|
+
const reason = rule(hole, value, context);
|
|
100
|
+
return reason ? { ok: false, reason } : { ok: true };
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const saveFill = (db, blueprintId, holeId, value, agent) => {
|
|
104
|
+
ensureBlueprintTables(db);
|
|
105
|
+
db.prepare(SQL.fill).run(blueprintId, holeId, value, agent);
|
|
106
|
+
};
|