@techninja/clearstack 0.3.2 → 0.3.5

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.
@@ -292,7 +292,7 @@ handler resolves `host` to the page component as expected.
292
292
  Because components use light DOM, styles flow naturally:
293
293
 
294
294
  ```
295
- public/index.html
295
+ src/index.html
296
296
  ├── <link> src/styles/reset.css ← base reset
297
297
  ├── <link> src/styles/tokens.css ← :root custom properties
298
298
  ├── <link> src/styles/shared.css ← error states, icons, utilities
@@ -269,6 +269,23 @@ multiple keys.
269
269
 
270
270
  ---
271
271
 
272
+ ## Spec vs Test: Two Different Questions
273
+
274
+ `spec` and `test` answer different questions and run at different times.
275
+
276
+ | | `npm run spec` | `npm test` |
277
+ | ---------- | ------------------------------------------ | -------------------------------- |
278
+ | Asks | Is my code clean? | Does my code work? |
279
+ | Checks | Line counts, lint, format, types, markdown | Unit, integration, browser tests |
280
+ | Speed | Fast (~10s) | Slower (varies) |
281
+ | When | Every save, every change | Before commit, in CI |
282
+ | Auto-fixes | Yes (lint, format, markdown) | No |
283
+
284
+ Spec is the inner dev loop — run it constantly. Tests are the commit gate —
285
+ run them before pushing. CI runs both, in parallel.
286
+
287
+ ---
288
+
272
289
  ## Session Retrospective
273
290
 
274
291
  At the end of each implementation session, ask:
@@ -117,12 +117,12 @@ import {
117
117
 
118
118
  ```
119
119
  project-root/
120
- ├── public/ # Static assets served at /
120
+ ├── src/ # The app served directly, no build
121
121
  │ ├── index.html # App shell with import map
122
- └── vendor/ # Vendored ES module deps (generated)
123
- └── hybrids/ # Copied from node_modules at install
124
-
125
- ├── src/ # Application source (served at /src/)
122
+ ├── vendor/ # Vendored ES modules (generated, gitignored)
123
+ └── hybrids/ # Copied from node_modules at install
124
+ ├── icons.json # Icon sprite (generated, gitignored)
125
+ │ │
126
126
  │ ├── components/ # UI components (atomic design)
127
127
  │ │ ├── atoms/ # Smallest UI primitives
128
128
  │ │ │ └── app-button/
@@ -130,39 +130,47 @@ project-root/
130
130
  │ │ │ ├── app-button.css # Scoped styles
131
131
  │ │ │ └── index.js # Re-export
132
132
  │ │ ├── molecules/ # Compositions of atoms
133
- │ │ │ └── search-bar/
134
133
  │ │ ├── organisms/ # Complex UI sections
135
- │ │ │ └── app-header/
136
134
  │ │ └── templates/ # Page-level layout shells
137
- │ │ └── page-layout/
138
135
  │ │
139
136
  │ ├── pages/ # Route-bound view components
140
- │ │ ├── home/
141
- │ │ │ ├── home-view.js
142
- │ │ │ └── index.js
143
- │ │ └── about/
137
+ │ │ └── home/
138
+ │ │ └── home-view.js
144
139
  │ │
145
140
  │ ├── store/ # Hybrids store model definitions
146
141
  │ │ ├── AppState.js # Singleton: global app state
147
- │ │ └── UserModel.js # Enumerable: user records
142
+ │ │ └── UserPrefs.js # Singleton: user preferences
148
143
  │ │
149
144
  │ ├── router/ # Router shell component
150
145
  │ │ └── index.js
151
146
  │ │
152
147
  │ ├── styles/ # Shared CSS
153
148
  │ │ ├── tokens.css # Design tokens (colors, spacing)
154
- │ │ └── reset.css # Minimal CSS reset
149
+ │ │ ├── reset.css # Minimal CSS reset
150
+ │ │ └── components.css # Aggregates component CSS imports
151
+ │ │
152
+ │ ├── utils/ # Pure helper functions
153
+ │ │ └── formatDate.js
155
154
  │ │
156
- └── utils/ # Pure helper functions
157
- └── formatDate.js
155
+ ├── api/ # Server routes (fullstack only)
156
+ │ ├── entities.js # Generic CRUD router
157
+ │ │ └── schemas.js # JSON Schema registry
158
+ │ │
159
+ │ └── server.js # Express entry point
160
+
161
+ ├── scripts/ # Install/setup scripts
162
+ │ ├── vendor-deps.js # Copies deps to src/vendor/
163
+ │ └── build-icons.js # Extracts Lucide SVGs to src/icons.json
158
164
 
159
- ├── scripts/ # Build/install scripts
160
- │ └── vendor-deps.js # Copies deps to public/vendor/
165
+ ├── .configs/ # Linter/formatter/type configs
166
+ ├── docs/
167
+ │ ├── clearstack/ # Spec docs (synced on update)
168
+ │ └── app-spec/ # Project-specific specs (yours)
161
169
 
