@firenet-designs/fnd-cli 2.0.0 → 2.2.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.
@@ -1,5 +1,36 @@
1
1
  {
2
2
  "commands": {
3
+ "backfill-project": {
4
+ "aliases": [],
5
+ "args": {
6
+ "shop": {
7
+ "description": "Shopify store handle, passed to Claude as a hint",
8
+ "name": "shop",
9
+ "required": false
10
+ }
11
+ },
12
+ "description": "Backfill missing scaffold files in an EXISTING repo + theme: .gitignore, .shopifyignore, CLAUDE.md. Each is created only if absent — existing files are left untouched. Does NOT git init, pull the theme, commit, or touch GitHub.\n\nRequires the claude CLI (npm install -g @anthropic-ai/claude-code) for the CLAUDE.md step.",
13
+ "examples": [
14
+ "<%= config.bin %> <%= command.id %>",
15
+ "<%= config.bin %> <%= command.id %> my-store",
16
+ "<%= config.bin %> <%= command.id %> my-store \"extra hints\""
17
+ ],
18
+ "flags": {},
19
+ "hasDynamicHelp": false,
20
+ "hiddenAliases": [],
21
+ "id": "backfill-project",
22
+ "pluginAlias": "@firenet-designs/fnd-cli",
23
+ "pluginName": "@firenet-designs/fnd-cli",
24
+ "pluginType": "core",
25
+ "strict": false,
26
+ "enableJsonFlag": false,
27
+ "isESM": true,
28
+ "relativePath": [
29
+ "dist",
30
+ "commands",
31
+ "backfill-project.js"
32
+ ]
33
+ },
3
34
  "create-app": {
4
35
  "aliases": [],
5
36
  "args": {},
@@ -40,6 +71,37 @@
40
71
  "create-app.js"
41
72
  ]
42
73
  },
74
+ "create-project": {
75
+ "aliases": [],
76
+ "args": {
77
+ "shop": {
78
+ "description": "Shopify store handle — pulls the live theme from <shop>.myshopify.com (omit to skip)",
79
+ "name": "shop",
80
+ "required": false
81
+ }
82
+ },
83
+ "description": "Scaffold a new client project: git on branch production, ignore files, Shopify theme pull, a Claude-generated CLAUDE.md, then a private GitHub repo under the FireNet-Designs org.\n\nRequires the claude CLI (npm install -g @anthropic-ai/claude-code) for the CLAUDE.md step.\n\nGitHub auth comes from YOUR environment — run `gh auth login` once, or export GH_TOKEN in your shell profile. This CLI never stores credentials. Override the org with FND_GH_ORG.",
84
+ "examples": [
85
+ "<%= config.bin %> <%= command.id %>",
86
+ "<%= config.bin %> <%= command.id %> my-store",
87
+ "<%= config.bin %> <%= command.id %> my-store \"Acme storefront, Klaviyo\""
88
+ ],
89
+ "flags": {},
90
+ "hasDynamicHelp": false,
91
+ "hiddenAliases": [],
92
+ "id": "create-project",
93
+ "pluginAlias": "@firenet-designs/fnd-cli",
94
+ "pluginName": "@firenet-designs/fnd-cli",
95
+ "pluginType": "core",
96
+ "strict": false,
97
+ "enableJsonFlag": false,
98
+ "isESM": true,
99
+ "relativePath": [
100
+ "dist",
101
+ "commands",
102
+ "create-project.js"
103
+ ]
104
+ },
43
105
  "token": {
44
106
  "aliases": [],
45
107
  "args": {},
@@ -120,7 +182,115 @@
120
182
  "commands",
121
183
  "token.js"
122
184
  ]
185
+ },
186
+ "workspace:cleanup": {
187
+ "aliases": [],
188
+ "args": {},
189
+ "description": "Unmount a leftover reverse-SSHFS mount on the remote — use this if a workspace session dropped before it could clean up after itself.\n\nUnmounts the remote mount and removes the matching entry from your local authorized_keys. With no --mount, it targets the same path `workspace` would use for the current directory.",
190
+ "examples": [
191
+ "<%= config.bin %> <%= command.id %> --ssh user@host",
192
+ "<%= config.bin %> <%= command.id %> --ssh user@host --mount /home/fnd/cole/fnd-cli"
193
+ ],
194
+ "flags": {
195
+ "mount": {
196
+ "description": "exact remote mount path to unmount (defaults to the current dir mapping)",
197
+ "name": "mount",
198
+ "hasDynamicHelp": false,
199
+ "multiple": false,
200
+ "type": "option"
201
+ },
202
+ "mount-base": {
203
+ "description": "base dir on the remote, used to derive the default mount path",
204
+ "name": "mount-base",
205
+ "default": "/home/fnd",
206
+ "hasDynamicHelp": false,
207
+ "multiple": false,
208
+ "type": "option"
209
+ },
210
+ "ssh": {
211
+ "description": "remote to connect to, as user@host",
212
+ "name": "ssh",
213
+ "required": true,
214
+ "hasDynamicHelp": false,
215
+ "multiple": false,
216
+ "type": "option"
217
+ }
218
+ },
219
+ "hasDynamicHelp": false,
220
+ "hiddenAliases": [],
221
+ "id": "workspace:cleanup",
222
+ "pluginAlias": "@firenet-designs/fnd-cli",
223
+ "pluginName": "@firenet-designs/fnd-cli",
224
+ "pluginType": "core",
225
+ "strict": true,
226
+ "enableJsonFlag": false,
227
+ "isESM": true,
228
+ "relativePath": [
229
+ "dist",
230
+ "commands",
231
+ "workspace",
232
+ "cleanup.js"
233
+ ]
234
+ },
235
+ "workspace": {
236
+ "aliases": [],
237
+ "args": {},
238
+ "description": "Open a remote workspace: mirror the current directory onto a remote Linux box via reverse SSHFS and drop into a shell there, cleaning everything up on exit.\n\nThe remote reaches your machine through the ssh tunnel (-R), so your local SSH server is never exposed to the internet or port-forwarded. For the back-connection the remote authenticates with its OWN SSH key, which is temporarily added to your local authorized_keys and removed again on exit. Locally you need an SSH *server* running (sshd), not sshfs — sshfs runs on the remote.",
239
+ "examples": [
240
+ "<%= config.bin %> <%= command.id %> --ssh user@203.0.113.4",
241
+ "<%= config.bin %> <%= command.id %> --ssh user@host --port 40222",
242
+ "<%= config.bin %> <%= command.id %> --ssh user@host --mount-base /home/fnd --local-ssh-port 2222"
243
+ ],
244
+ "flags": {
245
+ "local-ssh-port": {
246
+ "description": "port your LOCAL SSH server listens on (the tunnel forwards back to this)",
247
+ "name": "local-ssh-port",
248
+ "default": 22,
249
+ "hasDynamicHelp": false,
250
+ "multiple": false,
251
+ "type": "option"
252
+ },
253
+ "mount-base": {
254
+ "description": "base dir on the remote; the mount lands at <base>/<local-user>/<dir-name>",
255
+ "name": "mount-base",
256
+ "default": "/home/fnd",
257
+ "hasDynamicHelp": false,
258
+ "multiple": false,
259
+ "type": "option"
260
+ },
261
+ "port": {
262
+ "char": "p",
263
+ "description": "reverse-tunnel port opened on the remote (random 20000-60000 if omitted)",
264
+ "name": "port",
265
+ "hasDynamicHelp": false,
266
+ "multiple": false,
267
+ "type": "option"
268
+ },
269
+ "ssh": {
270
+ "description": "remote to connect to, as user@host",
271
+ "name": "ssh",
272
+ "required": true,
273
+ "hasDynamicHelp": false,
274
+ "multiple": false,
275
+ "type": "option"
276
+ }
277
+ },
278
+ "hasDynamicHelp": false,
279
+ "hiddenAliases": [],
280
+ "id": "workspace",
281
+ "pluginAlias": "@firenet-designs/fnd-cli",
282
+ "pluginName": "@firenet-designs/fnd-cli",
283
+ "pluginType": "core",
284
+ "strict": true,
285
+ "enableJsonFlag": false,
286
+ "isESM": true,
287
+ "relativePath": [
288
+ "dist",
289
+ "commands",
290
+ "workspace",
291
+ "index.js"
292
+ ]
123
293
  }
