@ijfw/memory-server 1.3.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 (106) hide show
  1. package/bin/ijfw +27 -0
  2. package/bin/ijfw-dashboard +180 -0
  3. package/bin/ijfw-dispatch-plan +41 -0
  4. package/bin/ijfw-memorize +273 -0
  5. package/bin/ijfw-memory +51 -0
  6. package/fixtures/demo-target.js +28 -0
  7. package/package.json +53 -0
  8. package/src/api-client.js +190 -0
  9. package/src/audit-roster.js +315 -0
  10. package/src/caps.js +37 -0
  11. package/src/cold-scan-runner.mjs +37 -0
  12. package/src/compute/edges.js +155 -0
  13. package/src/compute/extract.js +560 -0
  14. package/src/compute/fts5.js +420 -0
  15. package/src/compute/graph-auto-index.js +191 -0
  16. package/src/compute/graph-lock.js +114 -0
  17. package/src/compute/index.js +18 -0
  18. package/src/compute/migration-runner.js +116 -0
  19. package/src/compute/migrations/001-initial.js +23 -0
  20. package/src/compute/migrations/002-porter-stemming-source.js +139 -0
  21. package/src/compute/migrations/003-tier-semantic.js +69 -0
  22. package/src/compute/migrations/004-kg-tables.js +83 -0
  23. package/src/compute/migrations/005-stale-candidate.js +72 -0
  24. package/src/compute/python-resolver.js +106 -0
  25. package/src/compute/runner-vm.js +185 -0
  26. package/src/compute/runner.js +416 -0
  27. package/src/compute/sandbox-detect.js +122 -0
  28. package/src/compute/sandbox-linux.js +164 -0
  29. package/src/compute/sandbox-macos.js +167 -0
  30. package/src/compute/sandbox-windows.js +63 -0
  31. package/src/compute/schema.sql +118 -0
  32. package/src/compute/staleness.js +239 -0
  33. package/src/compute/synonyms.js +367 -0
  34. package/src/compute/traverse.js +180 -0
  35. package/src/cost/aggregator.js +229 -0
  36. package/src/cost/pricing.js +134 -0
  37. package/src/cost/readers/claude.js +179 -0
  38. package/src/cost/readers/codex.js +131 -0
  39. package/src/cost/readers/gemini.js +111 -0
  40. package/src/cost/savings.js +243 -0
  41. package/src/cross-dispatcher.js +437 -0
  42. package/src/cross-orchestrator-cli.js +1885 -0
  43. package/src/cross-orchestrator.js +598 -0
  44. package/src/cross-project-search.js +114 -0
  45. package/src/dashboard-client.html +1180 -0
  46. package/src/dashboard-server.js +895 -0
  47. package/src/design-companion.js +81 -0
  48. package/src/dispatch/colon-syntax.js +732 -0
  49. package/src/dispatch-planner.js +235 -0
  50. package/src/dream/cooldown.js +105 -0
  51. package/src/dream/runner.mjs +373 -0
  52. package/src/dream/staleness-wiring.js +195 -0
  53. package/src/feedback-detector.js +57 -0
  54. package/src/hero-line.js +115 -0
  55. package/src/importers/claude-mem.js +152 -0
  56. package/src/importers/cli.js +311 -0
  57. package/src/importers/common.js +84 -0
  58. package/src/importers/discover.js +235 -0
  59. package/src/importers/rtk.js +107 -0
  60. package/src/intent-router.js +221 -0
  61. package/src/lib/atomic-io.js +201 -0
  62. package/src/lib/cache.js +33 -0
  63. package/src/lib/npm-view.js +104 -0
  64. package/src/lib/status-card.js +95 -0
  65. package/src/lib/token.js +85 -0
  66. package/src/memory/fts5.js +349 -0
  67. package/src/memory/migration-runner.js +116 -0
  68. package/src/memory/migrations/001-fts5-init.js +26 -0
  69. package/src/memory/migrations/002-tier-semantic.js +60 -0
  70. package/src/memory/migrations/003-stale-candidate.js +60 -0
  71. package/src/memory/reader.js +300 -0
  72. package/src/memory/recall-counter.js +76 -0
  73. package/src/memory/schema.sql +79 -0
  74. package/src/memory/search.js +431 -0
  75. package/src/memory/staleness.js +237 -0
  76. package/src/memory/tier-promotion.js +377 -0
  77. package/src/memory/tokenize.js +63 -0
  78. package/src/project-type-detector.js +866 -0
  79. package/src/prompt-check.js +171 -0
  80. package/src/ralph-allowlist.js +88 -0
  81. package/src/receipts.js +129 -0
  82. package/src/redactor.js +107 -0
  83. package/src/sandbox.js +275 -0
  84. package/src/sanitizer.js +69 -0
  85. package/src/scan-resume.js +167 -0
  86. package/src/schema.js +82 -0
  87. package/src/search-bm25.js +108 -0
  88. package/src/server.js +1414 -0
  89. package/src/swarm-config.js +80 -0
  90. package/src/trident/dispatch.js +211 -0
  91. package/src/trident/lens-health.js +253 -0
  92. package/src/update-apply.js +79 -0
  93. package/src/update-check.js +136 -0
  94. package/src/vectors.js +178 -0
  95. package/templates/design/bento-grid.md +84 -0
  96. package/templates/design/brutalist-luxe.md +82 -0
  97. package/templates/design/cinematic-dark.md +82 -0
  98. package/templates/design/data-dense-dashboard.md +88 -0
  99. package/templates/design/editorial-warm.md +81 -0
  100. package/templates/design/glassmorphic.md +84 -0
  101. package/templates/design/magazine-editorial.md +84 -0
  102. package/templates/design/maximalist-vibrant.md +85 -0
  103. package/templates/design/neo-swiss-tech.md +85 -0
  104. package/templates/design/swiss-minimal.md +80 -0
  105. package/templates/design/terminal-native.md +83 -0
  106. package/templates/design/warm-organic.md +84 -0