162
- ├── src/server.js # Express entry point
163
- ├── package.json # type: "module", postinstall hook
164
- ├── README.md # Project overview
165
- └── FRONTEND_IMPLEMENTATION_RULES.md # This file
170
+ ├── data/ # JSON DB seed (fullstack only)
171
+ ├── .env # Defaults (committed)
172
+ ├── .env.local # Overrides (gitignored)
173
+ └── package.json
166
174
  ```
167
175
 
168
176
  ### Key Conventions
@@ -170,7 +178,7 @@ project-root/
170
178
  - **One component per directory.** Each gets its own folder with `.js`, `.css`,
171
179
  and `index.js` (re-export).
172
180
  - **`src/` is served as-is** — the browser loads these files directly.
173
- - **`public/vendor/`** is gitignored and regenerated on `npm install` via the
181
+ - **`src/vendor/`** is gitignored and regenerated on `npm install` via the
174
182
  `postinstall` script.
175
183
  - **No barrel files** beyond the per-component `index.js`. Import from the
176
184
  component directory, not from a giant `components/index.js`.
@@ -42,33 +42,30 @@ If a `package.json` already exists, Clearstack merges into it — your existing
42
42
  ```
43
43
  your-project/
44
44
  ├── .configs/ # ⟳ Managed — synced on update
45
- │ ├── eslint.config.js
46
- │ ├── .prettierrc
47
- │ ├── jsconfig.json
48
- │ └── web-test-runner.config.js
49
45
  ├── .github/ # CI workflow, PR + issue templates
50
46
  ├── docs/
51
47
  │ ├── clearstack/ # ⟳ Managed — spec docs, synced on update
52
48
  │ └── app-spec/ # ✏️ Yours — project-specific specs
53
- ├── public/ # ✏️ Yours — static assets, index.html, import map
54
- ├── scripts/ # ✏️ Yours — vendor-deps, build-icons
55
- ├── src/
56
- │ ├── api/ # ✏️ Yours server routes (fullstack only)
57
- │ ├── components/ # ✏️ Yours atoms/, molecules/, organisms/
58
- │ ├── pages/ # ✏️ Yours — route-level views
59
- │ ├── store/ # ✏️ Yours — Hybrids store models
60
- │ ├── styles/ # ✏️ Yours — global CSS
61
- │ ├── router/ # ✏️ Yours — client-side routing
62
- │ ├── utils/ # ✏️ Yours — shared helpers
63
- └── server.js # ✏️ Yours — Express entry (fullstack only)
64
- ├── data/ # ✏️ Yours — JSON DB seed (fullstack only)
65
- ├── .env # ✏️ Yours — PORT, spec thresholds
66
- └── package.json # ✏️ Yours (spec scripts merged in)
49
+ ├── scripts/ # ✏️ Yours — setup, test, vendor-deps, build-icons
50
+ ├── src/ # ✏️ Yours — the app, served directly
51
+ ├── index.html # App shell with import map
52
+ │ ├── vendor/ # Vendored ES modules (generated, gitignored)
53
+ │ ├── icons.json # Icon sprite (generated, gitignored)
54
+ │ ├── components/ # atoms/, molecules/, organisms/
55
+ │ ├── pages/ # Route-level views
56
+ │ ├── store/ # Hybrids store models
57
+ │ ├── styles/ # Global CSS
58
+ │ ├── router/ # Client-side routing
59
+ ├── utils/ # Shared helpers
60
+ ├── api/ # Server routes (fullstack only)
61
+ │ └── server.js # Express entry point
62
+ ├── data/ # JSON DB seed (fullstack only)
63
+ ├── .env # Defaults (committed)
64
+ ├── .env.local # Overrides (gitignored)
65
+ └── package.json
67
66
  ```
68
67
 
69
- **⟳ Managed** files are updated when you run `clearstack update`. Review changes via `git diff`.
70
-
71
- **✏️ Yours** files are scaffolded once and never touched by updates. They're your code.
68
+ `src/` is the entire app. `node src/server.js` serves it. For static deploy, point your CDN at `src/`. No build step.
72
69
 
73
70
  ## 4. Install and Run
74
71
 
@@ -76,18 +73,12 @@ your-project/
76
73
  npm install
77
74
  ```
78
75
 
79
- `postinstall` runs `vendor-deps.js` (copies hybrids to `public/vendor/`) and `build-icons.js` (extracts Lucide SVGs to `public/icons.json`).
76
+ `postinstall` runs `vendor-deps.js` (copies hybrids to `src/vendor/`) and `build-icons.js` (extracts Lucide SVGs to `src/icons.json`).
80
77
 
81
78
  ### Fullstack
82
79
 
83
80
  ```bash
84
- npm run dev # node --watch --env-file=.env src/server.js
85
- ```
86
-
87
- ### Static
88
-
89
- ```bash
90
- npx serve public # or any static file server
81
+ npm run dev
91
82
  ```
92
83
 
93
84
  ## 5. Development Rules
@@ -152,7 +143,7 @@ SPEC_CODE_MAX_LINES=150
152
143
  SPEC_DOCS_MAX_LINES=500
