whale-igniter 1.1.0

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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +275 -0
  3. package/dist/analyzer/imports.js +88 -0
  4. package/dist/analyzer/insights.js +276 -0
  5. package/dist/commands/add.js +36 -0
  6. package/dist/commands/adopt.js +180 -0
  7. package/dist/commands/adoptReview.js +267 -0
  8. package/dist/commands/component.js +93 -0
  9. package/dist/commands/createComponent.js +207 -0
  10. package/dist/commands/decision.js +98 -0
  11. package/dist/commands/docs.js +34 -0
  12. package/dist/commands/ignite.js +212 -0
  13. package/dist/commands/init.js +66 -0
  14. package/dist/commands/insights.js +123 -0
  15. package/dist/commands/mcp.js +106 -0
  16. package/dist/commands/refine.js +36 -0
  17. package/dist/commands/selene.js +516 -0
  18. package/dist/commands/sync.js +43 -0
  19. package/dist/commands/validate.js +48 -0
  20. package/dist/commands/watch.js +150 -0
  21. package/dist/commands/wiki.js +21 -0
  22. package/dist/generators/markdownGenerator.js +112 -0
  23. package/dist/generators/reportGenerator.js +50 -0
  24. package/dist/generators/wikiGenerator.js +365 -0
  25. package/dist/index.js +213 -0
  26. package/dist/mcp/server.js +404 -0
  27. package/dist/scanner/componentScanner.js +522 -0
  28. package/dist/scanner/foundationInferrer.js +174 -0
  29. package/dist/scanner/tailwindMapper.js +58 -0
  30. package/dist/scanner/tailwindScanner.js +186 -0
  31. package/dist/selene/apiClient.js +168 -0
  32. package/dist/selene/cache.js +68 -0
  33. package/dist/selene/clipboard.js +56 -0
  34. package/dist/selene/promptBuilder.js +229 -0
  35. package/dist/selene/providers.js +67 -0
  36. package/dist/selene/responseParser.js +149 -0
  37. package/dist/ui/atoms.js +30 -0
  38. package/dist/ui/blocks.js +208 -0
  39. package/dist/ui/capabilities.js +64 -0
  40. package/dist/ui/index.js +13 -0
  41. package/dist/ui/symbols.js +41 -0
  42. package/dist/ui/theme.js +78 -0
  43. package/dist/utils/components.js +40 -0
  44. package/dist/utils/config.js +31 -0
  45. package/dist/utils/decisions.js +32 -0
  46. package/dist/utils/paths.js +4 -0
  47. package/dist/utils/proposals.js +61 -0
  48. package/dist/utils/refinements.js +81 -0
  49. package/dist/utils/registry.js +45 -0
  50. package/dist/utils/writeJson.js +6 -0
  51. package/dist/validators/cssValidator.js +204 -0
  52. package/dist/version.js +1 -0
  53. package/docs/ROADMAP.md +206 -0
  54. package/package.json +76 -0