124
294
  },
125
- "version": "2.0.0"
295
+ "version": "2.2.0"
126
296
  }
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@firenet-designs/fnd-cli",
3
3
  "description": "A new CLI generated with oclif",
4
- "version": "2.0.0",
4
+ "version": "2.2.0",
5
5
  "author": "Cole Denslow",
6
+ "contributors": [
7
+ "Justin Schellenberg"
8
+ ],
6
9
  "bin": {
7
10
  "fnd": "bin/run.js"
8
11
  },
@@ -26,18 +29,22 @@
26
29
  "eslint": "^9",
27
30
  "eslint-config-oclif": "^6",
28
31
  "eslint-config-prettier": "^10",
29
- "mocha": "^10",
32
+ "mocha": "^11",
30
33
  "oclif": "^4",
31
- "shx": "^0.3.3",
34
+ "shx": "^0.4.0",
32
35
  "ts-node": "^10",
33
36
  "typescript": "^5"
34
37
  },
35
38
  "engines": {
36
39
  "node": ">=24.0.0"
37
40
  },
41
+ "overrides": {
42
+ "glob": "^13"
43
+ },
38
44
  "files": [
39
45
  "./bin",
40
46
  "./dist",
47
+ "./prompts",
41
48
  "./oclif.manifest.json"
42
49
  ],
