Allow context roots to be declared in .impeccable/config.json (decoupled from package managers)#307
Conversation
Greptile SummaryAdds
Confidence Score: 5/5Safe to merge — the change is additive and confined to a single new function prepended to an existing aggregator; all downstream workspace consumers are untouched. The new No files require special attention — both changed files are straightforward source and test additions. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[readWorkspacePatterns] --> B[readImpeccableContextRoots NEW]
A --> C[readPackageWorkspaces]
A --> D[readPnpmWorkspaces]
A --> E[readLernaWorkspaces]
B --> B1[Read .impeccable/config.json]
B --> B2[Read .impeccable/config.local.json]
B1 -->|contextRoots array| BM[Merge patterns]
B2 -->|contextRoots array| BM
BM --> F[Combined patterns array]
C --> F
D --> F
E --> F
F --> G{Caller}
G -->|isMonorepoRoot| H[Has any non-negated pattern - isMonorepo=true]
G -->|resolveWorkspaceProjectRoot| I[First matching pattern - projectRoot]
G -->|discoverTargetCandidates| J[Map keyed by rel path - deduplicated app list]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[readWorkspacePatterns] --> B[readImpeccableContextRoots NEW]
A --> C[readPackageWorkspaces]
A --> D[readPnpmWorkspaces]
A --> E[readLernaWorkspaces]
B --> B1[Read .impeccable/config.json]
B --> B2[Read .impeccable/config.local.json]
B1 -->|contextRoots array| BM[Merge patterns]
B2 -->|contextRoots array| BM
BM --> F[Combined patterns array]
C --> F
D --> F
E --> F
F --> G{Caller}
G -->|isMonorepoRoot| H[Has any non-negated pattern - isMonorepo=true]
G -->|resolveWorkspaceProjectRoot| I[First matching pattern - projectRoot]
G -->|discoverTargetCandidates| J[Map keyed by rel path - deduplicated app list]
Reviews (4): Last reviewed commit: "Allow context roots to be declared in .i..." | Re-trigger Greptile |
5986161 to
d044e3a
Compare
Monorepo detection previously read workspace roots only from package managers (package.json workspaces, pnpm-workspace.yaml, lerna.json), coupling "where design context lives" to the dependency graph. Add a `contextRoots` glob list to .impeccable/config.json / config.local.json so non-JS repos -- and design-context boundaries that don't match packages -- can declare nested PRODUCT.md/DESIGN.md roots directly. The new source is folded into readWorkspacePatterns(), so detection, project resolution, and the app picker pick it up unchanged. Negation and config.local.json extension work for free.
d044e3a to
944c97f
Compare
Why
After reading #300, I initially assumed that I could drop a
DESIGN.mdorPRODUCT.mdinto any folder of my project and have Impeccable treat it as a "monorepo app". Digging in, that capability is tightly coupled to package-manager configuration: a repo is only recognized as a monorepo whenpackage.jsonworkspaces,pnpm-workspace.yaml, orlerna.json(or a marker file besideapps//packages/) declares the workspaces.This PR closes that gap.
What
Adds a
contextRootsglob list to.impeccable/config.json(shared) and.impeccable/config.local.json(per-developer):{ "contextRoots": ["docs/design/throwback-jerseys/*"] }With it, a repo containing no package-manager files is still recognized as a monorepo, and each declared child resolves its own
PRODUCT.md/DESIGN.mdwith per-file fallback to the root -- identical to how package-manager workspaces already behave. Same glob syntax asworkspaces, including!negation, and it composes with package-manager workspaces rather than replacing them.How
One source added to the existing aggregator — everything else is unchanged, because detection (
isMonorepoRoot), project resolution (resolveWorkspaceProjectRoot), and the app picker (discoverTargetCandidates) all already funnel throughreadWorkspacePatterns():readImpeccableContextRoots()readscontextRootsfrom.impeccable/config.jsonthenconfig.local.json(per-dev extends shared). An.impeccableconfig without acontextRootskey changes nothing.Tests
5 new cases in
tests/context.test.mjs(full suite green — 63 pass, 0 fail):--targetresolution and run-from-inside-the-folder resolutionconfig.local.jsonextending sharedcontextRoots.impeccableconfig withoutcontextRoots(stays single-project)Generated provider output regenerated with
bun run build:skills:release.Open Question
.impeccable/config.jsonproperty naming: I went withcontextRoots, but we could also mirrorworkspaces-- or be more clear that these areadditionalContextRoots. I'm open to any preference.Note
Low Risk
Localized change to context path resolution with thorough tests; no auth, security, or data-path changes beyond how PRODUCT/DESIGN files are discovered.
Overview
Repos can be treated as Impeccable monorepos without package-manager workspace files by listing glob patterns in
contextRootson.impeccable/config.json(and extra patterns onconfig.local.json).Those globs are merged into the existing
readWorkspacePatterns()pipeline, so monorepo detection,--target/ cwd project resolution, per-childPRODUCT.md/DESIGN.mdwith root fallback, and the root app picker behave the same as for npm/pnpm/lerna workspaces. An.impeccableconfig with nocontextRootsis unchanged.Five tests cover detection without package managers, target and in-folder resolution, local config extension, non-monorepo regression, and selection listing.
Reviewed by Cursor Bugbot for commit 944c97f. Bugbot is set up for automated code reviews on this repo. Configure here.