@@ -0,0 +1,136 @@
1
+ // MCP tool: ijfw_update_check
2
+ //
3
+ // Returns { current, latest, available, confirmation_token?, expires_at?, changelog_url, instruction? }
4
+ // If update is available, issues a 5-min confirmation token. Token must be
5
+ // consumed via terminal-side `ijfw update --confirm <token>` -- see v3 sec 16.
6
+ //
7
+ // Re-entrancy guard: if state.json.last_applied_version == cached last_latest_seen,
8
+ // returns available:false (just-updated suppression).
9
+
10
+ import { join } from 'node:path';
11
+ import { homedir } from 'node:os';
12
+ import { readSafe, writeAtomic } from './lib/atomic-io.js';
13
+ import { npmView, compareSemver } from './lib/npm-view.js';
14
+ import { issueToken, writePendingSentinel, readPendingSentinel } from './lib/token.js';
15
+
16
+ const PKG = '@ijfw/install';
17
+ const REPO = 'therealseandonahoe/ijfw';
18
+
19
+ function ijfwHome() {
20
+ return process.env.IJFW_HOME || join(homedir(), '.ijfw');
21
+ }
22
+
23
+ export function readInstalledVersion() {
24
+ const sp = readSafe(join(ijfwHome(), 'state.json'));
25
+ if (sp.ok && sp.data && typeof sp.data.installed_version === 'string') {
26
+ return sp.data.installed_version;
27
+ }
28
+ return '0.0.0';
29
+ }
30
+
31
+ export function readLastApplied() {
32
+ const sp = readSafe(join(ijfwHome(), 'state.json'));
33
+ return (sp.ok && sp.data && sp.data.last_applied_version) || null;
34
+ }
35
+
36
+ export function readCachedCheck() {
37
+ return readSafe(join(ijfwHome(), 'cache', 'update-check.json'));
38
+ }
39
+
40
+ export function writeCachedCheck(data) {
41
+ return writeAtomic(join(ijfwHome(), 'cache', 'update-check.json'), {
42
+ schema_version: 1,
43
+ last_check: Math.floor(Date.now() / 1000),
44
+ ...data,
45
+ });
46
+ }
47
+
48
+ export async function ijfwUpdateCheck(args = {}) {
49
+ const sessionId = args.session_id || process.env.IJFW_SESSION_ID || 'default-session';
50
+ const force = args.force === true;
51
+
52
+ const current = readInstalledVersion();
53
+ const lastApplied = readLastApplied();
54
+ const cached = readCachedCheck();
55
+
56
+ let latest = null;
57
+ if (!force && cached.ok && cached.data && typeof cached.data.last_latest_seen === 'string') {
58
+ // Use cached value if fresh enough (24h)
59
+ const ageSec = Math.floor(Date.now() / 1000) - (cached.data.last_check || 0);
60
+ if (ageSec < 24 * 3600 && ageSec >= 0) {
61
+ latest = cached.data.last_latest_seen;
62
+ }
63
+ }
64
+ if (!latest) {
65
+ const r = await npmView(PKG);
66
+ if (!r.ok) {
67
+ return {
68
+ current,
69
+ latest: null,
70
+ available: null,
71
+ reachable: false,
72
+ error: r.error,
73
+ message: r.message || 'update check failed; will retry next session',
74
+ };
75
+ }
76
+ latest = r.version;
77
+ try { writeCachedCheck({ last_latest_seen: latest, last_failure: null }); } catch { /* */ }
78
+ }
79
+
80
+ // Re-entrancy: if we just applied this version, don't nudge again
81
+ if (lastApplied && compareSemver(lastApplied, latest) >= 0) {
82
+ return { current, latest, available: false, reachable: true, reason: 'up-to-date' };
83
+ }
84
+
85
+ const cmp = compareSemver(current, latest);
86
+ const available = cmp < 0;
87
+
88
+ const result = {
89
+ current,
90
+ latest,
91
+ available,
92
+ reachable: true,
93
+ changelog_url: `https://github.com/${REPO}/releases/tag/v${latest}`,
94
+ };
95
+
96
+ if (available) {
97
+ const tok = issueToken(sessionId, latest);
98
+ // Write the pending sentinel so the user can run `ijfw update --confirm
99
+ // <token>` in one step. The terminal command itself is the air-gap.
100
+ try { writePendingSentinel(sessionId, latest, tok.token); } catch { /* best-effort; the post-read below covers concurrent failures */ }
101
+ // Concurrent _check calls within the same session race on the token-file
102
+ // + sentinel writes. The last writer wins for both, so reflecting the
103
+ // sentinel's actual content in our response guarantees the returned
104
+ // token matches what `--confirm` will accept.
105
+ let finalToken = tok.token;
106
+ let finalTarget = latest;
107
+ const post = readPendingSentinel(sessionId);
108
+ if (post.ok && post.data && post.data.token) {
109
+ finalToken = post.data.token;
110
+ if (post.data.target_version) finalTarget = post.data.target_version;
111
+ }
112
+ result.confirmation_token = finalToken;
113
+ result.target_version = finalTarget;
114
+ result.expires_at = tok.expires_at;
115
+ result.instruction =
116
+ `To proceed, run in your TERMINAL: ijfw update --confirm ${finalToken}\n` +
117
+ `Token expires in 5 minutes. The MCP tool cannot execute the update directly.`;
118
+ }
119
+
120
+ return result;
121
+ }
122
+
123
+ export const TOOL_DEF = {
124
+ name: 'ijfw_update_check',
125
+ description:
126
+ 'Check if an IJFW update is available. Issues a confirmation token; the user must run ' +
127
+ "'ijfw update --confirm <token>' in their terminal to actually update. The model cannot " +
128
+ 'execute updates directly -- this air-gaps prompt injection from code execution.',
129
+ inputSchema: {
130
+ type: 'object',
131
+ properties: {
132
+ force: { type: 'boolean', description: 'Bypass cache, fetch from npm now' },
133
+ session_id: { type: 'string', description: 'Session ID for token scoping (optional)' },
134
+ },
135
+ },
136
+ };
package/src/vectors.js ADDED
@@ -0,0 +1,178 @@
1
+ // --- Vector embeddings (W3.3 / H5a-b-c) ---
2
+ //
3
+ // Thin wrapper around @xenova/transformers. Lazily imports the library on
4
+ // first use so the zero-deps default install path is unaffected -- users who
5
+ // don't enable vectors never pay the ~5MB bundle cost, and the 23MB model
6
+ // download only happens when the first embedding query fires.
7
+ //
8
+ // Environment control:
9
+ // IJFW_VECTORS=off -- disable vectors entirely (BM25-only)
10
+ // IJFW_VECTORS=on -- enable (default if the library is present)
11
+ // IJFW_VECTORS_MODEL -- override the embedding model (default: Xenova/all-MiniLM-L6-v2, ~23MB)
12
+ //
13
+ // Fallback: if @xenova/transformers isn't installed, vectors silently
14
+ // disable and callers get an `{ available: false, reason }` from getEmbedder().
15
+
16
+ const DEFAULT_MODEL = 'Xenova/all-MiniLM-L6-v2';
17
+
18
+ // X3/S8 -- model integrity pin. When IJFW_VECTORS_MODEL_SHA256 is set, we
19
+ // SHA-256 the loaded model.onnx after download and refuse the embedder if
20
+ // the hash doesn't match. Empty (default) allows any -- documented as opt-in
21
+ // in NO_TELEMETRY.md. Implemented in Phase 6 after the audit found the var
22
+ // was read but never enforced.
23
+
24
+ let _pipelinePromise = null;
25
+
26
+ // R2-B -- locate the actual ONNX file the pipeline loaded. transformers.js
27
+ // uses several cache layouts (HuggingFace-style models--{org}--{name}
28
+ // snapshots; flat cacheDir; explicit localModelPath). Scan candidates,
29
+ // return the first that exists.
30
+ async function resolveModelFile(env, modelId) {
31
+ const { existsSync, readdirSync, statSync } = await import('node:fs');
32
+ const { join: pjoin } = await import('node:path');
33
+ const roots = [];
34
+ if (env && env.localModelPath) roots.push(env.localModelPath);
35
+ if (env && env.cacheDir) roots.push(env.cacheDir);
36
+ if (process.env.IJFW_VECTORS_CACHE) roots.push(process.env.IJFW_VECTORS_CACHE);
37
+ if (process.env.HOME) roots.push(pjoin(process.env.HOME, '.cache', 'huggingface'));
38
+
39
+ const filenames = ['model.onnx', 'model_quantized.onnx', 'model_fp16.onnx'];
40
+ const modelSlugs = [
41
+ modelId,
42
+ modelId.replace('/', '_'),
43
+ 'models--' + modelId.replace('/', '--'),
44
+ ];
45
+
46
+ for (const root of roots) {
47
+ if (!root || !existsSync(root)) continue;
48
+ // Direct layout: {root}/{slug}/onnx/{file}
49
+ for (const slug of modelSlugs) {
50
+ for (const f of filenames) {
51
+ const p = pjoin(root, slug, 'onnx', f);
52
+ if (existsSync(p)) return p;
53
+ }
54
+ }
55
+ // HF snapshots: {root}/models--{org}--{name}/snapshots/{rev}/onnx/{file}
56
+ const hfDir = pjoin(root, 'models--' + modelId.replace('/', '--'));
57
+ if (existsSync(hfDir) && statSync(hfDir).isDirectory()) {
58
+ const snapshotsDir = pjoin(hfDir, 'snapshots');
59
+ if (existsSync(snapshotsDir)) {
60
+ for (const rev of readdirSync(snapshotsDir)) {
61
+ for (const f of filenames) {
62
+ const p = pjoin(snapshotsDir, rev, 'onnx', f);
63
+ if (existsSync(p)) return p;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+
72
+ async function verifyModelSha(env, modelId) {
73
+ const expected = process.env.IJFW_VECTORS_MODEL_SHA256;
74
+ if (!expected) return { ok: true }; // no pin configured, skip verification
75
+ try {
76
+ const { createReadStream } = await import('node:fs');
77
+ const { createHash } = await import('node:crypto');
78
+ const modelPath = await resolveModelFile(env, modelId);
79
+ if (!modelPath) {
80
+ // R2-B -- fail OPEN with a clear reason rather than closed. A path-guess
81
+ // miss should not disable a working embedder; surface the lack of
82
+ // verification so the user can set IJFW_VECTORS_CACHE explicitly.
83
+ process.stderr.write(
84
+ `IJFW: SHA verification skipped -- couldn't locate ONNX for ${modelId}. ` +
85
+ `Set IJFW_VECTORS_CACHE to the cache root or clear IJFW_VECTORS_MODEL_SHA256.\n`
86
+ );
87
+ return { ok: true, skipped: true };
88
+ }
89
+ await new Promise((resolve, reject) => {
90
+ const h = createHash('sha256');
91
+ const s = createReadStream(modelPath);
92
+ s.on('error', reject);
93
+ s.on('data', (c) => h.update(c));
94
+ s.on('end', () => {
95
+ const got = h.digest('hex');
96
+ if (got === expected.toLowerCase()) resolve();
97
+ else reject(new Error(`sha256 mismatch at ${modelPath}: expected ${expected}, got ${got}`));
98
+ });
99
+ });
100
+ return { ok: true, verified: true };
101
+ } catch (e) {
102
+ // Hash mismatch IS a closed-fail (user pinned; we can't trust this model).
103
+ return { ok: false, reason: `sha-verify-failed: ${e.message}` };
104
+ }
105
+ }
106
+
107
+ async function loadPipeline() {
108
+ if (_pipelinePromise) return _pipelinePromise;
109
+ _pipelinePromise = (async () => {
110
+ try {
111
+ const lib = await import('@xenova/transformers');
112
+ const { pipeline, env } = lib;
113
+ // Models cache locally under $XDG_CACHE_HOME or ~/.cache/xenova/.
114
+ env.localModelPath = process.env.IJFW_VECTORS_CACHE || undefined;
115
+ env.allowRemoteModels = true;
116
+ const model = process.env.IJFW_VECTORS_MODEL || DEFAULT_MODEL;
117
+ const extractor = await pipeline('feature-extraction', model);
118
+ // X3/S8 -- verify pinned SHA256 after load (post-download if remote).
119
+ const sha = await verifyModelSha(env, model);
120
+ if (!sha.ok) return { ok: false, reason: sha.reason };
121
+ return { ok: true, extractor, model };
122
+ } catch (e) {
123
+ return { ok: false, reason: e.code === 'ERR_MODULE_NOT_FOUND'
124
+ ? 'transformers-not-installed'
125
+ : `load-failed: ${e.message}` };
126
+ }
127
+ })();
128
+ return _pipelinePromise;
129
+ }
130
+
131
+ export function vectorsEnabled() {
132
+ const v = (process.env.IJFW_VECTORS || 'on').toLowerCase();
133
+ return v !== 'off' && v !== '0' && v !== 'false';
134
+ }
135
+
136
+ // Returns { available: true, embed(text) → Float32Array } or
137
+ // { available: false, reason }.
138
+ export async function getEmbedder() {
139
+ if (!vectorsEnabled()) return { available: false, reason: 'disabled-by-env' };
140
+ const loaded = await loadPipeline();
141
+ if (!loaded.ok) return { available: false, reason: loaded.reason };
142
+ return {
143
+ available: true,
144
+ model: loaded.model,
145
+ embed: async (text) => {
146
+ const out = await loaded.extractor(text, { pooling: 'mean', normalize: true });
147
+ return Array.from(out.data);
148
+ },
149
+ };
150
+ }
151
+
152
+ // Cosine similarity on two equal-length Float32 arrays (or plain arrays).
153
+ // Both inputs should already be L2-normalized (our embedder's normalize: true
154
+ // guarantees that) so this reduces to a dot product.
155
+ export function cosine(a, b) {
156
+ if (!a || !b || a.length !== b.length) return 0;
157
+ let s = 0;
158
+ for (let i = 0; i < a.length; i++) s += a[i] * b[i];
159
+ return s;
160
+ }
161
+
162
+ // Hybrid rerank: BM25 scores + vector cosine. Mixes with weights.
163
+ // bm25Results: [{ id, score, ... }]
164
+ // vectorMatches: Map<id, cosine>
165
+ // Returns merged, resorted list.
166
+ export function hybridRerank(bm25Results, vectorScores, opts = {}) {
167
+ const wBm25 = opts.wBm25 ?? 0.6;
168
+ const wVec = opts.wVec ?? 0.4;
169
+ // Normalize BM25 scores to 0..1 by dividing by max.
170
+ const maxB = Math.max(0.0001, ...bm25Results.map(r => r.score));
171
+ return bm25Results
172
+ .map(r => {
173
+ const vec = vectorScores.get(r.id) ?? 0;
174
+ const merged = (r.score / maxB) * wBm25 + vec * wVec;
175
+ return { ...r, bm25_score: r.score, vector_score: vec, score: merged };
176
+ })
177
+ .sort((a, b) => b.score - a.score);
178
+ }
@@ -0,0 +1,84 @@
1
+ # Bento Grid - DESIGN.md
2
+
3
+ ## 1. Visual Theme & Atmosphere
4
+ Card grid with varied sizes as the primary design language - the aesthetic of modern portfolio sites, SaaS landing pages, and personal product showcases. Visual hierarchy is established through card size rather than typographic scale or color. A 3-column grid where some cards span 2 columns, some span 2 rows, and feature cards command both. Neutral backgrounds keep the cards themselves as the composition. Use for landing pages, portfolio sites, feature showcases, personal sites, and any layout where the arrangement of content is itself the story.
5
+
6
+ ## 2. Color Palette & Roles
7
+ - `--color-bg`: #0F0F12 (page background - dark neutral)
8
+ - `--color-surface`: #18181C (standard card)
9
+ - `--color-surface-alt`: #1E1E24 (alternate card for variety)
10
+ - `--color-surface-light`: #F4F4F6 (light card variant for contrast inversion)
11
+ - `--color-border`: #2C2C34 (card borders, dividers)
12
+ - `--color-text-primary`: #EEEEF2 (headings, primary content)
13
+ - `--color-text-secondary`: #88889A (supporting text, labels)
14
+ - `--color-text-on-light`: #18181C (text on light cards)
15
+ - `--color-accent`: #6F6FFF (CTAs, highlights - periwinkle)
16
+ - `--color-accent-hover`: #8F8FFF (interactive accent state)
17
+ - `--color-accent-on-light`: #4040CC (accent on light card surfaces)
18
+ - `--color-separator`: #232328 (between grid cells, hairline rules)
19
+
20
+ ## 3. Typography Rules
21
+ - **Display font**: Inter - Google Fonts - weights 300, 400, 600, 700
22
+ - **Body font**: Inter - Google Fonts - weights 400, 500
23
+ - **Mono font**: JetBrains Mono - for stats, metrics, code callouts inside cards
24
+ - **Scale**: Display 56px / H1 40px / H2 26px / H3 18px / Body 15px / Small 12px
25
+ - **Line height**: 1.6 for body / 1.1 for display
26
+ - **Letter spacing**: 0 for body / -0.025em for display
27
+ - **Font loading**: `@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono&display=swap')`
28
+
29
+ ## 4. Component Stylings
30
+ ### Buttons
31
+ Primary: `background: --color-accent`, white text, `font-weight: 600`, `border-radius: 10px`, `padding: 10px 22px`. Hover: `--color-accent-hover`. Secondary: transparent, `border: 1px solid --color-border`, `color: --color-text-primary`, `border-radius: 10px`. Inside dark cards: secondary button uses `border-color: rgba(255,255,255,0.15)`. Inside light cards: use `--color-accent-on-light`.
32
+
33
+ ### Cards
34
+ Standard: `background: --color-surface`, `border: 1px solid --color-border`, `border-radius: 16px`, `padding: 28px`, `overflow: hidden`. Span-2 (wide): same styles, `grid-column: span 2`. Span-2 (tall): `grid-row: span 2`. Feature card (2x2): both spans. Light card: `background: --color-surface-light`, `color: --color-text-on-light`. Hover on all: `border-color: rgba(111,111,255,0.3)`, `box-shadow: 0 0 0 1px rgba(111,111,255,0.15)`.
35
+
36
+ ### Navigation
37
+ Height 60px, `background: rgba(15,15,18,0.8)`, `backdrop-filter: blur(12px)`, `border-bottom: 1px solid --color-border`. Sticky. Logo: Inter weight 700. Nav items: 14px, `color: --color-text-secondary`. Active: `color: --color-text-primary`. CTA button right-aligned.
38
+
39
+ ### Inputs
40
+ `background: --color-surface-alt`, `border: 1px solid --color-border`, `border-radius: 10px`, `padding: 10px 14px`, `font-size: 14px`, `color: --color-text-primary`. Focus: `border-color: --color-accent`, `box-shadow: 0 0 0 3px rgba(111,111,255,0.15)`. Placeholder: `--color-text-secondary`.
41
+
42
+ ### Badges / Chips
43
+ `background: rgba(111,111,255,0.12)`, `border: 1px solid rgba(111,111,255,0.25)`, `color: --color-accent-hover`, `border-radius: 20px`, `padding: 3px 10px`, `font-size: 12px`, `font-weight: 500`. On light cards: `background: rgba(64,64,204,0.08)`, `border-color: rgba(64,64,204,0.2)`, `color: --color-accent-on-light`.
44
+
45
+ ## 5. Layout Principles
46
+ - **Grid**: 3-column bento with `gap: 16px`; CSS grid with `grid-template-columns: repeat(3, 1fr)`
47
+ - **Max width**: 1200px
48
+ - **Section padding**: 80px vertical for page sections; cards are flush within the grid
49
+ - **Spacing scale**: 4 / 8 / 12 / 16 / 24 / 28 / 32 / 48 / 64 / 80px
50
+ - **Whitespace philosophy**: structured - the gaps between cards are the whitespace; inside cards is purposeful density
51
+
52
+ ## 6. Depth & Elevation
53
+ - **Surface hierarchy**: Level 0 = `--color-bg`, Level 1 = card (`--color-surface`), Level 2 = modal/popover
54
+ - **Shadow tokens**: resting = none (border only); hover = `0 4px 20px rgba(0,0,0,0.4)`; modal = `0 24px 64px rgba(0,0,0,0.6)`
55
+ - **Border usage**: 1px on every card; the border is what gives the bento grid its grid legibility
56
+
57
+ ## 7. Do's and Don'ts
58
+ **Do:**
59
+ - Design card contents individually - each card is a mini composition with its own hierarchy
60
+ - Use span-2 and span-2-row assignments to create visual rhythm and feature emphasis
61
+ - Mix dark and light cards to create contrast and visual anchoring points in the grid
62
+ - Keep the gap consistent (16px) - irregular gaps break the bento grid logic
63
+ - Let large metric numbers or illustrations fill the card edge-to-edge on tall cards
64
+
65
+ **Don't:**
66
+ - Use the same size for all cards - uniform grids lose the bento benefit entirely
67
+ - Add thick outer padding around the grid; the card borders define the structure
68
+ - Place navigation or chrome inside the bento grid area
69
+ - Use more than two distinct card backgrounds in the same grid section
70
+ - Apply heavy shadows to resting cards - the hover state needs contrast headroom
71
+
72
+ ## 8. Responsive Behavior
73
+ - **Mobile breakpoint**: 375px
74
+ - **Tablet breakpoint**: 768px
75
+ - **Touch targets**: minimum 44px
76
+ - **Typography scaling**: Display drops to 36px / H1 to 26px on mobile
77
+ - **Layout collapse**: 3-col grid → 2-col at 768px → 1-col at 375px; span overrides reset (all cards become 1-col at mobile); gap reduces to 10px
78
+
79
+ ## 9. Agent Prompt Guide
80
+ Use these prompts with Claude Design or ijfw-design to stay on-system:
81
+
82
+ - **New screen**: "Build a [screen type] following the Bento Grid DESIGN.md. Match the color tokens and type scale exactly."
83
+ - **Component**: "Add a [component] that fits the Bento Grid aesthetic - reference section 4 for styling rules."
84
+ - **Variant**: "Create a [light/dark/compact] variant of this screen. Keep the same token names, adjust the values."
@@ -0,0 +1,82 @@
1
+ # Brutalist Luxe - DESIGN.md
2
+
3
+ ## 1. Visual Theme & Atmosphere
4
+ Raw brutalism stripped of ugliness and dressed in restraint - the visual language of a Balenciaga campaign or a prestige print magazine that knows the rules and ignores them deliberately. Maximum contrast, visible structure, zero rounding. The grid is exposed, type is oversized, one bold accent color detonates against the monochrome field. Use for fashion, editorial, portfolio, culture, and any brand that positions itself as a category of one.
5
+
6
+ ## 2. Color Palette & Roles
7
+ - `--color-bg`: #F5F5F5 (page background - paper white with texture suggestion)
8
+ - `--color-surface`: #FFFFFF (card/panel surface)
9
+ - `--color-surface-dark`: #0A0A0A (inverted section surface)
10
+ - `--color-border`: #0A0A0A (dividers, input borders - hard black)
11
+ - `--color-text-primary`: #0A0A0A (headings, primary content)
12
+ - `--color-text-inverse`: #F5F5F5 (text on dark surfaces)
13
+ - `--color-text-secondary`: #555555 (supporting text, labels)
14
+ - `--color-accent`: #E8FF00 (CTAs, highlights - electric yellow)
15
+ - `--color-accent-hover`: #D4E800 (interactive accent state)
16
+ - `--color-accent-on-dark`: #E8FF00 (accent on dark surfaces - same, still works)
17
+
18
+ ## 3. Typography Rules
19
+ - **Display font**: Bebas Neue - Google Fonts - weight 400 (this face has no variants; use size for hierarchy)
20
+ - **Body font**: Inter - Google Fonts - weights 400, 500, 600
21
+ - **Mono font**: JetBrains Mono - for data, code, technical detail
22
+ - **Scale**: Display 96px / H1 56px / H2 36px / H3 24px / Body 16px / Small 13px
23
+ - **Line height**: 1.5 for body / 1.0 for display (Bebas Neue is tight by design)
24
+ - **Letter spacing**: 0 for body / 0.03em for display - Bebas benefits from slight tracking
25
+ - **Font loading**: `@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Inter:wght@400;500;600&family=JetBrains+Mono&display=swap')`
26
+
27
+ ## 4. Component Stylings
28
+ ### Buttons
29
+ Primary: `background: --color-accent`, `color: #0A0A0A`, `font-family: Inter`, `font-weight: 600`, `border-radius: 0`, `padding: 14px 28px`, `border: 2px solid #0A0A0A`, uppercase, `letter-spacing: 0.06em`. Hover: `background: --color-accent-hover`. Secondary: `background: transparent`, `border: 2px solid #0A0A0A`, `color: #0A0A0A`. No softening anywhere.
30
+
31
+ ### Cards
32
+ `background: --color-surface`, `border: 2px solid --color-border`, `border-radius: 0`, `padding: 28px`. No shadow. Optional: thick left bar `border-left: 6px solid --color-accent`. Dark variant: `background: --color-surface-dark`, `color: --color-text-inverse`, `border-color: --color-surface-dark`.
33
+
34
+ ### Navigation
35
+ Height 60px, `border-bottom: 2px solid --color-border`, background `--color-bg`. Logo in Bebas Neue 28px. Nav links: Inter `font-weight: 600`, `font-size: 13px`, uppercase, `letter-spacing: 0.08em`. Active: `background: --color-accent`, `color: #0A0A0A`, `padding: 4px 10px`.
36
+
37
+ ### Inputs
38
+ `background: --color-bg`, `border: 2px solid --color-border`, `border-radius: 0`, `padding: 11px 12px`, `font-family: Inter`, `font-size: 15px`. Focus: `border-color: #0A0A0A`, `box-shadow: 4px 4px 0 #0A0A0A`. Placeholder: `--color-text-secondary`.
39
+
40
+ ### Badges / Chips
41
+ `background: --color-accent`, `color: #0A0A0A`, `border: 2px solid #0A0A0A`, `border-radius: 0`, `padding: 3px 10px`, `font-size: 11px`, `font-family: Inter`, `font-weight: 600`, uppercase, `letter-spacing: 0.06em`. Inverted variant: `background: #0A0A0A`, `color: --color-accent`.
42
+
43
+ ## 5. Layout Principles
44
+ - **Grid**: 12-column with 2px visible column rules optional - the structure is the design
45
+ - **Max width**: 1400px
46
+ - **Section padding**: 80px vertical
47
+ - **Spacing scale**: 4 / 8 / 16 / 24 / 32 / 48 / 64 / 80 / 96px
48
+ - **Whitespace philosophy**: intentional asymmetry - some sections are dense, others are a single headline in 40% of the viewport
49
+
50
+ ## 6. Depth & Elevation
51
+ - **Surface hierarchy**: Level 0 = `--color-bg`, Level 1 = `--color-surface` (with 2px border), Level 2 = offset shadow only
52
+ - **Shadow tokens**: none (flat) or offset hard shadow `4px 4px 0 #0A0A0A` - no blurred shadows ever
53
+ - **Border usage**: 2px solid black everywhere; borders are structural, not decorative
54
+
55
+ ## 7. Do's and Don'ts
56
+ **Do:**
57
+ - Use Bebas Neue at large sizes only (36px+); below that Inter bold takes over
58
+ - Use the electric yellow accent as a weapon - it should feel aggressive when it appears
59
+ - Mix inverted sections (black bg) with light sections for rhythm and drama
60
+ - Let type collide with layout edges; crop is intentional
61
+ - Use the hard 4px offset shadow on interactive cards and focused inputs
62
+
63
+ **Don't:**
64
+ - Round any corner - `border-radius: 0` is a rule, not a suggestion
65
+ - Use more than one accent color; the yellow is the entire accent system
66
+ - Use soft drop shadows (blurred box-shadow); hard offset only
67
+ - Apply subtle animations; transitions are instant or simple slide-in
68
+ - Use imagery with busy compositions - high contrast or solid color photos only
69
+
70
+ ## 8. Responsive Behavior
71
+ - **Mobile breakpoint**: 375px
72
+ - **Tablet breakpoint**: 768px
73
+ - **Touch targets**: minimum 44px
74
+ - **Typography scaling**: Display drops to 56px / H1 to 36px on mobile; Bebas Neue stays at all sizes
75
+ - **Layout collapse**: 12-col → single column; column rules removed; padding reduces to 24px; bold borders remain
76
+
77
+ ## 9. Agent Prompt Guide
78
+ Use these prompts with Claude Design or ijfw-design to stay on-system:
79
+
80
+ - **New screen**: "Build a [screen type] following the Brutalist Luxe DESIGN.md. Match the color tokens and type scale exactly."
81
+ - **Component**: "Add a [component] that fits the Brutalist Luxe aesthetic - reference section 4 for styling rules."
82
+ - **Variant**: "Create a [light/dark/compact] variant of this screen. Keep the same token names, adjust the values."
@@ -0,0 +1,82 @@
1
+ # Cinematic Dark - DESIGN.md
2
+
3
+ ## 1. Visual Theme & Atmosphere
4
+ Film-inspired luxury dark - the aesthetic of a well-funded production company's website or a prestige tech brand's launch page. Near-absolute black background, cool grey surfaces, silver-white type. Every element is considered and spaced as if a cinematographer approved the composition. Use for flagship product launches, AI products, high-end SaaS, portfolio sites, and anything where first impression must signal quality and ambition.
5
+
6
+ ## 2. Color Palette & Roles
7
+ - `--color-bg`: #080808 (page background - near-total black)
8
+ - `--color-surface`: #111114 (card/panel surface - cool dark)
9
+ - `--color-surface-mid`: #1A1A1F (elevated panels, modals)
10
+ - `--color-border`: #242428 (dividers, input borders)
11
+ - `--color-border-subtle`: #1C1C20 (lightest surface separator)
12
+ - `--color-text-primary`: #F0F0F2 (headings, primary content - silver white)
13
+ - `--color-text-secondary`: #6E6E78 (supporting text, labels)
14
+ - `--color-accent`: #D4D4D8 (CTAs, links - cool silver)
15
+ - `--color-accent-hover`: #FFFFFF (interactive accent state - full white)
16
+ - `--color-muted`: #3A3A40 (placeholder text, disabled)
17
+
18
+ ## 3. Typography Rules
19
+ - **Display font**: Inter - Google Fonts - weights 200, 300, 400, 600
20
+ - **Body font**: Inter - Google Fonts - weights 300, 400
21
+ - **Mono font**: JetBrains Mono - for data, stats, technical callouts
22
+ - **Scale**: Display 72px / H1 48px / H2 32px / H3 22px / Body 16px / Small 13px
23
+ - **Line height**: 1.65 for body / 1.05 for display
24
+ - **Letter spacing**: 0.01em for body / -0.03em for display / 0.12em for small caps labels
25
+ - **Font loading**: `@import url('https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;600&family=JetBrains+Mono&display=swap')`
26
+
27
+ ## 4. Component Stylings
28
+ ### Buttons
29
+ Primary: `background: #F0F0F2`, `color: #080808`, `font-weight: 500`, `border-radius: 3px`, `padding: 12px 28px`, `letter-spacing: 0.01em`. Hover: `background: #FFFFFF`. Secondary: transparent, `border: 1px solid --color-border`, `color: --color-text-primary`. No colored accents - this palette is intentionally achromatic.
30
+
31
+ ### Cards
32
+ Background `--color-surface`, `border: 1px solid --color-border-subtle`, `border-radius: 6px`, `padding: 32px`. No shadow - dark backgrounds absorb light, borders define. For featured cards: `border-color: --color-border`, subtle inner glow `box-shadow: inset 0 1px 0 rgba(255,255,255,0.04)`.
33
+
34
+ ### Navigation
35
+ Height 64px, `border-bottom: 1px solid --color-border-subtle`, background `rgba(8,8,8,0.85)`, `backdrop-filter: blur(12px)`. Sticky. Logo weight 300. Nav items: Inter 14px, `color: --color-text-secondary`. Hover: `color: --color-text-primary`. Active: `color: #FFFFFF`.
36
+
37
+ ### Inputs
38
+ `background: --color-surface`, `border: 1px solid --color-border`, `border-radius: 4px`, `padding: 11px 14px`, `font-size: 15px`, `color: --color-text-primary`. Focus: `border-color: #404048`, `box-shadow: 0 0 0 3px rgba(255,255,255,0.04)`. Placeholder: `--color-muted`.
39
+
40
+ ### Badges / Chips
41
+ `background: --color-surface-mid`, `border: 1px solid --color-border`, `border-radius: 3px`, `padding: 3px 9px`, `font-size: 11px`, `font-weight: 500`, `color: --color-text-secondary`, `letter-spacing: 0.06em`, uppercase. Understated - status, not decoration.
42
+
43
+ ## 5. Layout Principles
44
+ - **Grid**: 12-column, 32px gutters
45
+ - **Max width**: 1440px
46
+ - **Section padding**: 120px vertical
47
+ - **Spacing scale**: 8 / 16 / 24 / 32 / 48 / 64 / 96 / 128 / 160px
48
+ - **Whitespace philosophy**: extreme - cinema uses darkness as framing; UI should too
49
+
50
+ ## 6. Depth & Elevation
51
+ - **Surface hierarchy**: Level 0 = `--color-bg`, Level 1 = `--color-surface`, Level 2 = `--color-surface-mid`
52
+ - **Shadow tokens**: low = `0 2px 8px rgba(0,0,0,0.5)`; mid = `0 8px 32px rgba(0,0,0,0.7)`; high = `0 24px 64px rgba(0,0,0,0.9)`
53
+ - **Border usage**: very subtle (1px at 14% opacity on surfaces); let darkness define depth more than borders
54
+
55
+ ## 7. Do's and Don'ts
56
+ **Do:**
57
+ - Use Inter at weight 200-300 for large display text - ultra-light type on black is cinematic
58
+ - Let large sections be mostly empty; negative space is composition
59
+ - Use full-bleed sections with edge-to-edge dark backgrounds
60
+ - Treat any imagery as frames - high contrast, desaturated, or masked
61
+ - Apply `backdrop-filter: blur` for sticky nav only; not for cards
62
+
63
+ **Don't:**
64
+ - Introduce any color accent - the achromatic palette is intentional and load-bearing
65
+ - Use shadows that are lighter than the surface (no glows, no coloured light)
66
+ - Round corners aggressively; max 6px maintains the cinematic edge
67
+ - Add animation that feels playful; motion should feel slow and deliberate
68
+ - Use more than three font weights on any single screen
69
+
70
+ ## 8. Responsive Behavior
71
+ - **Mobile breakpoint**: 390px
72
+ - **Tablet breakpoint**: 768px
73
+ - **Touch targets**: minimum 44px
74
+ - **Typography scaling**: Display drops to 44px / H1 to 32px on mobile; letter-spacing tightens slightly
75
+ - **Layout collapse**: 12-col → single column; section padding reduces to 64px; nav collapses to hamburger
76
+
77
+ ## 9. Agent Prompt Guide
78
+ Use these prompts with Claude Design or ijfw-design to stay on-system:
79
+
80
+ - **New screen**: "Build a [screen type] following the Cinematic Dark DESIGN.md. Match the color tokens and type scale exactly."
81
+ - **Component**: "Add a [component] that fits the Cinematic Dark aesthetic - reference section 4 for styling rules."
82
+ - **Variant**: "Create a [light/dark/compact] variant of this screen. Keep the same token names, adjust the values."
@@ -0,0 +1,88 @@
1
+ # Data Dense Dashboard - DESIGN.md
2
+
3
+ ## 1. Visual Theme & Atmosphere
4
+ Information density first - the aesthetic of tools where the data is the UI. Every pixel that isn't carrying information is a pixel wasted. Compact row heights, monospace data values, status chips with semantic color, and minimal chrome around the content. Dark background keeps the eye on the numbers. Use for monitoring dashboards, analytics platforms, ops tooling, admin panels, and any surface where a user needs to read 40 rows of data without losing their place.
5
+
6
+ ## 2. Color Palette & Roles
7
+ - `--color-bg`: #0C0C0F (page background - deep dark)
8
+ - `--color-surface`: #131318 (card/panel surface)
9
+ - `--color-surface-row`: #161619 (table row hover/selected)
10
+ - `--color-border`: #222228 (dividers, table borders)
11
+ - `--color-border-strong`: #2E2E36 (section separators)
12
+ - `--color-text-primary`: #E4E4EF (headings, primary data)
13
+ - `--color-text-secondary`: #6E6E82 (labels, column headers)
14
+ - `--color-text-dim`: #3E3E4E (disabled, empty state)
15
+ - `--color-accent`: #5B6EF5 (active links, primary CTA - indigo)
16
+ - `--color-accent-hover`: #7B8EFF (interactive accent state)
17
+ - `--color-success`: #16A34A
18
+ - `--color-success-bg`: rgba(22,163,74,0.1)
19
+ - `--color-error`: #DC2626
20
+ - `--color-error-bg`: rgba(220,38,38,0.1)
21
+ - `--color-warning`: #D97706
22
+ - `--color-warning-bg`: rgba(217,119,6,0.1)
23
+
24
+ ## 3. Typography Rules
25
+ - **Display font**: Inter - Google Fonts - weights 400, 500, 600
26
+ - **Body font**: Inter - Google Fonts - weights 400, 500
27
+ - **Mono font**: JetBrains Mono - weights 400, 500 - all data values, numbers, IDs, timestamps, code
28
+ - **Scale**: Display 32px / H1 24px / H2 18px / H3 14px / Body 13px / Small 11px
29
+ - **Line height**: 1.5 for body / 1.2 for display
30
+ - **Letter spacing**: 0 for body / 0.04em for uppercase column headers
31
+ - **Font loading**: `@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap')`
32
+
33
+ ## 4. Component Stylings
34
+ ### Buttons
35
+ Primary: `background: --color-accent`, white text, `font-weight: 500`, `border-radius: 5px`, `padding: 6px 14px`, `font-size: 13px`. Hover: `--color-accent-hover`. Secondary: `background: --color-surface`, `border: 1px solid --color-border-strong`, `color: --color-text-primary`, `border-radius: 5px`, same padding. Icon-only buttons are 28x28px, `border-radius: 5px`.
36
+
37
+ ### Cards / Panels
38
+ `background: --color-surface`, `border: 1px solid --color-border`, `border-radius: 6px`, `padding: 16px`. Metric card: large JetBrains Mono number top, Inter label below in `--color-text-secondary`. Table panels have `padding: 0` and let the table fill edge to edge inside the border.
39
+
40
+ ### Navigation
41
+ Height 44px - compact. `border-bottom: 1px solid --color-border`, background `--color-bg`. Items: Inter 13px, `font-weight: 500`, `color: --color-text-secondary`. Active: `color: --color-text-primary`, left-border indicator `3px solid --color-accent`. Sidebar variant: 220px wide, `border-right: 1px solid --color-border`.
42
+
43
+ ### Inputs
44
+ `background: --color-bg`, `border: 1px solid --color-border`, `border-radius: 5px`, `padding: 6px 10px`, `font-size: 13px`, `color: --color-text-primary`. Height 32px - compact. Focus: `border-color: --color-accent`, `box-shadow: 0 0 0 2px rgba(91,110,245,0.15)`. Search inputs prepend a 14px icon with 8px gap.
45
+
46
+ ### Badges / Chips
47
+ Status chips: `padding: 2px 7px`, `border-radius: 4px`, `font-size: 11px`, `font-weight: 500`, `font-family: Inter`. Success: `background: --color-success-bg`, `color: #4ADE80`. Error: `background: --color-error-bg`, `color: #F87171`. Warning: `background: --color-warning-bg`, `color: #FCD34D`. Neutral: `background: rgba(110,110,130,0.12)`, `color: --color-text-secondary`.
48
+
49
+ ## 5. Layout Principles
50
+ - **Grid**: 12-column, 16px gutters
51
+ - **Max width**: 1600px (dashboards use the full screen)
52
+ - **Section padding**: 24px vertical between panels
53
+ - **Spacing scale**: 2 / 4 / 6 / 8 / 12 / 16 / 24 / 32 / 48px (2px base unit for dense rows)
54
+ - **Whitespace philosophy**: none wasted - every gap is intentional breathing room between data groups
55
+
56
+ ## 6. Depth & Elevation
57
+ - **Surface hierarchy**: Level 0 = `#0C0C0F`, Level 1 = `#131318`, Level 2 = `#181820`
58
+ - **Shadow tokens**: low = `0 1px 3px rgba(0,0,0,0.5)`; mid = `0 4px 12px rgba(0,0,0,0.6)`; none on most panels - borders define everything
59
+ - **Border usage**: 1px borders on all panels; hairline 1px `--color-border` between table rows; `--color-border-strong` between logical sections
60
+
61
+ ## 7. Do's and Don'ts
62
+ **Do:**
63
+ - Use JetBrains Mono for every numeric value, ID, hash, timestamp, and code string
64
+ - Keep table row height at 36-40px; never pad rows to 52px in a data-dense context
65
+ - Use semantic status colors (`--color-success/error/warning`) consistently across every surface
66
+ - Right-align numeric columns; left-align text columns - this is a non-negotiable data table rule
67
+ - Show column header units (ms, %, $) in `--color-text-secondary` following the label
68
+
69
+ **Don't:**
70
+ - Use large section padding (80px+) - this is a dashboard, not a landing page
71
+ - Apply border-radius beyond 6px; compact aesthetics have sharp but not brutal edges
72
+ - Use gradients or decorative backgrounds behind data panels
73
+ - Show empty cards when data is loading - use skeleton rows at the correct density
74
+ - Mix monospace and proportional fonts on the same data row
75
+
76
+ ## 8. Responsive Behavior
77
+ - **Mobile breakpoint**: 375px
78
+ - **Tablet breakpoint**: 1024px (dashboards are desktop-first; tablet is the collapse point)
79
+ - **Touch targets**: minimum 44px
80
+ - **Typography scaling**: all type stays the same; only layout collapses
81
+ - **Layout collapse**: multi-column dashboard → single column stacked panels; sidebar → top nav; tables get horizontal scroll rather than column hiding
82
+
83
+ ## 9. Agent Prompt Guide
84
+ Use these prompts with Claude Design or ijfw-design to stay on-system:
85
+
86
+ - **New screen**: "Build a [screen type] following the Data Dense Dashboard DESIGN.md. Match the color tokens and type scale exactly."
87
+ - **Component**: "Add a [component] that fits the Data Dense Dashboard aesthetic - reference section 4 for styling rules."
88
+ - **Variant**: "Create a [light/dark/compact] variant of this screen. Keep the same token names, adjust the values."