43
50
  "homepage": "https://github.com/FireNet-Designs/fnd-cli",
@@ -52,7 +59,7 @@
52
59
  },
53
60
  "type": "module",
54
61
  "oclif": {
55
- "bin": "fnd-cli",
62
+ "bin": "fnd",
56
63
  "dirname": "fnd-cli",
57
64
  "commands": "./dist/commands",
58
65
  "hooks": {
@@ -67,6 +74,9 @@
67
74
  "topics": {
68
75
  "hello": {
69
76
  "description": "Say hello to the world and others"
77
+ },
78
+ "workspace": {
79
+ "description": "Open a remote reverse-SSHFS workspace and run Claude on it"
70
80
  }
71
81
  }
72
82
  },
@@ -0,0 +1,183 @@
1
+ # Init Project (Auto) — Generate & Write CLAUDE.md, No Questions
2
+
3
+ You are generating a `CLAUDE.md` for the code repo in the **current working directory** and writing it to disk at `./CLAUDE.md`. This is a non-interactive, one-shot run.
4
+
5
+ ## Hard rules
6
+
7
+ - **Do NOT ask the user anything.** There is no human watching this run. Infer every field from the repo. Where you genuinely cannot infer a value, use the sensible default noted below — never a placeholder like `[fill in]`.
8
+ - **Write the file yourself** to `./CLAUDE.md` using the Write tool. Do not print it in a code block and stop.
9
+ - If `./CLAUDE.md` already exists, read it first and treat the run as an update: keep any human-written project notes, refresh the inferred sections.
10
+ - Keep the whole thing tight. This file is loaded on every future session in the repo — no filler.
11
+
12
+ ## Step 1 — Inspect the repo (read-only)
13
+
14
+ Look at the current directory to infer the fields. Useful signals:
15
+
16
+ - `package.json` → name, scripts (dev/test/lint/typecheck/build/deploy), dependencies (stack + runtime via `engines`), entry (`main`/`module`/`exports`).
17
+ - `shopify.app.toml` → **Shopify App**. Look for an `extensions/` dir → note each extension and its `shopify.extension.toml`.
18
+ - `shopify.theme.toml`, or top-level `templates/ sections/ snippets/ layout/ config/` → **Shopify Theme**.
19
+ - `@shopify/hydrogen` in deps, or `app/routes/` + `remix`/`vite` → **Shopify Hydrogen**.
20
+ - `next.config.*` or `next` in deps → **Next.js**.
21
+ - An Express/Fastify/Hono server entry with no frontend → **Node API**.
22
+ - Anything else → **Custom** (write 4-6 conventions tailored to the actual stack).
23
+ - `tsconfig.json`, lockfile type (`package-lock.json`/`pnpm-lock.yaml`/`yarn.lock`/`bun.lockb`), `.nvmrc`/`engines` → runtime + package manager.
24
+ - `.env.example` / `.env.sample` → external services (Klaviyo, Stripe, Shopify Admin API, a DB URL, etc.).
25
+ - Top two levels of the directory tree → Project Structure block.
26
+
27
+ Defaults when a signal is missing: Type = `Custom`; Runtime = latest LTS Node; Build output = `none`; External Services = omit the section; commands = omit rows that don't exist.
28
+
29
+ ## Step 2 — Write `./CLAUDE.md` with exactly this structure
30
+
31
+ ```
32
+ # [Project Name]
33
+
34
+ **Type:** [Project Type]
35
+ **Stack:** [Stack — comma separated]
36
+ **Runtime:** [Runtime + package manager]
37
+
38
+ ## What This Is
39
+
40
+ [One or two sentences inferred from README/package.json description. If none, describe it from the code.]
41
+
42
+ ---
43
+
44
+ ## Engineering Principles
45
+
46
+ These apply to every file touched in this project.
47
+
48
+ | Principle | Rule |
49
+ |-----------|------|
50
+ | Separation of concerns | One module, one reason to change |
51
+ | No side effects at the core | Pure logic in the middle, IO/network/DB/filesystem only at the edges |
52
+ | Open/Closed | Extend by adding — modify existing code only to fix bugs, never for a new caller |
53
+ | WET before DRY | Duplicate until three identical-shape instances, then extract |
54
+ | No unnecessary abstraction | If you can delete it and nothing breaks, delete it |
55
+ | Boundary validation only | Validate user input and external API responses — trust internal code |
56
+ | Comments explain WHY | Not what. Never describe what the code does. |
57
+
58
+ ---
59
+
60
+ ## Project Structure
61
+
62
+ ```
63
+ [Top two levels of the directory tree]
64
+ ```
65
+
66
+ **Entry point:** `[entry point]`
67
+ **Build output:** `[build output]` — generated, never edit directly
68
+
69
+ ---
70
+
71
+ ## Conventions
72
+
73
+ [Insert the matching preset block below for the detected type. For Custom, write 4-6 stack-specific rules, one per line, imperative voice.]
74
+
75
+ ---
76
+
77
+ ## Workflow
78
+
79
+ | Task | Command |
80
+ |------|---------|
81
+ | Dev | `[dev]` |
82
+ | Test | `[test]` |
83
+ | Lint | `[lint]` |
84
+ | Type check | `[typecheck]` |
85
+ | Build | `[build]` |
86
+ | Deploy | `[deploy]` |
87
+
88
+ (Omit rows with no real command.)
89
+
90
+ ---
91
+
92
+ ## External Services
93
+
94
+ [One line per service + what it's for. Omit this whole section if none.]
95
+
96
+ ---
97
+
98
+ ## Package Security
99
+
100
+ - Never install a package younger than 14 days — let the community catch malicious releases first.
101
+ - Read every install command before approving the dependencies it pulls in.
102
+
103
+ ---
104
+
105
+ ## Agent Behavior
106
+
107
+ - **Never ask the user to test, run, or verify anything.** Claude owns the full dev loop — write, run, debug, fix, repeat until it works.
108
+ - If something genuinely cannot be verified (e.g. a visual UI state), say so explicitly. Never claim success without verifying.
109
+ - **Destructive commands require confirmation first.** Destructive = force push, `reset --hard`, deleting files/branches, dropping tables, removing packages, overwriting uncommitted work.
110
+ - Everything else: just run it. For complex or non-obvious commands, give one line of context — then run without waiting.
111
+
112
+ ---
113
+
114
+ ## Do Not Touch
115
+
116
+ - `[build output]` — generated output, never edit directly
117
+ - `.env` / `.env.*` — never read or write secret values; use `.env.example` for shape only
118
+ - Lockfiles — source of truth, never edit manually
119
+ - `node_modules/` — never edit
120
+ [Any extra off-limits dirs you spotted]
121
+ ```
122
+
123
+ ## Convention presets
124
+
125
+ ### Shopify Theme
126
+ ```
127
+ - Liquid templates live in `templates/`, `sections/`, `snippets/`, `layout/`
128
+ - CSS: `assets/[name].css`; JS: `assets/[name].js` — no bundler unless one is set up
129
+ - `config/settings_data.json` is written by Shopify admin — read but treat as volatile
130
+ - `config/settings_schema.json` is the source of truth for theme settings
131
+ - Never inline styles or scripts in Liquid — use asset references
132
+ - `{% schema %}` blocks must be valid JSON; verify with `shopify theme check`
133
+ ```
134
+
135
+ ### Shopify App
136
+ ```
137
+ - App entry: `[entry]`; Shopify CLI manages tunneling + dev environment
138
+ - `shopify.app.toml` — app config, commit it
139
+ - Extensions live in `extensions/[name]/`, each with its own `shopify.extension.toml`
140
+ - Never hardcode shop domain or access tokens — read from env or session
141
+ - Webhook handlers must verify HMAC before processing
142
+ ```
143
+
144
+ **If this is a Shopify App with extensions, ALSO append this block to the `Conventions` section verbatim:**
145
+ ```
146
+ ### Extension Constraints (hard platform limits)
147
+ - **No third-party libraries inside extension code.** Extensions run in a constrained sandbox — write plain code, no npm deps bundled into the extension.
148
+ - Hard limits per extension: **128 kB input / 11M instructions / 20 kB output.** Exceeding any of these fails the function at runtime.
149
+ - Keep extension logic minimal and allocation-free in hot paths; do work in the app backend where possible.
150
+ ```
151
+
152
+ ### Shopify Hydrogen
153
+ ```
154
+ - Remix file routing: `app/routes/` maps 1:1 to URL segments
155
+ - Loaders run server-side; actions handle mutations
156
+ - Storefront API queries live in `app/graphql/` as `.graphql` files
157
+ - Set cache headers in the loader return — never skip them on product/collection routes
158
+ - `public/` is served as-is; `app/assets/` goes through Vite
159
+ ```
160
+
161
+ ### Next.js
162
+ ```
163
+ - App Router: `app/`; `page.tsx` = route, `layout.tsx` = shell
164
+ - Server Components by default — add `"use client"` only when necessary
165
+ - API routes: `app/api/[route]/route.ts`
166
+ - Only `NEXT_PUBLIC_`-prefixed env vars are client-visible
167
+ - Always use `next/image` for content images, never raw `<img>`
168
+ - Never fetch in a Client Component when a Server Component can do it
169
+ ```
170
+
171
+ ### Node API
172
+ ```
173
+ - Route handlers in `src/routes/`, one file per resource
174
+ - Middleware in `src/middleware/`, applied in `src/app.ts`
175
+ - Validate request bodies at the route boundary only — not inside service functions
176
+ - Service functions are pure: typed inputs, typed outputs
177
+ - Database access only inside `src/db/` or `src/repositories/` — never in routes
178
+ - Environment config loaded once in `src/config.ts`
179
+ ```
180
+
181
+ ## Step 3 — Confirm
182
+
183
+ After writing, print one line: the absolute path of the file written and the detected project type. Nothing else.