whale-igniter 1.3.3 → 1.3.4

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.
@@ -0,0 +1,86 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import { resolveTarget } from "../utils/paths.js";
4
+ import { generateWiki } from "../generators/wikiGenerator.js";
5
+ import { ui } from "../ui/index.js";
6
+ const COLOR_ALIASES = {
7
+ black: { base: "#111827", hover: "#000000" },
8
+ slate: { base: "#334155", hover: "#0f172a" },
9
+ cyan: { base: "#0891b2", hover: "#0e7490" },
10
+ blue: { base: "#2563eb", hover: "#1d4ed8" },
11
+ green: { base: "#16a34a", hover: "#15803d" },
12
+ red: { base: "#dc2626", hover: "#b91c1c" }
13
+ };
14
+ export async function updateTokensCommand(options = {}) {
15
+ const target = resolveTarget();
16
+ const fileRel = options.file ?? "src/styles.css";
17
+ const fileAbs = path.join(target, fileRel);
18
+ if (!options.buttonColor) {
19
+ console.log(ui.fail("Pass --button-color <name|hex>. Example: whale update tokens --button-color black"));
20
+ process.exitCode = 1;
21
+ return;
22
+ }
23
+ if (!(await fs.pathExists(fileAbs))) {
24
+ console.log(ui.fail(`${ui.path(fileRel)} was not found.`));
25
+ console.log(ui.muted("Run `whale create landing --sections hero,features,proof,contact` first, or pass --file <path>."));
26
+ process.exitCode = 1;
27
+ return;
28
+ }
29
+ const colors = resolveColors(options.buttonColor, options.hoverColor);
30
+ if (!colors) {
31
+ console.log(ui.fail("Color must be a known name or a hex value like #111827."));
32
+ console.log(ui.muted(`Known names: ${Object.keys(COLOR_ALIASES).join(", ")}`));
33
+ process.exitCode = 1;
34
+ return;
35
+ }
36
+ const before = await fs.readFile(fileAbs, "utf8");
37
+ let after = upsertCssCustomProperty(before, "--color-accent", colors.base);
38
+ after = upsertCssCustomProperty(after, "--color-accent-strong", colors.hover);
39
+ if (after === before) {
40
+ console.log(ui.muted("No token changes needed."));
41
+ return;
42
+ }
43
+ await fs.writeFile(fileAbs, after, "utf8");
44
+ console.log(ui.ok(`Updated button tokens in ${ui.path(fileRel)}`));
45
+ console.log(` ${ui.kv("--color-accent", colors.base, { keyWidth: 22 })}`);
46
+ console.log(` ${ui.kv("--color-accent-strong", colors.hover, { keyWidth: 22 })}`);
47
+ if (!options.noSync) {
48
+ await generateWiki(target);
49
+ console.log(ui.muted(`${ui.glyph.check} AI context updated`));
50
+ }
51
+ }
52
+ function resolveColors(buttonColor, hoverColor) {
53
+ const base = resolveColor(buttonColor);
54
+ const hover = hoverColor ? resolveColor(hoverColor) : undefined;
55
+ if (!base || (hoverColor && !hover))
56
+ return null;
57
+ const alias = COLOR_ALIASES[buttonColor.trim().toLowerCase()];
58
+ return {
59
+ base,
60
+ hover: hover ?? alias?.hover ?? base
61
+ };
62
+ }
63
+ function resolveColor(input) {
64
+ const normalized = input.trim().toLowerCase();
65
+ const alias = COLOR_ALIASES[normalized];
66
+ if (alias)
67
+ return alias.base;
68
+ if (/^#[0-9a-f]{3}(?:[0-9a-f]{3})?(?:[0-9a-f]{2})?$/i.test(input.trim())) {
69
+ return input.trim();
70
+ }
71
+ return null;
72
+ }
73
+ function upsertCssCustomProperty(css, property, value) {
74
+ const propertyPattern = new RegExp(`(${escapeRegExp(property)}\\s*:\\s*)[^;]+;`);
75
+ if (propertyPattern.test(css)) {
76
+ return css.replace(propertyPattern, `$1${value};`);
77
+ }
78
+ const rootPattern = /:root\s*\{/;
79
+ if (rootPattern.test(css)) {
80
+ return css.replace(rootPattern, `:root {\n ${property}: ${value};`);
81
+ }
82
+ return `:root {\n ${property}: ${value};\n}\n\n${css}`;
83
+ }
84
+ function escapeRegExp(input) {
85
+ return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
86
+ }
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import { adoptReviewCommand, adoptStatusCommand } from "./commands/adoptReview.j
15
15
  import { insightsCommand, insightsDriftReviewCommand } from "./commands/insights.js";
16
16
  import { createComponentCommand } from "./commands/createComponent.js";
17
17
  import { createLandingCommand } from "./commands/createLanding.js";
18
+ import { updateTokensCommand } from "./commands/updateTokens.js";
18
19
  import { seleneDescribeCommand, seleneAuditCommand, seleneSuggestCommand, seleneApplyCommand, seleneStatusCommand, seleneCacheClearCommand } from "./commands/selene.js";
19
20
  import { mcpServeCommand, mcpConfigCommand } from "./commands/mcp.js";
20
21
  import { watchCommand } from "./commands/watch.js";
@@ -226,6 +227,17 @@ component
226
227
  .command("list")
227
228
  .description("List components registered in the catalog.")
228
229
  .action(componentListCommand);
230
+ const update = program
231
+ .command("update")
232
+ .description("Apply deterministic updates to project files.");
233
+ update
234
+ .command("tokens")
235
+ .description("Update generated CSS design tokens, such as button colors.")
236
+ .option("--button-color <nameOrHex>", "Button color name or hex (black, cyan, blue, green, red, slate, #111827)")
237
+ .option("--hover-color <nameOrHex>", "Optional hover color name or hex")
238
+ .option("--file <path>", "CSS file relative to the project root (default: src/styles.css)")
239
+ .option("--no-sync", "Don't regenerate CLAUDE.md / wiki after updating")
240
+ .action((opts) => updateTokensCommand(opts));
229
241
  const team = program
230
242
  .command("team")
231
243
  .description("Manage your AI team (friendly layer for operational packs).");
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = "1.3.3";
1
+ export const PACKAGE_VERSION = "1.3.4";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whale-igniter",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
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
5
  "type": "module",
6
6
  "main": "./dist/index.js",