153
144
  SPEC_CODE_EXTENSIONS=.js,.css
154
145
  SPEC_DOCS_EXTENSIONS=.md
155
- SPEC_IGNORE_DIRS=node_modules,public/vendor,.git,.configs
146
+ SPEC_IGNORE_DIRS=node_modules,src/vendor,.git,.configs
156
147
  ```
157
148
 
158
149
  ## 9. CI Pipeline
@@ -170,7 +161,7 @@ spec:code → spec:docs → lint → format → typecheck → test
170
161
  | Install Clearstack | `npm install -D @techninja/clearstack` |
171
162
  | Scaffold a project | `npx clearstack init` |
172
163
  | Install dependencies | `npm install` |
173
- | Start dev server | `npm run dev` / `npx serve public` |
164
+ | Start dev server | `npm run dev` |
174
165
  | Lint + format | `npm run lint:fix && npm run format` |
175
166
  | Type check | `npm run typecheck` |
176
167
  | Run tests | `npm test` |
@@ -14,13 +14,13 @@
14
14
 
15
15
  ### Responsibilities
16
16
 
17
- | Route | Serves |
18
- | ------------- | -------------------------------------- |
19
- | `/` | `public/index.html` (app shell) |
20
- | `/vendor/*` | `public/vendor/` (vendored ES modules) |
21
- | `/src/*` | `src/` (application source, as-is) |
22
- | `/api/*` | REST endpoints (see BACKEND_API_SPEC) |
23
- | `/api/events` | SSE stream for realtime sync |
17
+ | Route | Serves |
18
+ | ------------- | ------------------------------------- |
19
+ | `/` | `src/index.html` (app shell) |
20
+ | `/vendor/*` | `src/vendor/` (vendored ES modules) |
21
+ | `/src/*` | `src/` (application source, as-is) |
22
+ | `/api/*` | REST endpoints (see BACKEND_API_SPEC) |
23
+ | `/api/events` | SSE stream for realtime sync |
24
24
 
25
25
  ### Key Rules
26
26
 
@@ -38,7 +38,7 @@ const app = express();
38
38
  const PORT = process.env.PORT || 3000;
39
39
 
40
40
  // Static: vendored deps and app shell
41
- app.use(express.static('public'));
41
+ app.use(express.static('src'));
42
42
 
43
43
  // Static: application source (ES modules served directly)
44
44
  app.use('/src', express.static('src'));
@@ -57,7 +57,7 @@ app.listen(PORT, () => console.log(`http://localhost:${PORT}`));
57
57
  ## Vendor Dependency Loading
58
58
 
59
59
  Third-party ES module packages are copied from `node_modules/` into
60
- `public/vendor/` at install time. This makes them servable as static files.
60
+ `src/vendor/` at install time. This makes them servable as static files.
61
61
 
62
62
  ### scripts/vendor-deps.js
63
63
 
@@ -65,7 +65,7 @@ Third-party ES module packages are copied from `node_modules/` into
65
65
  import { cpSync, mkdirSync } from 'node:fs';
66
66
  import { resolve } from 'node:path';
67
67
 
68
- const VENDOR_DIR = resolve('public/vendor');
68
+ const VENDOR_DIR = resolve('src/vendor');
69
69
  const DEPS = [{ name: 'hybrids', src: 'node_modules/hybrids/src' }];
70
70
 
71
71
  mkdirSync(VENDOR_DIR, { recursive: true });
@@ -89,7 +89,7 @@ In `package.json`:
89
89
  }
90
90
  ```
91
91
 
92
- Running `npm install` automatically vendors dependencies. The `public/vendor/`
92
+ Running `npm install` automatically vendors dependencies. The `src/vendor/`
93
93
  directory should be in `.gitignore`.
94
94
 
95
95
  ### Adding a New Dependency
@@ -104,9 +104,9 @@ directory should be in `.gitignore`.
104
104
  ## Import Map
105
105
 
106
106
  The browser resolves bare specifiers like `'hybrids'` via an import map
107
- declared in `public/index.html`.
107
+ declared in `src/index.html`.
108
108
 
109
- ### public/index.html
109
+ ### src/index.html
110
110
 
111
111
  ```html
112
112
  <!DOCTYPE html>
@@ -137,7 +137,7 @@ declared in `public/index.html`.
137
137
 
138
138
  1. Browser encounters `import { html } from 'hybrids'` in any ES module.
139
139
  2. Import map resolves `'hybrids'` → `/vendor/hybrids/index.js`.
140
- 3. Server serves the file from `public/vendor/hybrids/index.js`.
140
+ 3. Server serves the file from `src/vendor/hybrids/index.js`.
141
141
  4. Hybrids' internal imports use relative paths — they resolve naturally.
142
142
 
143
143
  ### Rules
package/lib/check.js CHANGED
@@ -32,7 +32,7 @@ export function loadConfig(projectDir) {
32
32
  docsMax: parseInt(env.SPEC_DOCS_MAX_LINES) || 500,
33
33
  codeExt: (env.SPEC_CODE_EXTENSIONS || '.js,.css').split(','),
34
34
  docsExt: (env.SPEC_DOCS_EXTENSIONS || '.md').split(','),
35
- ignore: (env.SPEC_IGNORE_DIRS || 'node_modules,src/public/vendor,.git,.configs').split(','),
35
+ ignore: (env.SPEC_IGNORE_DIRS || 'node_modules,src/vendor,.git,.configs').split(','),
36
36
  };
37
37
  }
38
38
 
@@ -43,6 +43,7 @@ export const CMDS = {
43
43
  prettier: 'npx prettier --config .configs/.prettierrc --write src scripts',
44
44
  mdlint: 'npx markdownlint-cli2 --config .configs/.markdownlint.jsonc --fix "docs/**/*.md" "*.md"',
