Skip to content

[Feature]: Import a design system from a local codebase into DESIGN.md #484

Open

Description

@wudilyy999

Problem / pain point

  • Every run follows the workspace DESIGN.md when there is one (apps/desktop/src/main/prompt-context.ts:426), but someone designing for an existing product has to write it by hand: the existing "Link design system repo" scan only hands the model a candidate list of unnamed values, and defers to DESIGN.md. Their colors, type, spacing and radii already live in the repo: a Tailwind config, CSS custom properties, or design-token JSON.
  • The extractors for exactly those sources exist with tests: extractFromTailwindConfig, extractFromCssVars and importDtcgJson in packages/core/src/brand/. Nothing in the app calls them. The roadmap lists this as v0.3, "Codebase → design system".
  • Claude Design builds a design system from a codebase during onboarding, so this is one of the first things people coming from it look for.

Proposed solution

An import that needs no model call: pick a folder, Open CoDesign runs the existing extractors over it and writes the result into the workspace DESIGN.md, which every run already reads. Delivered as PRs under 400 lines each:

  1. Scan. Builds on the existing "Link design system repo" picker and walk in apps/desktop/src/main/design-system.ts (read-only, skips node_modules, .git and build output, capped file count and bytes), and adds discovery of tailwind.config.{js,cjs,mjs,ts}, CSS files with :root / [data-theme] custom properties or a Tailwind v4 @theme block, and DTCG token files (*.tokens.json, tokens.json). Files are parsed as text, as the extractors already do; nothing in the repo is executed or copied. The scan module loads on first use.
  2. Map tokens to DESIGN.md. A pure function in core turns the tokens into fields validateDesignMd accepts, and reports what it could not map:
    • colors → colors. rgb(), hsl() and oklch() (Tailwind v4's default) are converted to sRGB hex. Colors with transparency, var() references that do not resolve within the scan, and other formats are reported instead of guessed.
    • spacing → spacing; radii → rounded (px, em or rem).
    • font families → typography entries (fontFamily is required there); font sizes and line heights → the Typography section; shadows → Elevation & Depth.
    • When a token appears in several themes, the :root value is used and the others are reported.
  3. Review and write. An "Import design system from a folder…" action shows the generated DESIGN.md, which file each value came from, and what was not imported. Confirming creates DESIGN.md; if one exists, the imported token fields replace its token fields and its Markdown sections are kept.
  4. Docs. A short "Import a design system" section in the website docs.

Alternatives considered

  • Asking the agent to read the repo with its file tools: costs tokens on every run, depends on the model, and the repo is usually outside the workspace, so each read needs a permission prompt. The extractors are deterministic, free and offline.
  • Evaluating tailwind.config.js to get the resolved theme: runs code from the repo. The extractors parse it as text on purpose.
  • Inferring component styles from React or Vue source: larger and model-dependent; it can build on this token import later.
  • A separate tokens file: DESIGN.md is already the design-system file that users and the agent both edit and every run reads.

Scope

Generation capability

Hard-constraints check

  • I have read the hard constraints in CLAUDE.md, and this proposal does not conflict with any of them. No new dependencies: it reuses the existing extractors, the DESIGN.md serializer uses the yaml package that packages/shared already depends on, and the color conversion is a small pure function. The scan loads on first use, reads only a folder the user picks, stores its result in the workspace DESIGN.md, and makes no network or model calls.

Activity

  1. github-actions commented on Oct 12, 2026

    @github-actions
    Contributor

    Thanks @wudilyy999 — this is an unusually well-specified feature request, and a good chunk of it is already backed by commitments written down in the repo. Some notes and a few questions before PR 1.

    Where the proposal already agrees with the project

    • CLAUDE.md (Hard constraints, §5) names codebase scan explicitly as a heavy feature that must dynamic-import on first use, so "the scan module loads on first use" is a requirement here, not a nice-to-have.
    • AGENTS.md (Hard constraints, §8) says brand values are data, not model memory, and points at DESIGN.md, user files, official CSS/SVG/screenshots, or brand URLs. A deterministic extractor run over the user's own repo is the strongest form of that.
    • AGENTS.md (Hard constraints, §4) — every design has a workspace, and the workspace filesystem is the source of truth, which is exactly why writing the result into the workspace DESIGN.md fits the existing model.
    • packages/core is the generation-orchestration package, so a pure tokens → DESIGN.md mapping function in core is the right home for step 2.
    • CLAUDE.md also requires at least one Vitest test for a new feature, and bans console.* in apps/desktop/src/main/** and packages/core/** (use getLogger() in main, the injected CoreLogger in core). Worth building the PRs with that in mind from the start.

    What I could verify, and what I could not

    I could not open apps/desktop/src/main/prompt-context.ts or packages/core/src/brand/ from the repository context available to me, so please read the two path/line references in your proposal as unverified by me rather than confirmed. That is not a blocker — just noting it so nobody mistakes this comment for an endorsement of those line numbers.

    One concrete thing I did spot: in pnpm-lock.yaml, the packages/core importer declares @mariozechner/pi-agent-core, @mariozechner/pi-ai, @mariozechner/pi-coding-agent, @open-codesign/providers, @open-codesign/shared, and @sinclair/typebox — I don't see yaml there. If the scan path does need YAML parsing, either it is coming in transitively (which should be promoted to a direct declaration) or the "no new dependencies" claim needs another look. If the extractors only do text/JSON parsing, the claim stands and the point is moot.

    Design questions worth settling before the scan PR

    1. Where does the generated DESIGN.md land? Since the workspace is the source of truth and the run reads the workspace DESIGN.md, does "Import design system from a folder…" require an already-open design, or does it create a new design/workspace first? That choice drives the whole IPC shape.
    2. Merge semantics against an existing DESIGN.md. "The imported token fields replace its token fields and its Markdown sections are kept" needs to be precise: field-level replace or whole-section replace, and what happens to a value the user hand-edited that sits inside an imported field. Silently overwriting hand-written values is the fastest way to lose trust in the feature; a visible per-field diff in step 3 seems like the safer default.
    3. oklch() → sRGB hex. Tailwind v4's default palette is wide-gamut, so some values will be out of sRGB range. Clamping silently changes the color. Worth deciding up front whether out-of-gamut values are clamped-and-reported, skipped-and-reported, or render as color(srgb …)/hex fallback. A handful of fixed test vectors would keep the conversion honest.
    4. Scan limits and var() resolution. Depth, file count, and byte caps should be named constants with tests rather than inline numbers, and "var() references that do not resolve within the scan" should be defined as resolve-within-scanned-folder or report-only.

    Housekeeping for the PR series

    The CLAUDE.md guidance applies directly: keep each PR marking all four checks in .github/PULL_REQUEST_TEMPLATE.md, add a Vitest test per piece (extractor wiring, color conversion, DESIGN.md mapping, merge behavior), and keep the scan read-only with no repo code execution — the "parse as text, don't evaluate" decision in your alternatives section is the right call and worth stating explicitly in the PR description so reviewers don't ask for a resolved-theme shortcut.

    I won't comment on roadmap sequencing — the roadmap lives outside the public checkout, so I can't verify the v0.3 reference from here.

    Need more info:

    • Are extractFromTailwindConfig, extractFromCssVars, and importDtcgJson exported from packages/core's public entry, or only reachable via deep imports? The main process importing core internals would be a design decision worth naming.
    • Which Tailwind v4 @theme subset do you plan to support (plain custom properties, --color-*/--spacing-* namespaces, @theme inline, references to other custom properties)?
    • For a folder containing several themes, is :root always the winner, or should the user pick the theme in the review step?
    • Rough PR split and order you intend to open, so review can be scheduled against a single plan instead of four independently-shaped PRs.

    Open-CoDesign Bot

  2. wudilyy999 commented on Oct 12, 2026

    @wudilyy999
    ContributorAuthor

    Thanks, this is a useful read. Two of your points change the plan; the rest are answers.

    Corrections

    • yaml (2.9.0) is declared in packages/shared, where design-md.ts uses it, and not in packages/core. The serializer that writes DESIGN.md goes next to that parser in packages/shared. The tokens → fields mapping stays a pure function in core that returns a plain object, so core gets no new dependency. I have updated the hard-constraints line in the description.
    • The three extractors are exported from packages/core/src/brand/index.ts, and packages/core/package.json exposes only .. src/index.ts does not re-export brand. PR 1 adds that re-export, so desktop main imports the public entry and not core internals.

    Answers

    1. Target. The open design's workspace. readProjectContext(workspaceRoot) reads DESIGN.md per design, so the action lives inside a design and writes <workspace>/DESIGN.md. It does not create a design.
    2. Merge. Field-level. The review step shows a per-field diff. A key that is absent from the existing DESIGN.md is added. A key whose value differs is listed as a conflict and unchecked by default, so a hand-edited value changes only when the user ticks it. The Markdown sections stay as they are.
    3. oklch(). Converted through OKLab → linear sRGB → gamma. Out-of-gamut channels are clamped to [0, 1], and the preview lists every clamped token with its original value, so no change happens silently. Fixed test vectors cover in-gamut, out-of-gamut and achromatic inputs.
    4. Limits and var(). Depth, file count and bytes are named constants with tests. A var() is resolved when the custom property is defined in a scanned file (with a cycle guard) and reported otherwise.
    5. Tailwind v4 subset (what the extractor does today). Every @theme block, @theme inline included, contributes its --* declarations, classified by property name or value into color, font size, font family, line height, radius, shadow and spacing. Values stay as written, so --color-ink: var(--color-brand) is a var() string until the mapper resolves it. v3 configs contribute the theme and theme.extend sections for colors, fontSize, fontFamily, spacing, borderRadius, boxShadow and lineHeight.
      Running the extractors on a v4 sample showed two gaps, both fixed in PR 2 with tests: --font-sans and the other v4 --font-* families are dropped (the v4 classifier matches font-family only), and a token does not record which block it came from, so a :root and a [data-theme="dark"] value with the same name look identical. PR 2 adds an optional source-block field.
    6. Several themes. :root (or the @theme block) wins and the other [data-theme] values are listed as not imported. A theme picker is outside this series.
    7. PR order.
      1. Re-export the extractors from core, plus the scan module (walk, limits, file discovery), lazy-loaded.
      2. Extractor fixes, color conversion, the tokens → fields mapping and the serializer in shared, all with tests.
      3. IPC, the preview with the per-field diff, and the write (split into IPC and renderer if it passes ~400 lines).
      4. Docs.

    PR 1 follows once this plan looks right to you.

  3. github-actions commented on Oct 12, 2026

    @github-actions
    Contributor

    Thanks — both corrections land, and the answers close out every question from the last round. What I could and could not verify from here, then what is worth carrying into the PRs.

    On the two corrections

    • yaml / packages/shared: in the pnpm-lock.yaml I can see, the packages/core importer declares @mariozechner/pi-agent-core, @mariozechner/pi-ai, @mariozechner/pi-coding-agent, @open-codesign/providers, @open-codesign/shared and @sinclair/typebox — no yaml. So your correction is consistent with the lockfile, and putting the serializer beside design-md.ts in the package that already depends on it keeps core dependency-free. I could not open the packages/shared importer or design-md.ts from the context available to me, so treat that half as your assertion rather than something I re-verified.
    • The extractors exported from packages/core/src/brand/index.ts, with packages/core/package.json exposing only .: same caveat, I cannot open those files from here. Either way, adding the re-export in PR 1 so desktop main goes through the public entry is the right shape — deep imports into packages/core/src/** are the thing to avoid.

    Worth carrying into the PRs

    1. Lazy-load boundary. CLAUDE.md hard constraint 5 names codebase scan explicitly as a feature that must dynamic-import on first use. A static brand re-export from src/index.ts is fine if the extractors are small and side-effect-free, but the scan module (walk, limits, file discovery) must not be reachable from a static top-level import — await import(...) at the main-process call site. Keeping the re-export and the scan wiring in separate commits inside PR 1 makes that boundary reviewable.
    2. Report-only paths and logging. console.* is banned in apps/desktop/src/main/** and in packages/core, packages/providers, packages/exporters and packages/shared — getLogger() in main, the injected CoreLogger in core/providers/exporters. The report-only branches are exactly where a stray log creeps in: unmapped tokens, clamped oklch(), unresolved var(), and non-:root themes. Give them a structured result the renderer renders, not a log line.
    3. PR 2 changes the output of extractors that already have tests. An optional source-block field is additive, so keep it optional and absent by default so existing callers keep their meaning, and call out in the PR description which existing test expectations move because of the --font-* classification fix. I cannot see the current tests from here.
    4. Confirm step against a file that may move. DESIGN.md is a workspace file the user and the agent both edit, and the preview and the confirm are separated in time. This repo already has a staged, conflict-checked pattern for workspace source writes (.changeset/deterministic-local-source-edits.md describes hash-bound, atomic single-file writes with conflict checks). Reusing that path, or at minimum failing loudly when the file changed since preview, fits the existing model better than a blind write.
    5. Per-PR housekeeping. At least one Vitest test per piece (extractor wiring, color conversion, tokens → fields mapping, merge), the scan read-only with no repo code execution, and all four boxes in .github/PULL_REQUEST_TEMPLATE.md marked.

    Accepted as answered

    • Target is the open design's workspace; the action writes <workspace>/DESIGN.md and does not create a design.
    • Field-level merge with a per-field diff; differing keys listed and unchecked by default; Markdown sections untouched.
    • oklch() via OKLab → linear sRGB → gamma, out-of-gamut channels clamped and every clamp listed in the preview, with fixed in-gamut / out-of-gamut / achromatic vectors. Report-not-silent is the right default; worth also keeping the original value string next to the clamped one so the preview can show exactly what changed.
    • Depth, file count and byte caps as named constants with tests; var() resolved only when the custom property is defined in a scanned file (cycle-guarded) and reported otherwise.
    • v4 subset and v3 theme / theme.extend contribution as described, with var() strings preserved until the mapper resolves them; no resolved-theme shortcut, so no repo code is evaluated.
    • :root / @theme wins, other [data-theme] values listed as not imported; no theme picker in this series.
    • PR order 1 → 4 as listed.

    Need more info:

    • Which side does the scan module live on — packages/core or apps/desktop/src/main? Reading the picked folder and owning the native dialog is main-process work, and that choice decides whether the lazy-import requirement is even visible in the diff.
    • Is PR 1's brand re-export a static export * from './brand' in src/index.ts, or a separate entry/subpath so the extractors are not pulled into every consumer of core's entry?

    PR 1 once those two are settled — this plan looks right to me.


    Open-CoDesign Bot

  4. wudilyy999 commented on Oct 13, 2026

    @wudilyy999
    ContributorAuthor

    Thanks, that settles it. Answers to the two questions, and the rest is noted for the PRs.

    • Where the scan lives. In apps/desktop/src/main, next to the IPC handler that owns the native directory dialog. Walking the picked folder is main-process work, and the handler loads the scan with await import(...) on first use, so the lazy boundary is visible at one call site. packages/core gets only the pure parts: the extractors, the color conversion and the tokens → fields mapping.
    • The re-export. Named exports in src/index.ts (the three extractor functions and the token types), not export *. The extractors are side-effect-free text parsers, so a static re-export adds nothing at startup, and the scan module is not reachable from core's entry. PR 1 keeps the re-export and the scan wiring in separate commits.
    • For the later PRs. The report-only paths return a structured result for the renderer and never log. PR 2 lists which existing test expectations move with the --font-* fix and keeps the source-block field optional. The confirm step fails loudly when DESIGN.md changed since the preview, reusing the hash-bound workspace write if it fits, and the preview shows the original and the clamped value side by side.

    I will open PR 1 next.

  5. github-actions commented on Oct 13, 2026

    @github-actions
    Contributor

    Thanks — both answers land, and the plan reads as settled from here. Nothing in the last two questions changes the review shape, so this is a sign-off on the plan rather than another round.

    Scan location (apps/desktop/src/main)

    • Consistent with the constraint chain: CLAUDE.md hard constraint 5 names codebase scan explicitly among heavy features that must dynamic-import on first use, and AGENTS.md hard constraint 6 carries the same rule for heavy features (scaffolds, skills, brand refs, image generation). Putting the walk behind await import(...) at the single IPC call site is the reviewable version of that requirement.
    • Keeping the re-export and the scan wiring in separate commits inside PR 1 is what makes the boundary checkable — one diff with both changes makes a reviewer reason about reachability instead of seeing it.
    • Worth stating outright in the PR description: the scan reads text only, executes nothing from the picked repo, and skips node_modules / .git / build output. The "parse as text, don't evaluate" decision is the one reviewers are most likely to question, so it should be visible rather than inferred.

    Re-export

    • Named exports over export * is right for the reason you gave: side-effect-free text parsers, nothing added at startup, and the scan module stays unreachable from core's entry.
    • I can confirm from pnpm-lock.yaml that the packages/core importer declares @mariozechner/pi-agent-core, @mariozechner/pi-ai, @mariozechner/pi-coding-agent, @open-codesign/providers, @open-codesign/shared and @sinclair/typebox — no yaml. So the serializer next to design-md.ts in packages/shared, with the pure tokens → fields mapping in core, keeps core dependency-free exactly as you described. I cannot re-open packages/core/package.json or src/brand/index.ts from the context I have, so the exports-map half stays your assertion — not a blocker, since the re-export goes through the public entry either way.

    Carry into the PRs

    • Logging scope. The no-console.* rule covers packages/shared alongside apps/desktop/src/main/** and packages/core/** (CLAUDE.md, Things to avoid). Since the serializer lands in shared, returning a structured result on the report-only paths — unmapped tokens, clamped oklch(), unresolved var(), non-:root themes — is what keeps both packages clean without a logging escape hatch.
    • Confirm step. .changeset/deterministic-local-source-edits.md:10 describes atomic single-file workspace writes bound to exact source hashes with conflict checks. If that helper is reusable for DESIGN.md, reusing it beats introducing a second conflict mechanism; if it is too preview-bound to reuse, failing loudly when the file changed since preview is still the right minimum.
    • PR checklist. .github/PULL_REQUEST_TEMPLATE.md:21 is the pnpm lint && pnpm typecheck && pnpm test box, and CLAUDE.md constraint 6 requires all four PRINCIPLES §5b checks marked. Note that root pnpm test runs node --test .github/scripts/deepseek-common.test.mjs before turbo run test (root package.json), so a local run covers the script tests too.
    • PR 4 target. The root package.json scripts docs:dev, docs:build and docs:preview all target the open-codesign-website package, so the "Import a design system" page belongs there.

    Nothing further from me on the design — good luck with PR 1.


    Open-CoDesign Bot

  6. wudilyy999 commented on Oct 13, 2026

    @wudilyy999
    ContributorAuthor

    One correction before PR 1. The app already has a "Link design system repo" action: the Add menu → codesign:pick-design-system-directory → scanDesignSystem in apps/desktop/src/main/design-system.ts. It walks the picked folder, regex-collects unnamed colors (up to 24), fonts, spacing, radii and shadows (up to 16 each) from the 12 highest-scored files, stores that snapshot in the app config, and formatDesignSystem adds it to every run as a "candidate design-system scan" that defers to DESIGN.md. My description did not mention it, and "has to write it by hand" overstated the gap.

    What this series adds on top: the values arrive as named, typed tokens from the existing extractors instead of unnamed value lists, and they land in <workspace>/DESIGN.md, the artifact the current scan already defers to, where the user can review and edit them.

    That changes PR 1. There is no new walker and no new picker: PR 1 reuses the existing walk and directory dialog, adds discovery of the token sources (Tailwind config, CSS with :root or @theme, DTCG JSON) next to it, and re-exports the extractors. Token extraction is loaded with await import(...) in the handler. The existing snapshot and its prompt block keep their behavior, and whether the snapshot should eventually give way to DESIGN.md is a call for you; this series does not touch it. PRs 2 to 4 are unchanged.

  7. github-actions commented on Oct 13, 2026

    @github-actions
    Contributor

    Thanks — that is a materially better description of the current state, and the revised PR 1 follows from it. A few notes on what the correction changes, then two questions.

    What the correction changes

    • PR 1 shrinks to the parts that are actually new. No walker and no picker: reuse the existing scanDesignSystem walk and the codesign:pick-design-system-directory dialog, and add only (a) token-source discovery alongside the existing file list — Tailwind config, CSS with :root / @theme, DTCG JSON — (b) the named re-exports in packages/core's entry, and (c) the await import(...) at the handler. The commit split that made the lazy boundary reviewable still applies, it just has less to split.
    • The two features write to different stores, and the PR should say so. The existing snapshot lands in app config as an app-scoped candidate value list; the new import lands in <workspace>/DESIGN.md as a design-scoped artifact. They can look like one flow in the Add menu, so naming both targets explicitly in the IPC and PR description keeps reviewers from assuming the import overwrites the snapshot (or vice versa).
    • The composition argument gets stronger, not weaker. Since formatDesignSystem already defers to DESIGN.md, imported tokens take effect through the prompt block that exists today — no change to formatDesignSystem is needed, and none is proposed. Keeping the snapshot's behavior untouched, as you describe, is the right default for this series.
    • PR 1's test surface shifts. With no new walker, the Vitest coverage that matters in PR 1 is (a) the three extractors reachable from the public packages/core entry via named exports, and (b) token-source discovery selecting the right files out of the file list the existing walk already produces. Walk limits are the existing scan's concern, not PR 1's.

    On the snapshot vs. DESIGN.md question

    That call belongs to the maintainers, not to this comment — but the smallest diff is to keep both for now, since they are scoped differently (app-level candidate values vs. per-design design system). Retiring the snapshot later is a separate change with its own issue and its own compatibility story.

    One edit worth making in the issue body

    The body still reads "has to write it by hand," which this correction shows overstates the gap. A one-line amendment acknowledging "Link design system repo" and what it already does would keep future readers from re-deriving the same correction from the comments. Same for the scope framing: the proposal is adding named, typed tokens into the artifact the existing scan already defers to, not introducing design-system awareness that does not exist.

    Need more info:

    • Does the Add menu get a second entry (and a new IPC channel) for the token import, or does the existing pick handler grow a token-import step? The target differs — app config vs. <workspace>/DESIGN.md — so which process owns the confirm step follows from this.
    • Do the token-source discovery limits reuse the existing snapshot caps you describe (24 colors, 16 formatting values, 12 scored files), or are they new named constants with their own tests? Either is fine; it just needs to be stated so reviewers do not assume the two scans share a budget.

    I can't open apps/desktop/src/main/design-system.ts from the context available to me, so I'm taking the specifics of the existing action as your read of the code rather than something I independently verified. The point about the hash-bound staged write path still holds for the confirm step in PR 3.


    Open-CoDesign Bot

  8. wudilyy999 commented on Oct 13, 2026

    @wudilyy999
    ContributorAuthor

    Thanks. Answers to the two questions, and PR 1 is open as #486.

    • Entry point and IPC. A second Add-menu entry for the import (PR 3), with its own IPC channel. "Link design system repo" and its channel keep writing the app-config snapshot and are not touched, so the two targets stay separate: the snapshot is app-scoped, and the import writes <workspace>/DESIGN.md for the open design. The main process owns the confirm step. The renderer shows the preview, and main writes only if DESIGN.md is unchanged since the preview, reusing the hash-bound workspace write if it fits and failing loudly otherwise.
    • Limits. New named constants with their own tests, not the snapshot's caps. feat(desktop): find design-token sources in a picked folder #486 has them as DEFAULT_TOKEN_SCAN_LIMITS: depth 6, 10,000 files looked at, 256,000 bytes per file. The two scans do not share a budget.

    One difference from the plan I described earlier, which #486 also states: the walk in #486 is new rather than the existing one. The existing walk stops after 160 candidate files in directory order and has no tests, so on a larger repo it can stop before it reaches the token files. The two scans share the ignore list.

  9. github-actions commented on Oct 13, 2026

    @github-actions
    Contributor

    Thanks — both answers land. One of them also moves the review surface, so most of this is about that.

    Entry point and IPC — accepted

    A second Add-menu entry with its own channel, leaving codesign:pick-design-system-directory / scanDesignSystem to keep writing the app-config snapshot, keeps the two targets separable: app-scoped candidate values vs design-scoped <workspace>/DESIGN.md. Main owning the confirm step is the right side for it, and the write contract you describe — preview in the renderer, main writes only if DESIGN.md is unchanged since the preview, hash-bound write reused where it fits, loud failure otherwise — is the right minimum. Worth stating in the PR description that the hash is captured at preview and rechecked at write, since CLAUDE.md bans synchronous I/O in the main process and the recheck has to sit on the async path.

    .changeset/deterministic-local-source-edits.md:10 is the pattern I was pointing at: "Bind edits to exact source hashes and preview revisions ... stage atomic single-file writes with conflict checks. Surface saved-but-refresh-failed warnings without rolling back later external changes." If DESIGN.md can go through that helper, reuse beats standing up a second conflict mechanism; if the helper is too preview-bound, say so explicitly in the PR.

    Limits — accepted

    New named constants with their own tests, no shared budget with the snapshot caps, is exactly what the question asked for. DEFAULT_TOKEN_SCAN_LIMITS (depth 6, 10,000 files, 256,000 bytes per file) reads as fixed policy for this series — say so outright so nobody later assumes the values are user-configurable.

    One test-shape note: the byte cap is per file and the file cap is across the walk, so pin them independently — a nested fixture for depth, a candidate set larger than the file cap, and one oversized file for the byte cap. A single test that trips whichever limit comes first will not tell them apart.

    The new walk — this is the change

    If the existing walk really stops at 160 candidate files in directory order and has no tests, a new walk is a defensible call: a token import that silently never reaches tailwind.config.ts on a large repo is a bad first impression. But it reverses the agreed PR 1 shape ("reuse the existing walk"), so three things follow:

    • Duplication is the cost, and it should be named. Two directory walkers in main, with different caps and different traversal order. Keeping the ignore list in one shared place rather than mirrored in two files is what stops them drifting apart. Converging scanDesignSystem onto the new walk later would remove both the 160-file cap and the duplication, but that is a separate change with its own compatibility story — a follow-up issue rather than a PR in this series.
    • The lazy boundary moves with the walk. CLAUDE.md hard constraint 5 names codebase scan explicitly among features that must dynamic-import on first use, and AGENTS.md hard constraint 6 repeats the rule for heavy features. The new walk, and the constants it enforces, must not be reachable from a static top-level import in main — await import(...) at the handler, with the limits living inside the lazily-loaded module so nothing drags the walk into the app-start graph. The diff will not show that on its own, so it belongs in the PR description.
    • PR 1's test surface changes. The last round's "walk limits are the existing scan's concern" no longer holds: depth, file cap, byte cap, ignore list, and token-source selection over the new walk's output are now PR 1's coverage, alongside the named packages/core re-exports reachable from the public entry.

    Still open from earlier

    The issue body still reads "has to write it by hand." Your own correction shows that overstates the gap, since codesign:pick-design-system-directory → scanDesignSystem already walks the repo and formatDesignSystem already defers to DESIGN.md. A one-line amendment keeps future readers from re-deriving that from the comment thread.

    What I could and could not verify

    • From pnpm-lock.yaml: the packages/core importer declares @mariozechner/pi-agent-core, @mariozechner/pi-ai, @mariozechner/pi-coding-agent, @open-codesign/providers, @open-codesign/shared and @sinclair/typebox — no yaml. So the serializer beside design-md.ts in packages/shared, with a plain-object mapper in core, keeps core dependency-free as described.
    • From the root package.json: test runs pnpm test:scripts && turbo run test, and test:scripts is node --test .github/scripts/deepseek-common.test.mjs, so a local pnpm test covers the script tests too. .github/PULL_REQUEST_TEMPLATE.md:21 is the pnpm lint && pnpm typecheck && pnpm test box.
    • Not verifiable from my context: apps/desktop/src/main/design-system.ts (the 160-file cap, the "no tests" claim, the channel names), PR feat(desktop): find design-token sources in a picked folder #486's diff, and the DEFAULT_TOKEN_SCAN_LIMITS values. I am treating those as your read of the code, not as things I re-checked.

    Need more info:

    • Where does the new walk live — in apps/desktop/src/main/design-system.ts beside scanDesignSystem, or a separate module — and is it only reachable behind await import(...) at the handler?
    • Does PR 1 ship any IPC yet, or is it module + re-exports only, with every channel landing in PR 3?
    • Are DEFAULT_TOKEN_SCAN_LIMITS fixed for this series, or expected to become configurable?
    • Should converging the two walks (moving scanDesignSystem onto the new walk and retiring the 160-candidate cap) be filed as a follow-up issue so this series stays additive?

    Open-CoDesign Bot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions