feat(import): add design.md import reverse command#40
Open
zagi wants to merge 2 commits intogoogle-labs-code:mainfrom
Open
feat(import): add design.md import reverse command#40zagi wants to merge 2 commits intogoogle-labs-code:mainfrom
design.md import reverse command#40zagi wants to merge 2 commits intogoogle-labs-code:mainfrom
Conversation
Generates a DESIGN.md from an existing Node.js project by statically
analyzing its design sources. No AI, no network — deterministic code
analysis that runs in ~5ms on a clean project.
## Pipeline
detect framework → scan sources → parse → merge → emit
## What it reads
- **package.json / README.md** — project name, description, version,
and first-paragraph intro. README H1 beats package.json.name;
directory basename is the final fallback.
- **Tailwind configs** (`tailwind.config.{js,ts,cjs,mjs}`) — loaded
via dynamic import (Bun handles TS natively); `theme.extend` is
walked for colors, borderRadius, spacing, fontSize (incl. the
`[size, meta]` tuple form), and fontFamily. Regex fallback on
eval errors so malformed configs still surface their color block.
- **CSS custom properties** — both Tailwind v4 `@theme { }` blocks
(with prefix-stripping: `--color-primary` → `colors.primary`,
`--spacing-md`, `--radius-lg`, `--font-*`, `--text-*`, `--leading-*`,
`--tracking-*`, `--font-weight-*`; `--breakpoint-*` skipped) and
legacy `:root { }` blocks (name-heuristic classification).
- **DTCG tokens** (`tokens.json`, `design-tokens.json`,
`design_tokens.json`, `*.tokens.json`) — walks `$type`/`$value`;
only accepts tokens under `colors` / `spacing` / `rounded` /
`typography` top-level sections so per-component dimensions don't
pollute the scale.
## Framework detection
Cosmetic, reported in the UI. Recognizes Next, Nuxt, Vite, SvelteKit,
Remix, Astro, Create React App, Gatsby, Angular, Vue CLI, and falls
back to generic Node / unknown. Meta-frameworks beat Vite on conflicts.
## Scan hygiene
Bounded at depth 5. Skips node_modules, .git, .next, .nuxt, .output,
.svelte-kit, .turbo, build, coverage, dist. Also skips vendor trees
(public, static, vendor, vendors, third-party, third_party, bundles,
charting_library), minified/RTL stylesheets (*.min.css, *.rtl.css),
and hashed bundler output (<name>.<hash>.css) — so e.g. a bundled
TradingView charting library's 40+ v-rhythm-* tokens don't leak into
the project's own design system.
## Merge
Precedence: CSS → Tailwind → DTCG (later wins because DTCG is most
structured). Rebuilds the flat symbolTable the linter expects so the
generated state can be round-tripped through lint/export.
## Output
YAML frontmatter (name, description, colors, typography, rounded,
spacing) plus a markdown body: `# Name` heading, description, README
intro, `## Overview` (framework + counts + source summary),
per-section bullet lists of the imported tokens, and a footer
inviting the team to edit the prose. The frontmatter alone
round-trips cleanly through `lint` and back through `export`.
## CLI
design.md import <project> # writes <project>/DESIGN.md
design.md import <project> --dryRun # prints to stdout
design.md import <project> --format json # NDJSON progress events
Pretty mode renders live via Ink, showing staged progress (◐/✓/⚠/✗)
for detect → scan → parse → merge → write. JSON mode emits one
ImportStep per line on stdout for scripts and CI.
## Tests
275 passing. Unit tests cover every parser, the framework detector,
the source scanner's vendor filtering, the merger, the markdown
emitter, project metadata, and the Ink component. Integration tests
(VR-1, VR-2) round-trip examples/paws-and-paths, atmospheric-glass,
totality-festival, and three framework fixtures (Next, Vite, Nuxt)
through import → lint and assert zero linter errors.
## Build
Marks ink, react, and react-devtools-core as --external so Ink 7's
devtools import doesn't break the bundler.
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Threat model: user runs `design.md import` on a repo they don't fully
trust. Every file under the project root is attacker-controlled.
- safe-eval: replace dynamic import() with vm.runInNewContext + inert-
clone of exports (strips getters/functions/proxies) to block the
Error.prepareStackTrace realm escape. ReDoS-proof TS type stripping
(linear-time negated class; old `as` regex was O(n²), 28s on 50k
stacked casts).
- safe-write: O_NOFOLLOW + lstat + realpath containment so a planted
`DESIGN.md -> ~/.zshrc` symlink cannot redirect the write.
- source-scanner: lstatSync + skip symlinks so an `evil.tokens.json
-> /etc/passwd` symlink cannot exfiltrate host files into DESIGN.md.
- safe-json: JSON.parse reviver drops __proto__/constructor/prototype;
framework-detector builds a null-prototype deps map.
- markdown-emitter: sanitize description/README intro (collapse
newlines, escape HTML and leading `#`) and wrap README intro in a
blockquote so downstream LLM consumers attribute it to the repo.
- error-sanitize: stderr defaults to {code}-only; --verbose opts into
a path-redacted message. Redaction handles unicode, spaces, and
URLs without over-matching.
- runImport canonicalizes projectPath via realpathSync once and uses
the same root for scan + write containment (no TOCTOU split).
Red-team verified: getter RCE, symlink overwrite, symlink scan escape,
and __proto__ pollution all blocked in one malicious repo. 319 tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Generates a DESIGN.md from an existing Node.js project by statically
analyzing its design sources. No AI, no network — deterministic code
analysis that runs in ~5ms on a clean project.
Pipeline
What it reads
tailwind.config.{js,ts,cjs,mjs}) — loaded via dynamic import (Bun handles TS natively);theme.extendis walked for colors, borderRadius, spacing, fontSize (incl. the[size, meta]tuple form), and fontFamily. Regex fallback on eval errors so malformed configs still surface their color block.@theme { }blocks (with prefix-stripping:--color-primary→colors.primary,--spacing-md,--radius-lg,--font-*,--text-*,--leading-*,--tracking-*,--font-weight-*;--breakpoint-*skipped) and legacy:root { }blocks (name-heuristic classification).tokens.json,design-tokens.json,design_tokens.json,*.tokens.json) — walks$type/$value; only accepts tokens undercolors/spacing/rounded/typographytop-level sections so per-component dimensions don't pollute the scale.Framework detection
Cosmetic, reported in the UI. Recognizes Next, Nuxt, Vite, SvelteKit,
Remix, Astro, Create React App, Gatsby, Angular, Vue CLI, and falls
back to generic Node / unknown. Meta-frameworks beat Vite on conflicts.
Scan hygiene
Bounded at depth 5. Skips node_modules, .git, .next, .nuxt, .output,
.svelte-kit, .turbo, build, coverage, dist. Also skips vendor trees
(public, static, vendor, vendors, third-party, third_party, bundles,
charting_library), minified/RTL stylesheets (*.min.css, .rtl.css),
and hashed bundler output (..css) — so e.g. a bundled
TradingView charting library's 40+ v-rhythm- tokens don't leak into
the project's own design system.
Merge
Precedence: CSS → Tailwind → DTCG (later wins because DTCG is most
structured). Rebuilds the flat symbolTable the linter expects so the
generated state can be round-tripped through lint/export.
Output
YAML frontmatter (name, description, colors, typography, rounded,
spacing) plus a markdown body:
# Nameheading, description, READMEintro,
## Overview(framework + counts + source summary),per-section bullet lists of the imported tokens, and a footer
inviting the team to edit the prose. The frontmatter alone
round-trips cleanly through
lintand back throughexport.CLI
Pretty mode renders live via Ink, showing staged progress (◐/✓/⚠/✗)
for detect → scan → parse → merge → write. JSON mode emits one
ImportStep per line on stdout for scripts and CI.
Tests
275 passing. Unit tests cover every parser, the framework detector,
the source scanner's vendor filtering, the merger, the markdown
emitter, project metadata, and the Ink component. Integration tests
(VR-1, VR-2) round-trip examples/paws-and-paths, atmospheric-glass,
totality-festival, and three framework fixtures (Next, Vite, Nuxt)
through import → lint and assert zero linter errors.
Build
Marks ink, react, and react-devtools-core as --external so Ink 7's
devtools import doesn't break the bundler.