45
45
  types: 'npx tsc --project .configs/jsconfig.json',
46
+ audit: 'npm audit --omit=dev',
46
47
  };
47
48
 
48
49
  /**
@@ -77,6 +78,7 @@ export async function check(projectDir, scope) {
77
78
  runCmd('Prettier', CMDS.prettier, projectDir, `${jsFiles} files`),
78
79
  runCmd('Markdown', CMDS.mdlint, projectDir, `${mdFiles} files`),
79
80
  runCmd('JSDoc types', CMDS.types, projectDir, `${jsFiles} files`),
81
+ runCmd('Security audit', CMDS.audit, projectDir),
80
82
  ];
81
83
 
82
84
  const passed = results.filter(Boolean).length;
@@ -40,6 +40,7 @@ export async function writePackageJson(dest, vars, existing) {
40
40
  eslint: '^10.1.0',
41
41
  'eslint-config-prettier': '^10.1.8',
42
42
  'eslint-plugin-jsdoc': '^62.8.1',
43
+ 'eslint-plugin-unused-imports': '^4.0.0',
43
44
  'markdownlint-cli2': '^0.22.0',
44
45
  playwright: '^1.50.0',
45
46
  prettier: '^3.8.1',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techninja/clearstack",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "type": "module",
5
5
  "description": "A no-build web component framework specification — scaffold, validate, and evolve spec-compliant projects",
6
6
  "bin": {
@@ -51,6 +51,7 @@
51
51
  "eslint": "^10.1.0",
52
52
  "eslint-config-prettier": "^10.1.8",
53
53
  "eslint-plugin-jsdoc": "^62.8.1",
54
+ "eslint-plugin-unused-imports": "^4.4.1",
54
55
  "express": "^5.2.1",
55
56
  "hybrids": "^9.1.22",
56
57
  "lucide-static": "^1.7.0",
@@ -19,7 +19,7 @@ app.use(express.static('src'));
19
19
  // SPA fallback
20
20
  app.use((req, res, next) => {
21
21
  if (req.method === 'GET' && !req.path.includes('.') && !req.path.startsWith('/api')) {
22
- return res.sendFile('index.html', { root: 'src/public' });
22
+ return res.sendFile('index.html', { root: 'src' });
23
23
  }
24
24
  next();
25
25
  });
@@ -1,10 +1,11 @@
1
1
  import prettier from 'eslint-config-prettier';
2
2
  import jsdoc from 'eslint-plugin-jsdoc';
3
+ import unusedImports from 'eslint-plugin-unused-imports';
3
4
 
4
5
  export default [
5
6
  {
6
7
  files: ['**/*.js'],
7
- plugins: { jsdoc },
8
+ plugins: { jsdoc, 'unused-imports': unusedImports },
8
9
  languageOptions: {
9
10
  ecmaVersion: 2024,
10
11
  sourceType: 'module',
@@ -34,7 +35,9 @@ export default [
34
35
  'no-var': 'error',
35
36
  'prefer-const': 'error',
36
37
  eqeqeq: ['error', 'always'],
37
- 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
38
+ 'no-unused-vars': 'off',
39
+ 'unused-imports/no-unused-imports': 'error',
40
+ 'unused-imports/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
38
41
  'no-console': 'off',
39
42
 
40
43
  // JSDoc enforcement
@@ -55,10 +58,11 @@ export default [
55
58
  rules: {
56
59
  'jsdoc/require-jsdoc': 'off',
57
60
  'no-unused-vars': 'off',
61
+ 'unused-imports/no-unused-vars': 'off',
58
62
  },
59
63
  },
60
64
  {
61
- ignores: ['node_modules/', 'src/public/vendor/'],
65
+ ignores: ['node_modules/', 'src/vendor/'],
62
66
  },
63
67
  prettier,
64
68
  ];
@@ -14,5 +14,5 @@
14
14
  }
15
15
  },
16
16
  "include": ["../src/**/*.js", "../scripts/**/*.js"],
17
- "exclude": ["../node_modules", "../src/public/vendor", "../**/*.test.js"]
17
+ "exclude": ["../node_modules", "../src/vendor", "../**/*.test.js"]
18
18
  }
@@ -3,7 +3,7 @@ SPEC_CODE_MAX_LINES=150
3
3
  SPEC_DOCS_MAX_LINES=500
4
4
  SPEC_CODE_EXTENSIONS=.js,.css
5
5
  SPEC_DOCS_EXTENSIONS=.md
6
- SPEC_IGNORE_DIRS=node_modules,src/public/vendor,.git,.configs
6
+ SPEC_IGNORE_DIRS=node_modules,src/vendor,.git,.configs
7
7
 
8
8
  # Server
9
9
  PORT={{port}}
@@ -292,7 +292,7 @@ handler resolves `host` to the page component as expected.
292
292
  Because components use light DOM, styles flow naturally:
293
293
 
294
294
  ```
295
- public/index.html
295
+ src/index.html
296
296
  ├── <link> src/styles/reset.css ← base reset
297
297
  ├── <link> src/styles/tokens.css ← :root custom properties
298
298
  ├── <link> src/styles/shared.css ← error states, icons, utilities
@@ -269,6 +269,23 @@ multiple keys.
269
269
 
270
270
  ---
271
271
 
272
+ ## Spec vs Test: Two Different Questions
273
+
274
+ `spec` and `test` answer different questions and run at different times.
275
+
276
+ | | `npm run spec` | `npm test` |
277
+ | ---------- | ------------------------------------------ | -------------------------------- |
278
+ | Asks | Is my code clean? | Does my code work? |
279
+ | Checks | Line counts, lint, format, types, markdown | Unit, integration, browser tests |
280
+ | Speed | Fast (~10s) | Slower (varies) |
281
+ | When | Every save, every change | Before commit, in CI |
282
+ | Auto-fixes | Yes (lint, format, markdown) | No |
283
+
284
+ Spec is the inner dev loop — run it constantly. Tests are the commit gate —
285
+ run them before pushing. CI runs both, in parallel.
286
+
287
+ ---
288
+
272
289
  ## Session Retrospective
273
290
 
274
291
  At the end of each implementation session, ask:
@@ -117,12 +117,12 @@ import {
117
117
 
118
118
  ```
119
119
  project-root/
120
- ├── public/ # Static assets served at /
120
+ ├── src/ # The app served directly, no build
121
121
  │ ├── index.html # App shell with import map
122
- └── vendor/ # Vendored ES module deps (generated)
123
- └── hybrids/ # Copied from node_modules at install
124
-
125
- ├── src/ # Application source (served at /src/)
122
+ ├── vendor/ # Vendored ES modules (generated, gitignored)
123
+ └── hybrids/ # Copied from node_modules at install
124
+ ├── icons.json # Icon sprite (generated, gitignored)
125
+ │ │
126
126
  │ ├── components/ # UI components (atomic design)
127
127
  │ │ ├── atoms/ # Smallest UI primitives
128
128
  │ │ │ └── app-button/
@@ -130,39 +130,47 @@ project-root/
130
130
  │ │ │ ├── app-button.css # Scoped styles
131
131
  │ │ │ └── index.js # Re-export
132
132
  │ │ ├── molecules/ # Compositions of atoms
133
- │ │ │ └── search-bar/
134
133
  │ │ ├── organisms/ # Complex UI sections
135
- │ │ │ └── app-header/
136
134
  │ │ └── templates/ # Page-level layout shells
137
- │ │ └── page-layout/
138
135
  │ │
139
136
  │ ├── pages/ # Route-bound view components
140
- │ │ ├── home/
141
- │ │ │ ├── home-view.js
142
- │ │ │ └── index.js
143
- │ │ └── about/
137
+ │ │ └── home/
138
+ │ │ └── home-view.js
144
139
  │ │
145
140
  │ ├── store/ # Hybrids store model definitions
146
141
  │ │ ├── AppState.js # Singleton: global app state
147
- │ │ └── UserModel.js # Enumerable: user records
142
+ │ │ └── UserPrefs.js # Singleton: user preferences
148
143
  │ │
149
144
  │ ├── router/ # Router shell component
150
145
  │ │ └── index.js
151
146
  │ │
152
147
  │ ├── styles/ # Shared CSS
153
148
  │ │ ├── tokens.css # Design tokens (colors, spacing)
154
- │ │ └── reset.css # Minimal CSS reset
149
+ │ │ ├── reset.css # Minimal CSS reset
150
+ │ │ └── components.css # Aggregates component CSS imports
151
+ │ │
152
+ │ ├── utils/ # Pure helper functions
153
+ │ │ └── formatDate.js
155
154
  │ │
156
- └── utils/ # Pure helper functions
157
- └── formatDate.js
155
+ ├── api/ # Server routes (fullstack only)
156
+ │ ├── entities.js # Generic CRUD router
157
+ │ │ └── schemas.js # JSON Schema registry
158
+ │ │
159
+ │ └── server.js # Express entry point
160
+
161
+ ├── scripts/ # Install/setup scripts
162
+ │ ├── vendor-deps.js # Copies deps to src/vendor/
163
+ │ └── build-icons.js # Extracts Lucide SVGs to src/icons.json
158
164
 
