From edd2cb67cb2527e23846c60aa6852e6f094c6ed7 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 16 May 2026 09:25:02 +0800 Subject: [PATCH] feat: add semantic slicing skill --- .claude-plugin/marketplace.json | 6 + README.md | 2 + catalog.yaml | 7 + releases/skills.json | 6 + skills/semantic-slicing/SKILL.md | 86 +++++ skills/semantic-slicing/agents/openai.yaml | 10 + skills/semantic-slicing/assets/icon.jpg | Bin 0 -> 37951 bytes .../references/openclaw-profile.md | 59 ++++ .../references/slicing-taxonomy.md | 109 ++++++ .../semantic-slicing/references/workflow.md | 153 +++++++++ .../semantic-slicing/scripts/semantic-map.mjs | 310 ++++++++++++++++++ 11 files changed, 748 insertions(+) create mode 100644 skills/semantic-slicing/SKILL.md create mode 100644 skills/semantic-slicing/agents/openai.yaml create mode 100644 skills/semantic-slicing/assets/icon.jpg create mode 100644 skills/semantic-slicing/references/openclaw-profile.md create mode 100644 skills/semantic-slicing/references/slicing-taxonomy.md create mode 100644 skills/semantic-slicing/references/workflow.md create mode 100755 skills/semantic-slicing/scripts/semantic-map.mjs diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 9866a31..40ba377 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -38,6 +38,12 @@ "skills": "./", "description": "Create and manage worktrees safely and consistently across projects while avoiding stale branch bases." }, + { + "name": "semantic-slicing", + "source": "./skills/semantic-slicing", + "skills": "./", + "description": "Turn a large repo into reviewable semantic slices with evidence. Use code shape, threat candidates, issue clusters, and support chatter together so review budget lands on the right parts of the system." + }, { "name": "technical-deslop", "source": "./skills/technical-deslop", diff --git a/README.md b/README.md index e375ff9..9f04670 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ This is my personal **.skills** repository for Codex, Cursor, OpenClaw and agent | `ghcrawl-cluster-operator` | Operate ghcrawl local sync, clustering, and cluster inspection from the CLI. | `npx skills add vincentkoc/dotskills --skill ghcrawl-cluster-operator -y` | | `openclaw-github-dedupe` | Triage GitHub issue/PR clusters across repositories, preserving contributor credit while selecting canonical fixes and safe closures. | `npx skills add vincentkoc/dotskills --skill openclaw-github-dedupe -y` | | `operations-worktree` | Create safe git worktrees from fresh remote defaults instead of stale local branches. | `npx skills add vincentkoc/dotskills --skill operations-worktree -y` | +| `semantic-slicing` | Build local feature, threat, issue, and support maps for focused repo review. | `npx skills add vincentkoc/dotskills --skill semantic-slicing -y` | | `technical-deslop` | Ship clean diffs fast: remove AI noise and keep behavior unchanged. | `npx skills add vincentkoc/dotskills --skill technical-deslop -y` | | `technical-documentation` | Produce dev‑ready docs: clear, structured build/review for brownfield + evergreen. | `npx skills add vincentkoc/dotskills --skill technical-documentation -y` | | `technical-integrations` | Design integrations that land: vendor‑agnostic API/RFC/SDK plans with rollout safety. | `npx skills add vincentkoc/dotskills --skill technical-integrations -y` | @@ -57,6 +58,7 @@ npx skills add vincentkoc/dotskills --skill openclaw-github-dedupe -y npx skills add vincentkoc/dotskills --skill operations-worktree -y npx skills add vincentkoc/dotskills --skill crawlkit -y npx skills add vincentkoc/dotskills --skill graincrawl -y +npx skills add vincentkoc/dotskills --skill semantic-slicing -y ``` List available public skills: diff --git a/catalog.yaml b/catalog.yaml index bfb5c02..fa0721b 100644 --- a/catalog.yaml +++ b/catalog.yaml @@ -123,3 +123,10 @@ skills: source: local tags: [git, worktree, operations, workflow] version: 0.1.0 + + - id: semantic-slicing + name: Semantic Slicing + path: skills/semantic-slicing + source: local + tags: [semantic-slicing, security, review, visualization, gitcrawl, discrawl] + version: 0.1.0 diff --git a/releases/skills.json b/releases/skills.json index c277f41..b82261b 100644 --- a/releases/skills.json +++ b/releases/skills.json @@ -31,6 +31,12 @@ "description": "Create and manage worktrees safely and consistently across projects while avoiding stale branch bases.", "install": "npx skills add https://github.com/vincentkoc/dotskills --skill operations-worktree -y" }, + { + "name": "semantic-slicing", + "source": "./skills/semantic-slicing", + "description": "Turn a large repo into reviewable semantic slices with evidence. Use code shape, threat candidates, issue clusters, and support chatter together so review budget lands on the right parts of the system.", + "install": "npx skills add https://github.com/vincentkoc/dotskills --skill semantic-slicing -y" + }, { "name": "technical-deslop", "source": "./skills/technical-deslop", diff --git a/skills/semantic-slicing/SKILL.md b/skills/semantic-slicing/SKILL.md new file mode 100644 index 0000000..6b2ac4d --- /dev/null +++ b/skills/semantic-slicing/SKILL.md @@ -0,0 +1,86 @@ +--- +name: semantic-slicing +description: Build local semantic review slices by combining clawpatch feature maps, deepsec threat candidates, visual review maps, and optional gitcrawl/discrawl evidence for repos such as openclaw/openclaw. +license: AGPL-3.0-only +metadata: + source: "https://github.com/vincentkoc/dotskills" +--- + +# Semantic Slicing + +## Purpose + +Turn a large repo into reviewable semantic slices with evidence. Use code shape, threat candidates, issue clusters, and support chatter together so review budget lands on the right parts of the system. + +Default stance: map locally first, rank second, spend agent/security-review budget last. + +## When to use + +- Setting up or running `openclaw/clawpatch` against a target repo. +- Setting up or running `vercel-labs/deepsec` against a target repo. +- Producing a local visual map of feature slices, risky files, ownership clusters, or review targets. +- Cross-checking code slices against `gitcrawl` issue/PR data or `discrawl` Discord/support data. +- Planning a focused security, regression, architecture, or maintainer-review pass for a large repo. + +## Workflow + +1. Create a scratch run directory outside the target checkout, usually `~/.semantic-slicing//`. +2. Read target repo instructions before scanning. For OpenClaw, read root `AGENTS.md`; subtree guides matter when reviewing a slice. +3. Verify tool setup: + - `clawpatch`: clone/build `openclaw/clawpatch`, then run `clawpatch init`, `clawpatch map`, `clawpatch status`. + - `deepsec`: clone/build `vercel-labs/deepsec`, scaffold a scratch workspace, then run `deepsec scan`. + - `gitcrawl`: run `gitcrawl doctor --json`, then pull clusters/threads for related issue evidence. + - `discrawl`: run `discrawl doctor --json` and `discrawl status --json`; use search/digest only when support chatter is relevant. +4. Run deterministic maps before AI review: + - Clawpatch feature map for entrypoints/packages/config/test slices. + - Deepsec regex scan for candidate threat surfaces. + - Optional gitcrawl/discrawl lookups for historical pain around the same files, components, or symptoms. +5. Run `scripts/semantic-map.mjs` to merge the local artifacts into `semantic-map.html` and `semantic-map.json`. +6. Rank slices by combined signal: + - high-risk entrypoint or tool boundary, + - deepsec candidate density and slug quality, + - recent/open gitcrawl clusters, + - matching discrawl support terms, + - churn/ownership/test gaps if available. +7. Choose a cost size before running AI stages: + - `low`: deterministic maps only; no `deepsec process` or real `clawpatch review`. + - `medium`: one to three explicit files/features with high-risk slugs, batch size 1, concurrency 1, and a turn cap. + - `high`: broader AI processing or multiple feature reviews; requires an explicit budget/time decision. +8. Run AI only at the chosen size: + - `clawpatch review --feature ` or a small `--limit`. + - `deepsec process --files ` or tightly scoped `--filter` plus `--only-slugs`. +9. Report exact artifact paths, run IDs, counts, cost size, exclusions, and skipped expensive stages. + +## Inputs + +- `target_repo`: local checkout path and/or GitHub `owner/repo`. +- `scratch_root`: local artifact directory, default `~/.semantic-slicing//`. +- `clawpatch_repo`: local clone of `openclaw/clawpatch`, optional if `clawpatch` is already on PATH. +- `deepsec_repo`: local clone of `vercel-labs/deepsec`, optional if `deepsec` is already on PATH. +- `focus`: optional path prefixes, issue numbers, slugs, components, or channels to prioritize. +- `cost_size`: `low`, `medium`, or `high`; default `low`. +- `budget_mode`: `map-only`, `targeted-ai`, or `full-ai`; default follows `cost_size`. + +## Outputs + +- Tool setup status and blocker list. +- Clawpatch feature counts and contamination checks. +- Deepsec scan run ID, candidate counts, top slugs, and top files. +- Optional gitcrawl cluster/thread evidence and discrawl support evidence. +- Local visual map: `semantic-map.html` plus machine-readable `semantic-map.json`. +- Ranked slice plan with recommended next commands and cost-size rationale. + +## Guardrails + +- Keep generated artifacts out of the target repo unless the user explicitly wants checked-in config. +- Do not run full `deepsec process` or broad `clawpatch review` without an explicit high-cost decision; these can be expensive and noisy. +- Treat local nested worktrees and dot-agent folders as contamination unless intentionally in scope: `.claude/`, `.codex/`, `.agents/`, `.deepsec/`, `.semantic-slicing/`. +- If a tool maps contaminated paths, post-filter before ranking and call out the upstream limitation. +- Never paste secrets from scan outputs. Scrub absolute personal paths before external PRs/comments. +- For OpenClaw, use Testbox/Crabbox only when the task moves from mapping into validation. + +## References + +- Read `references/workflow.md` for concrete local setup and run commands. +- Read `references/slicing-taxonomy.md` when choosing slice types or map layers. +- Read `references/openclaw-profile.md` when the target is `openclaw/openclaw`. diff --git a/skills/semantic-slicing/agents/openai.yaml b/skills/semantic-slicing/agents/openai.yaml new file mode 100644 index 0000000..bcf1cfa --- /dev/null +++ b/skills/semantic-slicing/agents/openai.yaml @@ -0,0 +1,10 @@ +interface: + display_name: "Semantic Slicing" + short_description: "Build local feature, threat, issue, and support maps for focused repo review." + icon_small: "./assets/icon.jpg" + icon_large: "./assets/icon.jpg" + brand_color: "#111827" + default_prompt: "Create a semantic slice map for this repo using local clawpatch/deepsec artifacts and gitcrawl/discrawl evidence where relevant." + +policy: + allow_implicit_invocation: true diff --git a/skills/semantic-slicing/assets/icon.jpg b/skills/semantic-slicing/assets/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c8b22a372b8ed9e7701906f1712a4f0fbc5e1f0 GIT binary patch literal 37951 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 literal 0 HcmV?d00001 diff --git a/skills/semantic-slicing/references/openclaw-profile.md b/skills/semantic-slicing/references/openclaw-profile.md new file mode 100644 index 0000000..7c52caf --- /dev/null +++ b/skills/semantic-slicing/references/openclaw-profile.md @@ -0,0 +1,59 @@ +# OpenClaw Profile + +Target repo: `openclaw/openclaw`. + +## High-signal buckets + +- `src/agents/**`: agent tools, shell/process/file access, sandbox policy, session reuse. +- `src/gateway/**`: protocol, auth, WebSocket, delivery, live runtime. +- `src/plugins/**`: plugin discovery, registry, activation, manifest/public-surface loaders. +- `extensions/*`: bundled plugin runtime boundaries, channel/provider behavior. +- `packages/memory-host-sdk/**`: storage, embeddings, remote HTTP, SSRF and proxy controls. +- `scripts/**`: release, CI, Docker, package, generated contract checks. +- `ui/**` and apps: local app/browser boundary, WebView and bridge surfaces. + +## Default excludes + +Treat these as contamination unless explicitly requested: + +```text +.git/ +.claude/ +.codex/ +.agents/ +.deepsec/ +.semantic-slicing/ +node_modules/ +dist/ +build/ +coverage/ +.next/ +.turbo/ +``` + +## Local probe on 2026-05-16 + +Observed setup results on `openclaw/openclaw`: + +- `clawpatch` built locally and mapped 1,099 feature records. +- The map included hundreds of `.claude`/`.codex`/`.agents` path references even with a config exclude. Post-filtering is required before review queue ranking. +- `deepsec` built locally and scanned OpenClaw in 53.7 seconds. +- Deepsec scan run ID: `20260516011830-96433ac3b3b6762a`. +- Deepsec found 4,055 pending candidate files and 9,628 total matcher hits. +- Highest-volume slugs were `insecure-crypto`, `agent-tool-definition`, `process-env-access`, `secret-in-log`, and `spread-operator-injection`. +- `gitcrawl doctor --json` showed local OpenClaw data but the last sync was older than the current date, so use it as shortlist context and verify live state with `gh` before mutating. +- `discrawl doctor --json` was healthy in git-share mode; `discrawl status --json` showed share update needed. + +Hydrated follow-up on the same day: + +- Hydrated `deepsec` with OpenClaw-specific `INFO.md` and priority/ignore config. +- Fresh scan run ID: `20260516014350-082402b74eb441df`. +- Fresh scan found 4,050 candidate files and 9,579 total matcher hits. +- One targeted AI process pass on `src/agents/pi-embedded-runner/run/attempt.ts` produced 0 findings, cost `$4.794546`, and used 236,258 input tokens plus 6,065,152 cache-read tokens. +- That file mapped to clawpatch feature `feat_library_997fa9c066`; dry-run review returned `wouldReview: 1`. + +Operational implication: default to `low` cost sizing for maps and queue building. Use `medium` only for file-explicit high-risk slices. Treat broad `deepsec process` as `high` cost unless the user has set a clear budget. + +## OpenClaw verification rule + +Mapping is not validation. If a slice leads to a code change, follow OpenClaw repo rules for targeted tests and Testbox/Crabbox proof before handoff. diff --git a/skills/semantic-slicing/references/slicing-taxonomy.md b/skills/semantic-slicing/references/slicing-taxonomy.md new file mode 100644 index 0000000..63d2db9 --- /dev/null +++ b/skills/semantic-slicing/references/slicing-taxonomy.md @@ -0,0 +1,109 @@ +# Slicing Taxonomy + +Use multiple slice layers. One lens is too easy to game. + +## Feature slices + +Source: clawpatch feature records. + +Best for: +- package/API/plugin boundaries, +- route or command entrypoints, +- test ownership, +- review units for targeted agent passes. + +Watch for: +- hidden worktrees, +- generated output, +- overly broad source-group slices, +- missing framework-specific entrypoints. + +## Threat slices + +Source: deepsec file records and candidates. + +Best for: +- path traversal, SSRF, RCE, auth bypass, secret/log surfaces, +- high-density files, +- framework mismatch gaps where default matchers are weak. + +Watch for: +- noisy slug families like generic crypto usage, +- candidate count without exploitability, +- processing cost. + +## Issue slices + +Source: gitcrawl clusters, threads, summaries, live GitHub checks. + +Best for: +- repeated user-visible failures, +- closed-but-recurring regressions, +- duplicate PR/issue clusters, +- maintainer narrative and shipped history. + +Watch for: +- stale local crawl state, +- title similarity without root-cause match, +- closed clusters that should only inform risk, not current truth. + +## Support slices + +Source: discrawl search, digest, analytics. + +Best for: +- Discord/support symptom clusters, +- community vocabulary that differs from GitHub titles, +- production pain not yet filed as issues. + +Watch for: +- private chatter leakage, +- stale share sync, +- unstructured complaints without repro. + +## Diff slices + +Source: `git diff`, PR file lists, changed-lines metadata. + +Best for: +- PR review, +- regression-focused scans, +- verifying whether a fix touches the real symptom path. + +Watch for: +- tests/docs-only changes that should not expand into a full security scan, +- moved files that break naive path matching. + +## Runtime/import slices + +Source: import graph, startup profiles, package manifests, plugin manifests. + +Best for: +- hot-path performance, +- lazy-loading regressions, +- plugin/core boundary leakage, +- package/dependency ownership. + +Watch for: +- circular imports, +- static+dynamic imports of the same heavy module, +- core/plugin boundary violations. + +## Visual review map + +Map each bucket with: +- feature count, +- deepsec candidate count, +- top slugs, +- top files, +- gitcrawl cluster count, +- discrawl hit count, +- contamination count. + +Recommended ranking: + +```text +score = entrypoint_weight + threat_density + issue_signal + support_signal + churn_signal - contamination_penalty +``` + +The score is a review queue, not a bug claim. diff --git a/skills/semantic-slicing/references/workflow.md b/skills/semantic-slicing/references/workflow.md new file mode 100644 index 0000000..1eafd12 --- /dev/null +++ b/skills/semantic-slicing/references/workflow.md @@ -0,0 +1,153 @@ +# Local Semantic Workflow + +## Scratch layout + +Use a run directory outside the target checkout: + +```bash +RUN_ROOT="$HOME/.semantic-slicing/openclaw/$(date +%Y%m%d-%H%M%S)" +mkdir -p "$RUN_ROOT" +``` + +Recommended layout: + +```text +/ + clawpatch/ + deepsec/ + semantic-map.html + semantic-map.json +``` + +## Clawpatch + +Setup from source: + +```bash +git clone https://github.com/openclaw/clawpatch.git ~/GIT/_Perso/clawpatch +cd ~/GIT/_Perso/clawpatch +pnpm install +pnpm build +``` + +Run against a target repo: + +```bash +node ~/GIT/_Perso/clawpatch/dist/cli.js \ + --root ~/GIT/_Perso/openclaw \ + --state-dir "$RUN_ROOT/clawpatch" \ + init --json + +node ~/GIT/_Perso/clawpatch/dist/cli.js \ + --root ~/GIT/_Perso/openclaw \ + --state-dir "$RUN_ROOT/clawpatch" \ + map --json + +node ~/GIT/_Perso/clawpatch/dist/cli.js \ + --root ~/GIT/_Perso/openclaw \ + --state-dir "$RUN_ROOT/clawpatch" \ + status --json +``` + +After mapping, check contamination: + +```bash +find "$RUN_ROOT/clawpatch/features" -type f -print0 | + xargs -0 jq -r '.ownedFiles[].path, .entrypoints[].path, .contextFiles[].path' | + rg '^\.(claude|codex|agents|deepsec|semantic-slicing)/' | wc -l +``` + +If contamination is non-zero, post-filter before ranking. Current clawpatch may still seed hidden local worktree paths even when config excludes are present. + +## Deepsec + +Setup from source: + +```bash +git clone https://github.com/vercel-labs/deepsec.git ~/GIT/_Perso/deepsec +cd ~/GIT/_Perso/deepsec +pnpm install +pnpm -r build +pnpm bundle +``` + +Create a scratch workspace and link the local build: + +```bash +node ~/GIT/_Perso/deepsec/packages/deepsec/dist/cli.mjs \ + init "$RUN_ROOT/deepsec" ~/GIT/_Perso/openclaw --id openclaw --force + +cd "$RUN_ROOT/deepsec" +pnpm add -w "deepsec@file:$HOME/GIT/_Perso/deepsec/packages/deepsec" +``` + +Run deterministic scan: + +```bash +node ~/GIT/_Perso/deepsec/packages/deepsec/dist/cli.mjs scan --project-id openclaw +node ~/GIT/_Perso/deepsec/packages/deepsec/dist/cli.mjs status --project-id openclaw +node ~/GIT/_Perso/deepsec/packages/deepsec/dist/cli.mjs metrics --project-id openclaw +``` + +Do not run `process` blindly on large candidate sets. Size the run first: + +| Size | Use when | Shape | +| --- | --- | --- | +| `low` | You need a review map or queue only. | `clawpatch map`, `deepsec scan`, `semantic-map.mjs`; no AI processing. | +| `medium` | A slice has strong threat + feature overlap. | 1-3 files/features, high-risk slugs only, `--batch-size 1`, `--concurrency 1`, capped turns. | +| `high` | The user explicitly wants broad AI review and accepts cost/time. | Multiple features or wider filters; record budget, run IDs, and stopping condition first. | + +Prefer file-explicit processing after ranking: + +```bash +pnpm deepsec process --project-id openclaw \ + --files src/agents/pi-tools.read.ts,src/agents/sandbox/ssh-backend.ts \ + --only-slugs path-traversal,rce,ssrf,auth-bypass,missing-auth,secret-in-log \ + --batch-size 1 \ + --concurrency 1 \ + --max-turns 40 +``` + +If you only need matcher-level narrowing: + +```bash +pnpm deepsec scan --project-id openclaw --matchers path-traversal,rce,ssrf +``` + +## Gitcrawl + +Use gitcrawl as issue/PR memory, not live truth: + +```bash +gitcrawl doctor --json +gitcrawl clusters openclaw/openclaw --min-size 2 --limit 20 --sort size --json +gitcrawl threads openclaw/openclaw --numbers 123,456 --include-closed --json +``` + +For freshness, re-check decisive open/closed/merged state with `gh` before mutation. + +## Discrawl + +Use discrawl for support/channel evidence when user reports mention Discord, support chatter, or community symptoms: + +```bash +discrawl doctor --json +discrawl status --json +discrawl search "gateway auth" --limit 25 --json +discrawl digest --help +``` + +Avoid pulling personal or unrelated message content into reports. Summarize only the symptom evidence needed to rank the slice. + +## Visual map + +Generate the local review map: + +```bash +node /path/to/semantic-slicing/scripts/semantic-map.mjs \ + --clawpatch "$RUN_ROOT/clawpatch" \ + --deepsec "$RUN_ROOT/deepsec/data/openclaw" \ + --out "$RUN_ROOT/semantic-map.html" +``` + +The script writes both HTML and JSON. Use the HTML for review and the JSON for follow-up automation. diff --git a/skills/semantic-slicing/scripts/semantic-map.mjs b/skills/semantic-slicing/scripts/semantic-map.mjs new file mode 100755 index 0000000..11f273c --- /dev/null +++ b/skills/semantic-slicing/scripts/semantic-map.mjs @@ -0,0 +1,310 @@ +#!/usr/bin/env node +import fs from "node:fs"; +import path from "node:path"; + +const args = parseArgs(process.argv.slice(2)); +if (!args.clawpatch && !args.deepsec) { + die("usage: semantic-map.mjs --clawpatch --deepsec --out "); +} + +const outPath = args.out ?? path.resolve(process.cwd(), "semantic-map.html"); +const contaminationPrefixes = [ + ".git/", + ".claude/", + ".codex/", + ".agents/", + ".deepsec/", + ".semantic-slicing/", + "node_modules/", + "dist/", + "build/", + "coverage/", + ".next/", + ".turbo/", +]; + +const features = args.clawpatch ? readClawpatchFeatures(args.clawpatch) : []; +const deepsecFiles = args.deepsec ? readDeepsecFiles(args.deepsec) : []; +const buckets = new Map(); +const slugCounts = new Map(); +const topFiles = []; + +for (const feature of features) { + const refs = [ + ...(feature.ownedFiles ?? []), + ...(feature.entrypoints ?? []), + ...(feature.contextFiles ?? []), + ].map((item) => item.path).filter(Boolean); + const contaminated = refs.filter(isContaminated); + const cleanRefs = refs.filter((item) => !isContaminated(item)); + if (cleanRefs.length === 0 && contaminated.length > 0) continue; + const bucket = bucketFor(preferredFeaturePath(feature, cleanRefs)); + const record = ensureBucket(buckets, bucket); + record.features += 1; + record.kinds[feature.kind ?? "unknown"] = (record.kinds[feature.kind ?? "unknown"] ?? 0) + 1; + record.sources[feature.source ?? "unknown"] = (record.sources[feature.source ?? "unknown"] ?? 0) + 1; + record.contaminatedRefs += contaminated.length; + if (feature.featureId && record.featureIds.length < 12) record.featureIds.push(feature.featureId); +} + +for (const file of deepsecFiles) { + if (isContaminated(file.filePath)) continue; + const candidates = Array.isArray(file.candidates) ? file.candidates : []; + if (candidates.length === 0) continue; + const bucket = bucketFor(file.filePath); + const record = ensureBucket(buckets, bucket); + record.deepsecFiles += 1; + record.deepsecCandidates += candidates.length; + for (const candidate of candidates) { + const slug = candidate.vulnSlug ?? candidate.slug ?? "unknown"; + record.slugs[slug] = (record.slugs[slug] ?? 0) + 1; + slugCounts.set(slug, (slugCounts.get(slug) ?? 0) + 1); + } + topFiles.push({ + path: file.filePath, + bucket, + candidates: candidates.length, + slugs: [...new Set(candidates.map((item) => item.vulnSlug ?? item.slug ?? "unknown"))].slice(0, 8), + }); +} + +const bucketRows = [...buckets.values()] + .map((bucket) => ({ + ...bucket, + score: bucket.features + bucket.deepsecCandidates + bucket.deepsecFiles * 3 - bucket.contaminatedRefs, + topSlugs: topEntries(bucket.slugs, 8), + topKinds: topEntries(bucket.kinds, 5), + topSources: topEntries(bucket.sources, 5), + })) + .sort((a, b) => b.score - a.score || b.deepsecCandidates - a.deepsecCandidates || a.name.localeCompare(b.name)); + +topFiles.sort((a, b) => b.candidates - a.candidates || a.path.localeCompare(b.path)); + +const summary = { + generatedAt: new Date().toISOString(), + inputs: { + clawpatch: args.clawpatch ?? null, + deepsec: args.deepsec ?? null, + }, + totals: { + buckets: bucketRows.length, + features: bucketRows.reduce((sum, item) => sum + item.features, 0), + deepsecFiles: bucketRows.reduce((sum, item) => sum + item.deepsecFiles, 0), + deepsecCandidates: bucketRows.reduce((sum, item) => sum + item.deepsecCandidates, 0), + contaminatedRefs: bucketRows.reduce((sum, item) => sum + item.contaminatedRefs, 0), + }, + topSlugs: topEntries(Object.fromEntries(slugCounts), 20), + buckets: bucketRows, + topFiles: topFiles.slice(0, 100), +}; + +fs.mkdirSync(path.dirname(outPath), { recursive: true }); +fs.writeFileSync(outPath, renderHtml(summary), "utf8"); +fs.writeFileSync(outPath.replace(/\.html?$/u, "") + ".json", JSON.stringify(summary, null, 2) + "\n", "utf8"); +console.log(`wrote ${outPath}`); +console.log(`wrote ${outPath.replace(/\.html?$/u, "")}.json`); + +function parseArgs(argv) { + const out = {}; + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (!arg.startsWith("--")) die(`unexpected argument: ${arg}`); + const key = arg.slice(2); + const value = argv[index + 1]; + if (!value || value.startsWith("--")) die(`missing value for ${arg}`); + out[key] = value; + index += 1; + } + return out; +} + +function readClawpatchFeatures(stateDir) { + const featureDir = path.join(stateDir, "features"); + if (!fs.existsSync(featureDir)) return []; + return readJsonFiles(featureDir); +} + +function readDeepsecFiles(projectDataDir) { + const fileDir = path.join(projectDataDir, "files"); + if (!fs.existsSync(fileDir)) return []; + return readJsonFiles(fileDir); +} + +function readJsonFiles(dir) { + const files = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...readJsonFiles(full)); + } else if (entry.isFile() && entry.name.endsWith(".json")) { + try { + files.push(JSON.parse(fs.readFileSync(full, "utf8"))); + } catch { + // Keep map generation best-effort; corrupt records are skipped. + } + } + } + return files; +} + +function bucketFor(filePath) { + const normalized = String(filePath).replaceAll("\\", "/").replace(/^\.?\//u, ""); + const parts = normalized.split("/"); + if (parts[0] === "extensions" && parts[1]) return `extensions/${parts[1]}`; + if (parts[0] === "packages" && parts[1]) return `packages/${parts[1]}`; + if (parts[0] === "apps" && parts[1]) return `apps/${parts[1]}`; + if (parts[0] === "src" && parts[1]) return `src/${parts[1]}`; + if (parts[0] === "ui") return parts[1] === "src" && parts[2] ? `ui/${parts[2]}` : "ui"; + if (parts[0] === "scripts") return "scripts"; + if (parts[0] === "docs") return "docs"; + return parts[0] || "unknown"; +} + +function preferredFeaturePath(feature, cleanRefs) { + const nonManifest = cleanRefs.find((item) => path.basename(item) !== "package.json"); + if (nonManifest) return nonManifest; + const titlePath = titlePathHint(feature?.title); + if (titlePath) return titlePath; + return cleanRefs[0] ?? feature?.title ?? "unknown"; +} + +function titlePathHint(title) { + if (!title) return null; + const match = String(title).match(/\b(?:Node source|Project config|Package script|CLI command)\s+([^#(]+)/u); + if (!match) return null; + return match[1].trim(); +} + +function ensureBucket(map, name) { + if (!map.has(name)) { + map.set(name, { + name, + features: 0, + deepsecFiles: 0, + deepsecCandidates: 0, + contaminatedRefs: 0, + kinds: {}, + sources: {}, + slugs: {}, + featureIds: [], + }); + } + return map.get(name); +} + +function isContaminated(filePath) { + const normalized = String(filePath).replaceAll("\\", "/").replace(/^\.?\//u, ""); + return contaminationPrefixes.some((prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)); +} + +function topEntries(object, limit) { + return Object.entries(object) + .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])) + .slice(0, limit) + .map(([name, count]) => ({ name, count })); +} + +function renderHtml(summary) { + const maxScore = Math.max(...summary.buckets.map((item) => item.score), 1); + const nodes = summary.buckets.slice(0, 32).map((bucket, index) => { + const col = index % 8; + const row = Math.floor(index / 8); + const radius = 18 + Math.round((bucket.score / maxScore) * 34); + return { + ...bucket, + x: 70 + col * 135, + y: 75 + row * 125, + radius, + }; + }); + const width = 1080; + const height = Math.max(260, 135 + Math.ceil(nodes.length / 8) * 125); + return ` + + + + +Semantic Slice Map + + + +
+

Semantic Slice Map

+
Generated ${escapeHtml(summary.generatedAt)}
+
+${card("Buckets", summary.totals.buckets)} +${card("Features", summary.totals.features)} +${card("Deepsec files", summary.totals.deepsecFiles)} +${card("Candidates", summary.totals.deepsecCandidates)} +
+

Review Map

+ +${nodes.map((node) => ` 100 ? "mid" : ""}" cx="${node.x}" cy="${node.y}" r="${node.radius}">${escapeHtml(node.name)}: score ${node.score}, features ${node.features}, candidates ${node.deepsecCandidates} +${escapeHtml(shortLabel(node.name, 18))} +${node.features}f / ${node.deepsecCandidates}c`).join("\n")} + +

Top Buckets

+ + + +${summary.buckets.slice(0, 40).map((bucket) => ``).join("\n")} + +
BucketScoreFeaturesFilesCandidatesTop slugs
${escapeHtml(bucket.name)}${bucket.score}${bucket.features}${bucket.deepsecFiles}${bucket.deepsecCandidates}${pills(bucket.topSlugs)}
+

Top Files

+ + + +${summary.topFiles.slice(0, 50).map((file) => ``).join("\n")} + +
FileBucketCandidatesSlugs
${escapeHtml(file.path)}${escapeHtml(file.bucket)}${file.candidates}${file.slugs.map((slug) => `${escapeHtml(slug)}`).join("")}
+
+ +`; +} + +function card(label, value) { + return `
${Number(value).toLocaleString()}${escapeHtml(label)}
`; +} + +function pills(entries) { + return entries.map((entry) => `${escapeHtml(entry.name)} ${entry.count}`).join(""); +} + +function shortLabel(value, limit) { + return value.length > limit ? `${value.slice(0, limit - 1)}…` : value; +} + +function escapeHtml(value) { + return String(value) + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """); +} + +function die(message) { + console.error(message); + process.exit(2); +}