@@ -0,0 +1,204 @@
1
+ import fs from "fs-extra";
2
+ import { glob } from "glob";
3
+ import path from "node:path";
4
+ import postcss from "postcss";
5
+ import { loadConfig } from "../utils/config.js";
6
+ import { loadRefinements, isSuppressed } from "../utils/refinements.js";
7
+ // Properties that take spacing values (length-based, grid-relevant).
8
+ const SPACING_PROPS = new Set([
9
+ "margin", "margin-top", "margin-right", "margin-bottom", "margin-left",
10
+ "padding", "padding-top", "padding-right", "padding-bottom", "padding-left",
11
+ "gap", "row-gap", "column-gap",
12
+ "top", "right", "bottom", "left",
13
+ "width", "height", "min-width", "min-height", "max-width", "max-height"
14
+ ]);
15
+ // Heuristic: a stylesheet is "interactive" if it styles something that needs focus.
16
+ // Token sheets typically just declare custom properties; they don't need :focus-visible.
17
+ function hasInteractiveTargets(root) {
18
+ let found = false;
19
+ root.walkRules((rule) => {
20
+ const sel = rule.selector;
21
+ if (sel.includes("button") ||
22
+ sel.includes("input") ||
23
+ sel.includes("select") ||
24
+ sel.includes("textarea") ||
25
+ sel.includes("a[") ||
26
+ /\ba\b/.test(sel) ||
27
+ sel.includes("[role=") ||
28
+ sel.includes("[tabindex")) {
29
+ found = true;
30
+ return false; // stop walking
31
+ }
32
+ });
33
+ return found;
34
+ }
35
+ // A token sheet is one that mostly declares CSS custom properties at :root.
36
+ // We don't expect focus styles or grid compliance from these.
37
+ function isTokenSheet(root) {
38
+ let customProps = 0;
39
+ let otherDecls = 0;
40
+ root.walkDecls((decl) => {
41
+ if (decl.prop.startsWith("--"))
42
+ customProps++;
43
+ else
44
+ otherDecls++;
45
+ });
46
+ return customProps > 0 && customProps >= otherDecls * 2;
47
+ }
48
+ // Parse a CSS value into individual length tokens. Ignores function calls
49
+ // like url(...), var(...), calc(...) — those are handled separately.
50
+ function extractPxValues(value) {
51
+ // Strip function calls so we don't pick up px inside url("foo.png?w=10px").
52
+ const stripped = value.replace(/\b\w+\s*\([^)]*\)/g, "");
53
+ const matches = stripped.matchAll(/(-?\d+(?:\.\d+)?)px\b/g);
54
+ const out = [];
55
+ for (const m of matches) {
56
+ out.push({ raw: m[0], px: parseFloat(m[1]) });
57
+ }
58
+ return out;
59
+ }
60
+ // Extract hex colors from a value, but ignore those inside url() or var()
61
+ // (var() shouldn't contain hex but be safe). Also ignore inside strings.
62
+ function extractHexColors(value) {
63
+ // Remove function-call contents and quoted strings first.
64
+ const cleaned = value
65
+ .replace(/"[^"]*"/g, "")
66
+ .replace(/'[^']*'/g, "")
67
+ .replace(/\b\w+\s*\([^)]*\)/g, "");
68
+ const matches = cleaned.matchAll(/#[0-9a-fA-F]{3,8}\b/g);
69
+ return Array.from(matches, (m) => m[0]);
70
+ }
71
+ function getSelectorFor(node) {
72
+ let parent = node.parent;
73
+ while (parent) {
74
+ if (parent.type === "rule")
75
+ return parent.selector;
76
+ parent = parent.parent;
77
+ }
78
+ return undefined;
79
+ }
80
+ export async function validateCss(target, options = {}) {
81
+ const config = options.configOverride ?? await loadConfig(target);
82
+ const refinements = await loadRefinements(target);
83
+ const grid = config.foundations?.grid ?? 8;
84
+ const allowedRadii = [
85
+ 0,
86
+ config.foundations?.radius?.control ?? 2,
87
+ config.foundations?.radius?.container ?? 4
88
+ ];
89
+ const files = await glob("**/*.css", {
90
+ cwd: target,
91
+ absolute: true,
92
+ ignore: ["node_modules/**", "dist/**", ".git/**"]
93
+ });
94
+ const issues = [];
95
+ for (const file of files) {
96
+ const content = await fs.readFile(file, "utf8");
97
+ const relativeFile = path.relative(target, file);
98
+ let root;
99
+ try {
100
+ root = postcss.parse(content, { from: file });
101
+ }
102
+ catch (err) {
103
+ issues.push({
104
+ severity: "error",
105
+ type: "parse-error",
106
+ file: relativeFile,
107
+ line: 1,
108
+ column: 1,
109
+ message: `CSS parse failed: ${err.message}`,
110
+ suggestion: "Fix the CSS syntax."
111
+ });
112
+ continue;
113
+ }
114
+ const tokenSheet = isTokenSheet(root);
115
+ root.walkDecls((decl) => {
116
+ const line = decl.source?.start?.line ?? 1;
117
+ const column = decl.source?.start?.column ?? 1;
118
+ const selector = getSelectorFor(decl);
119
+ // Skip declarations inside custom-property definitions —
120
+ // token sheets are allowed to declare raw values.
121
+ const isCustomPropDef = decl.prop.startsWith("--");
122
+ // --- Raw hex colors ---
123
+ if (!isCustomPropDef) {
124
+ const hexes = extractHexColors(decl.value);
125
+ for (const hex of hexes) {
126
+ const issue = {
127
+ severity: "warning",
128
+ type: "raw-hex-color",
129
+ file: relativeFile,
130
+ line,
131
+ column,
132
+ message: `Raw hex color detected: ${hex}`,
133
+ suggestion: "Use a semantic CSS token (var(--color-...)) instead.",
134
+ selector
135
+ };
136
+ if (!isSuppressed(issue, refinements))
137
+ issues.push(issue);
138
+ }
139
+ }
140
+ // --- Spacing grid compliance ---
141
+ if (SPACING_PROPS.has(decl.prop) && !isCustomPropDef) {
142
+ const pxValues = extractPxValues(decl.value);
143
+ for (const { raw, px } of pxValues) {
144
+ if (px === 0)
145
+ continue;
146
+ if (px % grid !== 0) {
147
+ const nearest = Math.round(px / grid) * grid;
148
+ const issue = {
149
+ severity: "error",
150
+ type: "spacing-scale",
151
+ file: relativeFile,
152
+ line,
153
+ column,
154
+ message: `${decl.prop}: ${raw} does not follow the ${grid}px grid.`,
155
+ suggestion: `Use ${nearest}px or a spacing token.`,
156
+ selector
157
+ };
158
+ if (!isSuppressed(issue, refinements))
159
+ issues.push(issue);
160
+ }
161
+ }
162
+ }
163
+ // --- Border radius consistency ---
164
+ if (decl.prop === "border-radius" && !isCustomPropDef) {
165
+ const pxValues = extractPxValues(decl.value);
166
+ for (const { raw, px } of pxValues) {
167
+ if (!allowedRadii.includes(px)) {
168
+ const issue = {
169
+ severity: "error",
170
+ type: "radius-consistency",
171
+ file: relativeFile,
172
+ line,
173
+ column,
174
+ message: `Invalid border-radius: ${raw}.`,
175
+ suggestion: `Use ${allowedRadii.filter((r) => r > 0).join("px or ")}px (controls vs. containers).`,
176
+ selector
177
+ };
178
+ if (!isSuppressed(issue, refinements))
179
+ issues.push(issue);
180
+ }
181
+ }
182
+ }
183
+ });
184
+ // --- Focus-visible: only required on sheets with interactive targets,
185
+ // and only flagged once per sheet, not once per declaration. ---
186
+ if (!tokenSheet && hasInteractiveTargets(root) && !content.includes(":focus-visible")) {
187
+ const issue = {
188
+ severity: "warning",
189
+ type: "focus-visible",
190
+ file: relativeFile,
191
+ line: 1,
192
+ column: 1,
193
+ message: "Stylesheet has interactive elements but no :focus-visible state.",
194
+ suggestion: "Add visible keyboard focus styles for buttons/inputs/links."
195
+ };
196
+ if (!isSuppressed(issue, refinements))
197
+ issues.push(issue);
198
+ }
199
+ }
200
+ return issues;
201
+ }
202
+ export function hasErrors(issues) {
203
+ return issues.some((i) => i.severity === "error");
204
+ }
@@ -0,0 +1 @@
1
+ export const PACKAGE_VERSION = "1.1.0";
@@ -0,0 +1,206 @@
1
+ # Whale Igniter — Roadmap
2
+
3
+ The path from "a CLI that bootstraps AI context" to "the operating
4
+ system for AI-assisted product work."
5
+
6
+ This roadmap is followed in order. We don't skip versions; if a step
7
+ reveals that the next plan needs revising, we revise it before shipping.
8
+
9
+ ---
10
+
11
+ ## Shipped
12
+
13
+ ### v0.6 — Real validation + closed refinement loop
14
+
15
+ - PostCSS-based CSS validator (no regex, AST-correct).
16
+ - Foundations driven by `whale.config.json`.
17
+ - Scope-inferred refinements suppress matching issues automatically.
18
+ - Pack taxonomy: executable vs. generator.
19
+
20
+ ### v0.7 — AI context bootstrapper
21
+
22
+ - `whale ignite` in three modes (opinionated, minimal, interactive wizard).
23
+ - `CLAUDE.md` at project root + optional `AGENTS.md`, `.cursorrules`,
24
+ `.github/copilot-instructions.md`.
25
+ - Structured stores: `decisions.json`, `components.json`,
26
+ `refinements.json`.
27
+ - Auto-sync on every recorded change.
28
+
29
+ ### v0.8 — Brownfield adoption
30
+
31
+ - `whale adopt` scans React + Tailwind projects with a real AST.
32
+ - Component detection across function / arrow / forwardRef / memo /
33
+ class patterns. Entry-point names excluded.
34
+ - Tailwind class observation pipeline with variant stripping, arbitrary
35
+ values, font-size vs color disambiguation, ring-width handling.
36
+ - Foundation inference: grid via weighted coverage, radii with single
37
+ vs split detection, color palette.
38
+ - Staged proposals in `intelligence/proposed.json` with idempotent
39
+ fingerprints; interactive `adopt review` with accept/reject/edit.
40
+
41
+ ### v0.9 — Insights + generation
42
+
43
+ - `whale insights` analyzer with 7+ deterministic checks: refinement
44
+ clusters, scopeless refinements, catalog coverage, orphan
45
+ components, decision tension, token drift, grid drift.
46
+ - `whale create component <Name>` generates typed React scaffolds
47
+ respecting current foundations (control or container template).
48
+ - `--json`, `--category`, `--min-severity` for scripting / CI.
49
+
50
+ ### v0.10 — Selene MVP (prompt mode)
51
+
52
+ - `whale selene describe / audit / suggest / apply`.
53
+ - Prompt builder composes self-contained prompts with project context,
54
+ task, input, and strict output schemas.
55
+ - Robust response parser handles preambles, multiple fenced blocks,
56
+ bare JSON. Type-guarded schemas.
57
+ - Clipboard helper with cross-platform cascade (pbcopy / clip.exe /
58
+ wl-copy / xclip / xsel) and zero npm dependencies.
59
+ - Works fully offline.
60
+
61
+ ### v0.11 — Selene API mode
62
+
63
+ - Auto-detect `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` and call directly.
64
+ - Bloque `selene` en `whale.config.json` para model, temperature,
65
+ autoCall, confirmCost, noCache.
66
+ - Pre-flight cost estimation, opt-in via `--confirm-cost`.
67
+ - On-disk response cache (sha256, 7-day TTL).
68
+ - Transparent fallback to prompt mode on any API failure.
69
+
70
+ ### v1.0 — Production
71
+
72
+ - **MCP server.** Ten tools exposed over stdio: `whale_project_overview`,
73
+ `whale_list_components`, `whale_list_decisions`,
74
+ `whale_list_refinements`, `whale_validate`, `whale_insights`,
75
+ `whale_register_component`, `whale_record_decision`,
76
+ `whale_record_refinement`, `whale_sync`. Configurable for Claude Code,
77
+ Cursor, Zed, or any MCP client via `whale mcp config`.
78
+ - **File watcher.** `whale watch` regenerates CLAUDE.md and wiki on
79
+ changes to foundations or intelligence stores. Debounced, with
80
+ in-flight coalescing.
81
+ - **npm distribution.** Published as `whale-igniter`. Works with
82
+ `npx whale-igniter` or global install.
83
+ - **Production package.** Engines, license, files, .npmignore,
84
+ prepublishOnly script. README and ROADMAP refreshed.
85
+ - **Test coverage.** 44 unit tests + MCP smoke test that spawns the
86
+ server and exercises real protocol calls end-to-end.
87
+
88
+ ### v1.1 — Premium runtime UI
89
+
90
+ The terminal output became the product surface. Commands stopped reaching
91
+ for chalk directly; every command now consumes a semantic UI layer where
92
+ output describes *intent* (success, warning, section header, key/value
93
+ pair) and the active theme decides what each intent looks like.
94
+
95
+ - **Semantic UI layer in `src/ui/`.** Atoms (color roles), symbols (glyphs
96
+ with ASCII fallbacks), blocks (header, section, kv, panel, summary,
97
+ next, ok/fail/warn/note), theme dispatcher. Commands import from `ui/`
98
+ rather than chalk; chalk is now confined to the theme module.
99
+ - **Single premium theme — graphite.** Cyan accent, charcoal grays, three
100
+ text-hierarchy levels. Structure ready for v1.2's multi-theme without
101
+ touching any command.
102
+ - **Capability detection.** Auto-detects color and Unicode support; falls
103
+ back to ASCII glyphs (`*`, `v`, `x`, `->`) and plain output without a
104
+ TTY. Three env overrides — `WHALE_PLAIN`, `WHALE_UNICODE`, `NO_COLOR` —
105
+ let users force a mode in CI logs or in legacy terminals.
106
+ - **All 12 commands migrated.** ignite, adopt, adopt review, init, sync,
107
+ wiki, validate, refine, decision, component (add/list), create
108
+ component, add, docs, watch, mcp (serve/config), selene (describe /
109
+ audit / suggest / apply / status / cache). Zero `chalk` imports outside
110
+ `src/ui/theme.ts`.
111
+ - **Tests.** 14 new UI tests on top of the v1.0 suite: capability
112
+ detection, theme structure, block formatting, alignment, glyph fallback.
113
+ Total: 61 unit tests + 6 MCP smoke checks, all green.
114
+
115
+ ---
116
+
117
+ ## Next
118
+
119
+ The post-v1.1 plan is shaped by what actual users hit first. The list
120
+ below is in rough priority order, but every item will be re-evaluated
121
+ once real usage produces feedback.
122
+
123
+ ### v1.2 — Hardening from real-world usage
124
+
125
+ Now that the surface area is feature-complete, the next version should
126
+ be small and reactive. Expected items:
127
+
128
+ - Edge cases in the scanner discovered against larger repos (10k+ LOC
129
+ Next.js apps, monorepos).
130
+ - MCP server polishing: better error messages, validation of inputs
131
+ the agents pass, telemetry helpers for the user to debug.
132
+ - Pre-commit hook helper: `whale hook install` to wire up
133
+ `whale validate && whale sync` automatically.
134
+ - Selene prompt caching at the Anthropic API level (split the context
135
+ prefix as a separate system message so caching can hit).
136
+ - Optional second theme once real users have asked for it. The
137
+ architecture is ready; we're deliberately waiting for demand before
138
+ shipping more themes.
139
+
140
+ ### v1.3 — Multi-framework parsing
141
+
142
+ The Vue + Svelte parser is the largest stretch from current code. The
143
+ rest of the pipeline (foundation inference, insights, generators) is
144
+ already framework-agnostic; only the scanner needs new dialects.
145
+
146
+ ### v1.4 — Style-system breadth
147
+
148
+ CSS-in-JS (vanilla-extract, Panda CSS, styled-components), plus
149
+ Tailwind config resolution that actually evaluates the user's
150
+ `tailwind.config.{js,ts}` for accurate scales. Style Dictionary /
151
+ Figma Tokens import would slot in here too.
152
+
153
+ ### v2.0 — Team layer (uncommitted)
154
+
155
+ The decision we keep deferring: how teams collaborate around
156
+ `intelligence/`. Today it's local + git, and PRs are the workflow. At
157
+ some scale that breaks down — multiple writers, conflicting
158
+ decisions, no review surface. Possibilities:
159
+
160
+ - A "shared inbox" of proposed decisions/refinements that need
161
+ approval, separate from accepted ones.
162
+ - A web viewer for the wiki that's read-only and link-shareable.
163
+ - A schema lock that prevents PRs from contradicting active decisions
164
+ without an explicit `supersedes`.
165
+
166
+ We won't pick one until we have multiple teams telling us which one
167
+ they need most.
168
+
169
+ ---
170
+
171
+ ## What we will NOT do
172
+
173
+ These are listed explicitly to defend focus over time.
174
+
175
+ - **A web dashboard.** Whale is CLI-first. Forever.
176
+ - **A hosted SaaS.** Intelligence stays in the user's repo. No accounts.
177
+ - **Generic linting.** ESLint, Stylelint, Biome already exist. Whale's
178
+ validators only check what's tied to operational context (foundations,
179
+ refinements, decisions). Everything else is out of scope.
180
+ - **Lighthouse-style perf / SEO audits.** Different problem space. The
181
+ `lighthouse` pack name is about *guidance* (the lighthouse metaphor),
182
+ not the Google tool.
183
+ - **Project management.** No tasks, no kanban, no time tracking.
184
+
185
+ ---
186
+
187
+ ## Operational milestones (not engineering work)
188
+
189
+ These are tasks that depend on the project owner, not on the codebase:
190
+
191
+ - **Publish to npm.** Code is ready. Requires an npm account and
192
+ `npm publish` from a clean checkout.
193
+ - **Public repo.** Move from local zips to a versioned GitHub repo
194
+ with releases.
195
+ - **Demo video.** Record the 5-minute "adopt a real repo and watch
196
+ Claude Code work with full context" walkthrough referenced in the
197
+ done criteria.
198
+ - **Field test.** Adopt the tool in three real projects (own or
199
+ partner teams) and feed observations into v1.1.
200
+
201
+ Until those happen, v1.0 is "feature-complete and ready to ship" but
202
+ not "released and validated in the wild."
203
+
204
+ ---
205
+
206
+ _Last updated: v1.1 release._
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "whale-igniter",
3
+ "version": "1.1.0",
4
+ "description": "CLI-first operational intelligence. Bootstraps and adopts AI-readable project context so agents like Claude Code, Codex and Cursor understand your design system from the first commit. Includes an MCP server, a file watcher, deterministic insights, and an opt-in AI bridge (Selene).",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "whale": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE",
14
+ "docs/ROADMAP.md"
15
+ ],
16
+ "scripts": {
17
+ "dev": "tsx src/index.ts",
18
+ "build": "tsc",
19
+ "start": "node dist/index.js",
20
+ "test": "tsx tests/run.ts && tsx tests/mcp-smoke.ts",
21
+ "prepublishOnly": "npm run build && npm test"
22
+ },
23
+ "engines": {
24
+ "node": ">=20.0.0"
25
+ },
26
+ "keywords": [
27
+ "cli",
28
+ "design-system",
29
+ "ai",
30
+ "claude",
31
+ "claude-code",
32
+ "cursor",
33
+ "codex",
34
+ "mcp",
35
+ "model-context-protocol",
36
+ "tailwind",
37
+ "react",
38
+ "design-ops",
39
+ "intelligence",
40
+ "documentation",
41
+ "ai-context",
42
+ "agents-md"
43
+ ],
44
+ "author": "Whale Igniter contributors",
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+ssh://git@github.com/luisviol/whale-igniter.git"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/luisviol/whale-igniter/issues"
52
+ },
53
+ "homepage": "https://github.com/luisviol/whale-igniter#readme",
54
+ "dependencies": {
55
+ "@babel/parser": "^7.25.0",
56
+ "@babel/traverse": "^7.25.0",
57
+ "@babel/types": "^7.25.0",
58
+ "@modelcontextprotocol/sdk": "^1.29.0",
59
+ "chalk": "^5.3.0",
60
+ "commander": "^12.1.0",
61
+ "fs-extra": "^11.2.0",
62
+ "glob": "^10.4.5",
63
+ "ora": "^8.1.0",
64
+ "postcss": "^8.4.47",
65
+ "prompts": "^2.4.2",
66
+ "zod": "^4.4.3"
67
+ },
68
+ "devDependencies": {
69
+ "@types/babel__traverse": "^7.20.6",
70
+ "@types/fs-extra": "^11.0.4",
71
+ "@types/node": "^22.0.0",
72
+ "@types/prompts": "^2.4.9",
73
+ "tsx": "^4.16.0",
74
+ "typescript": "^5.5.4"
75
+ }
76
+ }