159
- ├── scripts/ # Build/install scripts
160
- │ └── vendor-deps.js # Copies deps to public/vendor/
165
+ ├── .configs/ # Linter/formatter/type configs
166
+ ├── docs/
167
+ │ ├── clearstack/ # Spec docs (synced on update)
168
+ │ └── app-spec/ # Project-specific specs (yours)
161
169
 
162
- ├── src/server.js # Express entry point
163
- ├── package.json # type: "module", postinstall hook
164
- ├── README.md # Project overview
165
- └── FRONTEND_IMPLEMENTATION_RULES.md # This file
170
+ ├── data/ # JSON DB seed (fullstack only)
171
+ ├── .env # Defaults (committed)
172
+ ├── .env.local # Overrides (gitignored)
173
+ └── package.json
166
174
  ```
167
175
 
168
176
  ### Key Conventions
@@ -170,7 +178,7 @@ project-root/
170
178
  - **One component per directory.** Each gets its own folder with `.js`, `.css`,
171
179
  and `index.js` (re-export).
172
180
  - **`src/` is served as-is** — the browser loads these files directly.
173
- - **`public/vendor/`** is gitignored and regenerated on `npm install` via the
181
+ - **`src/vendor/`** is gitignored and regenerated on `npm install` via the
174
182
  `postinstall` script.
175
183
  - **No barrel files** beyond the per-component `index.js`. Import from the
176
184
  component directory, not from a giant `components/index.js`.
@@ -42,33 +42,30 @@ If a `package.json` already exists, Clearstack merges into it — your existing
42
42
  ```
43
43
  your-project/
44
44
  ├── .configs/ # ⟳ Managed — synced on update
45
- │ ├── eslint.config.js
46
- │ ├── .prettierrc
47
- │ ├── jsconfig.json
48
- │ └── web-test-runner.config.js
49
45
  ├── .github/ # CI workflow, PR + issue templates
50
46
  ├── docs/
51
47
  │ ├── clearstack/ # ⟳ Managed — spec docs, synced on update
52
48
  │ └── app-spec/ # ✏️ Yours — project-specific specs
53
- ├── public/ # ✏️ Yours — static assets, index.html, import map
54
- ├── scripts/ # ✏️ Yours — vendor-deps, build-icons
55
- ├── src/
56
- │ ├── api/ # ✏️ Yours server routes (fullstack only)
57
- │ ├── components/ # ✏️ Yours atoms/, molecules/, organisms/
58
- │ ├── pages/ # ✏️ Yours — route-level views
59
- │ ├── store/ # ✏️ Yours — Hybrids store models
60
- │ ├── styles/ # ✏️ Yours — global CSS
61
- │ ├── router/ # ✏️ Yours — client-side routing
62
- │ ├── utils/ # ✏️ Yours — shared helpers
63
- └── server.js # ✏️ Yours — Express entry (fullstack only)
64
- ├── data/ # ✏️ Yours — JSON DB seed (fullstack only)
65
- ├── .env # ✏️ Yours — PORT, spec thresholds
66
- └── package.json # ✏️ Yours (spec scripts merged in)
49
+ ├── scripts/ # ✏️ Yours — setup, test, vendor-deps, build-icons
50
+ ├── src/ # ✏️ Yours — the app, served directly
51
+ ├── index.html # App shell with import map
52
+ │ ├── vendor/ # Vendored ES modules (generated, gitignored)
53
+ │ ├── icons.json # Icon sprite (generated, gitignored)
54
+ │ ├── components/ # atoms/, molecules/, organisms/
55
+ │ ├── pages/ # Route-level views
56
+ │ ├── store/ # Hybrids store models
57
+ │ ├── styles/ # Global CSS
58
+ │ ├── router/ # Client-side routing
59
+ ├── utils/ # Shared helpers
60
+ ├── api/ # Server routes (fullstack only)
61
+ │ └── server.js # Express entry point
62
+ ├── data/ # JSON DB seed (fullstack only)
63
+ ├── .env # Defaults (committed)
64
+ ├── .env.local # Overrides (gitignored)
65
+ └── package.json
67
66
  ```
68
67
 
69
- **⟳ Managed** files are updated when you run `clearstack update`. Review changes via `git diff`.
70
-
71
- **✏️ Yours** files are scaffolded once and never touched by updates. They're your code.
68
+ `src/` is the entire app. `node src/server.js` serves it. For static deploy, point your CDN at `src/`. No build step.
72
69
 
73
70
  ## 4. Install and Run
74
71
 
@@ -76,18 +73,12 @@ your-project/
76
73
  npm install
77
74
  ```
78
75
 
79
- `postinstall` runs `vendor-deps.js` (copies hybrids to `public/vendor/`) and `build-icons.js` (extracts Lucide SVGs to `public/icons.json`).
76
+ `postinstall` runs `vendor-deps.js` (copies hybrids to `src/vendor/`) and `build-icons.js` (extracts Lucide SVGs to `src/icons.json`).
80
77
 
81
78
  ### Fullstack
82
79
 
83
80
  ```bash
84
- npm run dev # node --watch --env-file=.env src/server.js
85
- ```
86
-
87
- ### Static
88
-
89
- ```bash
90
- npx serve public # or any static file server
81
+ npm run dev
91
82
  ```
92
83
 
93
84
  ## 5. Development Rules
@@ -152,7 +143,7 @@ SPEC_CODE_MAX_LINES=150
152
143
  SPEC_DOCS_MAX_LINES=500
153
144
  SPEC_CODE_EXTENSIONS=.js,.css
154
145
  SPEC_DOCS_EXTENSIONS=.md
155
- SPEC_IGNORE_DIRS=node_modules,public/vendor,.git,.configs
146
+ SPEC_IGNORE_DIRS=node_modules,src/vendor,.git,.configs
156
147
  ```
157
148
 
158
149
  ## 9. CI Pipeline
@@ -170,7 +161,7 @@ spec:code → spec:docs → lint → format → typecheck → test
170
161
  | Install Clearstack | `npm install -D @techninja/clearstack` |
171
162
  | Scaffold a project | `npx clearstack init` |
172
163
  | Install dependencies | `npm install` |
173
- | Start dev server | `npm run dev` / `npx serve public` |
164
+ | Start dev server | `npm run dev` |
174
165
  | Lint + format | `npm run lint:fix && npm run format` |
175
166
  | Type check | `npm run typecheck` |
176
167
  | Run tests | `npm test` |
@@ -14,13 +14,13 @@
14
14
 
15
15
  ### Responsibilities
16
16
 
17
- | Route | Serves |
18
- | ------------- | -------------------------------------- |
19
- | `/` | `public/index.html` (app shell) |
20
- | `/vendor/*` | `public/vendor/` (vendored ES modules) |
21
- | `/src/*` | `src/` (application source, as-is) |
22
- | `/api/*` | REST endpoints (see BACKEND_API_SPEC) |
23
- | `/api/events` | SSE stream for realtime sync |
17
+ | Route | Serves |
18
+ | ------------- | ------------------------------------- |
19
+ | `/` | `src/index.html` (app shell) |
20
+ | `/vendor/*` | `src/vendor/` (vendored ES modules) |
21
+ | `/src/*` | `src/` (application source, as-is) |
22
+ | `/api/*` | REST endpoints (see BACKEND_API_SPEC) |
23
+ | `/api/events` | SSE stream for realtime sync |
24
24
 
25
25
  ### Key Rules
26
26
 
@@ -38,7 +38,7 @@ const app = express();
38
38
  const PORT = process.env.PORT || 3000;
39
39
 
40
40
  // Static: vendored deps and app shell
41
- app.use(express.static('public'));
41
+ app.use(express.static('src'));
42
42
 
43
43
  // Static: application source (ES modules served directly)
44
44
  app.use('/src', express.static('src'));
@@ -57,7 +57,7 @@ app.listen(PORT, () => console.log(`http://localhost:${PORT}`));
57
57
  ## Vendor Dependency Loading
58
58
 
59
59
  Third-party ES module packages are copied from `node_modules/` into
60
- `public/vendor/` at install time. This makes them servable as static files.
60
+ `src/vendor/` at install time. This makes them servable as static files.
61
61
 
62
62
  ### scripts/vendor-deps.js
63
63
 
@@ -65,7 +65,7 @@ Third-party ES module packages are copied from `node_modules/` into
65
65
  import { cpSync, mkdirSync } from 'node:fs';
66
66
  import { resolve } from 'node:path';
67
67
 
68
- const VENDOR_DIR = resolve('public/vendor');
68
+ const VENDOR_DIR = resolve('src/vendor');
69
69
  const DEPS = [{ name: 'hybrids', src: 'node_modules/hybrids/src' }];
70
70
 
71
71
  mkdirSync(VENDOR_DIR, { recursive: true });
@@ -89,7 +89,7 @@ In `package.json`:
89
89
  }
90
90
  ```
91
91
 
92
- Running `npm install` automatically vendors dependencies. The `public/vendor/`
92
+ Running `npm install` automatically vendors dependencies. The `src/vendor/`
93
93
  directory should be in `.gitignore`.
94
94
 
95
95
  ### Adding a New Dependency
@@ -104,9 +104,9 @@ directory should be in `.gitignore`.
104
104
  ## Import Map
105
105
 
106
106
  The browser resolves bare specifiers like `'hybrids'` via an import map
107
- declared in `public/index.html`.
107
+ declared in `src/index.html`.
108
108
 
109
- ### public/index.html
109
+ ### src/index.html
110
110
 
111
111
  ```html
112
112
  <!DOCTYPE html>
@@ -137,7 +137,7 @@ declared in `public/index.html`.
137
137
 
138
138
  1. Browser encounters `import { html } from 'hybrids'` in any ES module.
139
139
  2. Import map resolves `'hybrids'` → `/vendor/hybrids/index.js`.
140
- 3. Server serves the file from `public/vendor/hybrids/index.js`.
140
+ 3. Server serves the file from `src/vendor/hybrids/index.js`.
141
141
  4. Hybrids' internal imports use relative paths — they resolve naturally.
142
142
 
143
143
  ### Rules
@@ -1,5 +1,5 @@
1
1
  node_modules/
2
- src/public/vendor/
3
- src/public/icons.json
2
+ src/vendor/
3
+ src/icons.json
4
4
  data/db.json
5
5
  .env.local
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * Extracts SVG path data from lucide-static for icons used in the app.
5
- * Generates public/icons.json — loaded by app-icon at runtime.
5
+ * Generates src/icons.json — loaded by app-icon at runtime.
6
6
  * Runs on `npm postinstall`.
7
7
  */
8
8
 
@@ -12,7 +12,7 @@ import { fileURLToPath } from 'node:url';
12
12
 
13
13
  const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
14
14
  const ICONS_DIR = resolve(ROOT, 'node_modules/lucide-static/icons');
15
- const OUT = resolve(ROOT, 'src/public/icons.json');
15
+ const OUT = resolve(ROOT, 'src/icons.json');
16
16
 
17
17
  /** Icons used in the app — lucide name → app name */
18
18
  const ICON_MAP = {
@@ -83,4 +83,4 @@ for (const [lucideName, appName] of Object.entries(ICON_MAP)) {
83
83
  }
84
84
 
85
85
  writeFileSync(OUT, JSON.stringify(icons, null, 2));
86
- console.log(`✓ Built ${count} icons → src/public/icons.json`);
86
+ console.log(`✓ Built ${count} icons → src/icons.json`);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Copies third-party ES module sources into public/vendor/ so the browser
4
+ * Copies third-party ES module sources into src/vendor/ so the browser
5
5
  * can load them directly via import map. Runs on `npm postinstall`.
6
6
  */
7
7
 
@@ -10,7 +10,7 @@ import { resolve, dirname } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
11
 
12
12
  const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
13
- const VENDOR_DIR = resolve(ROOT, 'src/public/vendor');
13
+ const VENDOR_DIR = resolve(ROOT, 'src/vendor');
14
14
 
15
15
  /** @type {{ name: string, src: string }[]} */
16
16
  const DEPS = [{ name: 'hybrids', src: 'node_modules/hybrids/src' }];
@@ -21,5 +21,5 @@ for (const dep of DEPS) {
21
21
  const src = resolve(ROOT, dep.src);
22
22
  const dest = resolve(VENDOR_DIR, dep.name);
23
23
  cpSync(src, dest, { recursive: true });
24
- console.log(`✓ Vendored: ${dep.name} → src/public/vendor/${dep.name}/`);
24
+ console.log(`✓ Vendored: ${dep.name} → src/vendor/${dep.name}/`);
25
25
  }
@@ -11,7 +11,10 @@ let iconCache = null;
11
11
 
12
12
  /** @type {Promise<Record<string, string>>} */
13
13
  const loading = fetch('/icons.json')
14
- .then((r) => (r.ok ? r : fetch('/public/icons.json')))
14
+ .then((r) => {
15
+ if (r.ok) return r;
16
+ throw new Error('icons.json not found');
17
+ })
15
18
  .then((r) => r.json())
16
19
  .then((data) => {
17
20
  iconCache = data;
@@ -14,7 +14,7 @@
14
14
  <script type="importmap">
15
15
  {
16
16
  "imports": {
17
- "hybrids": "/public/vendor/hybrids/index.js"
17
+ "hybrids": "/vendor/hybrids/index.js"
18
18
  }
19
19
  }
20
20
  </script>
@@ -7,25 +7,3 @@
7
7
  @import '../components/atoms/app-badge/app-badge.css';
8
8
  @import '../components/atoms/app-icon/app-icon.css';
9
9
  @import '../components/atoms/theme-toggle/theme-toggle.css';
10
- @import '../components/atoms/canvas-toolbar/canvas-toolbar.css';
11
-
12
- /* Molecules */
13
- @import '../components/molecules/task-card/task-card.css';
14
- @import '../components/molecules/project-card/project-card.css';
15
- @import '../components/molecules/form-field/form-field.css';
16
-
17
- /* Organisms */
18
- @import '../components/organisms/task-list/task-list.css';
19
- @import '../components/organisms/project-header/project-header.css';
20
- @import '../components/organisms/schema-form/schema-form.css';
21
- @import '../components/organisms/project-canvas/project-canvas.css';
22
-
23
- /* Templates */
24
- @import '../components/templates/page-layout/page-layout.css';
25
-
26
- /* Pages */
27
- @import '../pages/home/home-view.css';
28
- @import '../pages/project/project-view.css';
29
-
30
- /* Router */
31
- @import '../router/router.css';
@@ -12,7 +12,7 @@ app.use(express.static('src'));
12
12
 
13
13
  app.use((req, res, next) => {
14
14
  if (req.method === 'GET' && !req.path.includes('.')) {
15
- return res.sendFile('index.html', { root: 'src/public' });
15
+ return res.sendFile('index.html', { root: 'src' });
16
16
  }
17
17
  next();
18
18
  });