codz-cli 0.1.2 → 0.1.8

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.
package/docs/COMMANDS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # codz commands
2
2
 
3
- Generated from `src/core/commands.ts` (codz 0.1.0). Do not edit by hand; run `npm run docs`.
3
+ Generated from `src/core/commands.ts` (codz 0.1.8). Do not edit by hand; run `npm run docs`.
4
4
 
5
5
  ## Start
6
6
 
@@ -74,6 +74,8 @@ Input: --curl, --url, --clipboard, or a cURL command or URL pasted on stdin, opt
74
74
  | `--no-unwrap` | Keep a {"data": ...} envelope instead of returning data |
75
75
  | `--into <module>` | Call the endpoint from that module's controller (GetX) |
76
76
  | `--no-load` | Do not call the load method from onInit |
77
+ | `--model-dir <folder>` | Write new models into this folder for this command only (e.g. lib/features/orders/models); set modelsFolder in codz.config.yaml to change it for good |
78
+ | `--split` | One file per model, instead of nested models in the file of the model that uses them |
77
79
 
78
80
  Accepts the change options.
79
81
 
@@ -92,6 +94,8 @@ Add every endpoint from an OpenAPI 3, Swagger 2, Postman or HAR file.
92
94
  | `--base-url <url>` | The API's base URL (setup), or only HAR requests under it (import/sync) |
93
95
  | `--include-errors` | HAR: keep failed (non-2xx) responses |
94
96
  | `--skip-conflicts` | Import what works and report endpoints that cannot be added |
97
+ | `--model-dir <folder>` | Write new models into this folder for this command only (e.g. lib/features/orders/models); set modelsFolder in codz.config.yaml to change it for good |
98
+ | `--split` | One file per model, instead of nested models in the file of the model that uses them |
95
99
 
96
100
  Accepts the change options.
97
101
 
@@ -107,6 +111,8 @@ Bring endpoints in line with an API spec: update changed ones, report breaking c
107
111
  | `--tag <tag>` | Only operations with this tag (folder in Postman, host in HAR) (repeatable) |
108
112
  | `--path </users/{id}>` | Path parameters (api add), route path (screen/route add), or a path filter (import/sync) |
109
113
  | `--base-url <url>` | The API's base URL (setup), or only HAR requests under it (import/sync) |
114
+ | `--model-dir <folder>` | Write new models into this folder for this command only (e.g. lib/features/orders/models); set modelsFolder in codz.config.yaml to change it for good |
115
+ | `--split` | One file per model, instead of nested models in the file of the model that uses them |
110
116
 
111
117
  Accepts the change options.
112
118
 
@@ -170,6 +176,58 @@ Every endpoint: method, path, function, response, file.
170
176
 
171
177
  One endpoint: signature, models, every place that uses it.
172
178
 
179
+ ### `codz model <Name> --from-clipboard | --sample <file.json>`
180
+
181
+ A model (and its nested models) from JSON, without an endpoint.
182
+
183
+ Reads the JSON from --from-clipboard, --sample, or standard input (pipe or paste, then Ctrl+D). Nested objects become their own classes (Order → OrderItem, OrderAddress), written in the same file as the main model; --split gives each its own file. A list gives the model of its items; {"data": ...} is unwrapped unless --no-unwrap.
184
+
185
+ Writes into the models folder, or --model-dir <folder> for this model only. An existing model with the same name is replaced only with --force.
186
+
187
+ | Option | |
188
+ | --- | --- |
189
+ | `--from-clipboard` | Read the JSON from the clipboard |
190
+ | `--sample <file.json>` | Response sample; repeat for several samples (repeatable) |
191
+ | `--model-dir <folder>` | Write new models into this folder for this command only (e.g. lib/features/orders/models); set modelsFolder in codz.config.yaml to change it for good |
192
+ | `--no-unwrap` | Keep a {"data": ...} envelope instead of returning data |
193
+ | `--force` | Do it even when codz would normally refuse (see the command) |
194
+ | `--split` | One file per model, instead of nested models in the file of the model that uses them |
195
+
196
+ Accepts the change options.
197
+
198
+ ### `codz field add <Model> <name:type> [...]`
199
+
200
+ Add a field to a model, in every place the model spells it.
201
+
202
+ Types: string, int, double, bool, date, any, a model name, or a list (string[], list<Category>). End with ? for an optional field: rating:double?. The name is the JSON key (created_at); codz writes the property name the project uses (createdAt).
203
+
204
+ TypeScript interfaces, Dart classes (plain, json_serializable, freezed: constructor, fromJson, field and toJson), Kotlin data classes and Swift Codable structs (CodingKeys too). Only the new lines are added; then it lists the files that use the model.
205
+
206
+ Accepts the change options.
207
+
208
+ ### `codz field remove <Model> <field> [...]`
209
+
210
+ Remove a field from a model and list the lines that still use it.
211
+
212
+ Accepts the change options.
213
+
214
+ ### `codz mock serve [--spec <file>] [--port 4010] [--delay 300] [--items 5] [--scenario normal] [--lan]`
215
+
216
+ A local fake API with sample data, so screens show data before the backend exists.
217
+
218
+ Answers the project's endpoints, or every operation in --spec (OpenAPI, Swagger, Postman, HAR), with sample data from the response models. A list and its items share in-memory data, so create, edit and delete work and the list shows the change; restarting resets it. Paths match by their last segments, so /v1/products reaches GET /products. Prints how to point the app at it (emulator, simulator, phone).
219
+
220
+ Scenarios check every screen state: empty (empty lists), error (500), unauthorized (401), slow (3 s), offline (connection drops). Open http://localhost:4010/__codz to switch them, for every endpoint or one, reset the data, and see each request with its body.
221
+
222
+ | Option | |
223
+ | --- | --- |
224
+ | `--spec <file>` | OpenAPI, Swagger, Postman or HAR file to take the endpoints from |
225
+ | `--port <n>` | Port for the mock API (default 4010) |
226
+ | `--delay <ms>` | Wait before each answer, so loading states show (default 300) |
227
+ | `--items <n>` | Items in each list to start with (default 5) |
228
+ | `--scenario <name>` | How the mock answers at the start; switch live at /__codz |
229
+ | `--lan` | Listen on your network too, for a phone on the same Wi-Fi (default: this computer only) |
230
+
173
231
  ### `codz mock add <endpoint>... | --all`
174
232
 
175
233
  Mock responses for building screens before the backend.
@@ -199,7 +257,42 @@ Accepts the change options.
199
257
 
200
258
  ## Screens and app
201
259
 
202
- ### `codz screen add <name> --api <endpoint> [--path /x/:id] [--form]`
260
+ ### `codz assets`
261
+
262
+ Typed names for images, icons and fonts, kept in step with the files; lists unused ones.
263
+
264
+ Flutter: lib/gen/assets.dart (AppImages.logo, AppFonts.inter) and every asset folder and font family in pubspec.yaml. React Native: src/assets/index.ts with require() for each file. Next.js and Vite: paths for public/ (and imports for src/assets in Vite). Android and iOS already have typed resources; codz checks names and lists unused drawables.
265
+
266
+ Run it again after adding or removing files.
267
+
268
+ Accepts the change options.
269
+
270
+ ### `codz env add <name> --base-url <url>`
271
+
272
+ Add an API environment (staging, qa, ...) the app can run against.
273
+
274
+ Next.js and Expo: .env.<name> plus npm scripts that load it (dotenv-cli). Vite: .env.<name> and --mode <name>. React Native CLI: an API_URLS table in env.ts. Flutter: env/<name>.json for --dart-define-from-file, and a VS Code run configuration. Android: an apiUrls table in the app build file, picked with -PapiEnv=<name> into BuildConfig.BASE_URL. iOS: an AppEnvironment enum, picked with APP_ENV in the scheme.
275
+
276
+ The base URL you have now becomes the default environment. Never writes .env.local.
277
+
278
+ | Option | |
279
+ | --- | --- |
280
+ | `--base-url <url>` | The API's base URL (setup), or only HAR requests under it (import/sync) |
281
+ | `--force` | Do it even when codz would normally refuse (see the command) |
282
+
283
+ Accepts the change options.
284
+
285
+ ### `codz env list`
286
+
287
+ The API environments and their base URLs.
288
+
289
+ ### `codz env use <name>`
290
+
291
+ Switch the app to an environment (React Native CLI, Android, iOS).
292
+
293
+ Accepts the change options.
294
+
295
+ ### `codz screen add <name> --api <endpoint> [--path /x/:id] [--paginate] [--search] [--form]`
203
296
 
204
297
  A working screen (route, page, the call, loading, error, data), or a form with --form.
205
298
 
@@ -211,6 +304,8 @@ Android: a Compose screen plus a ViewModel (Hilt, Koin or the service locator) w
211
304
  | `--path </users/{id}>` | Path parameters (api add), route path (screen/route add), or a path filter (import/sync) |
212
305
  | `--initial` | Make it the first screen |
213
306
  | `--form` | A form that sends the endpoint's request body (POST, PUT, PATCH) |
307
+ | `--paginate` | List screens: load page after page as you scroll (the endpoint needs a page or offset parameter) |
308
+ | `--search` | List screens: a search box (on the server with a q/search parameter, otherwise over the loaded items) |
214
309
 
215
310
  Accepts the change options.
216
311
 
@@ -250,6 +345,8 @@ Everything is planned in a private copy of the project and shown as one diff: on
250
345
  | `--no-tests` | Do not generate tests |
251
346
  | `--skip-conflicts` | Import what works and report endpoints that cannot be added |
252
347
  | `--no-install` | Do not install dependencies |
348
+ | `--model-dir <folder>` | Write new models into this folder for this command only (e.g. lib/features/orders/models); set modelsFolder in codz.config.yaml to change it for good |
349
+ | `--split` | One file per model, instead of nested models in the file of the model that uses them |
253
350
 
254
351
  Accepts the change options.
255
352
 
@@ -299,6 +396,16 @@ Add a language: copies every key from the default one.
299
396
 
300
397
  Accepts the change options.
301
398
 
399
+ ### `codz locale extract [files...]`
400
+
401
+ Move the text in screens into the locale files and read it back.
402
+
403
+ Finds Text(...) and hint, label and tooltip text (Flutter), JSX text and placeholder, title, alt and aria-label (React, Next.js, React Native), and Text("...") in Compose. Writes AppLocalizations.of(context)!.key (ARB, wiring it into MaterialApp the first time), 'key'.tr (GetX), t('key') with next-intl or react-i18next, or stringResource(R.string.key) with strings.xml.
404
+
405
+ Keys are named after the screen (products.nothingHereYet); text in several screens goes under common, and text that already has a key reuses it. Without files, every screen.
406
+
407
+ Accepts the change options.
408
+
302
409
  ### `codz locale key <key> "<text>" [--value hi="..."]`
303
410
 
304
411
  Add a text key to every language file.
@@ -380,12 +487,6 @@ Accepts the change options.
380
487
 
381
488
  Find problems in codz.config.yaml (exit 1 on errors).
382
489
 
383
- ### `codz mcp [--cwd <project>]`
384
-
385
- Serve codz to AI agents and editors over the Model Context Protocol (stdio).
386
-
387
- Every command becomes a tool (api add → api_add). Tools that edit code return a diff and a plan id; the apply tool applies it. Resources: codz://project and codz://commands.
388
-
389
490
  ### `codz completion bash|zsh|fish`
390
491
 
391
492
  Print the shell completion script.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codz-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.8",
4
4
  "description": "CLI that adds API models, screens, routes and tests to Flutter, Next.js, React Native, Android and iOS apps in your project's own style, from OpenAPI or cURL.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "typescript": "^5.8.0"
35
35
  },
36
36
  "license": "MIT",
37
- "author": "pradeep817",
37
+ "author": "codz-dev",
38
38
  "repository": {
39
39
  "type": "git",
40
40
  "url": "git+https://github.com/pradeep817/codz.git"
@@ -53,7 +53,6 @@
53
53
  "react",
54
54
  "android",
55
55
  "ios",
56
- "swiftui",
57
- "mcp"
56
+ "swiftui"
58
57
  ]
59
58
  }
@@ -1,175 +0,0 @@
1
- // codz mcp: a Model Context Protocol server on stdio, so AI agents and
2
- // editors can call codz as tools.
3
- //
4
- // Every command in the command table becomes a tool (api add → api_add) whose
5
- // input is its options plus the positional arguments. Tools run the codz
6
- // CLI itself with --json, in the project folder, so they behave exactly like
7
- // the command line. Commands that edit code never write: they return the diff
8
- // and a plan id, and the `apply` tool applies that plan exactly as shown.
9
- //
10
- // Resources: codz://project (what codz knows about the project) and
11
- // codz://commands (every command and option, as markdown).
12
- import { spawnSync } from 'node:child_process';
13
- import { createInterface } from 'node:readline';
14
- import { fileURLToPath } from 'node:url';
15
- import { CHANGE_FLAGS, COMMAND_LIST, FLAGS, GLOBAL_FLAGS, renderMarkdown } from "../core/commands.js";
16
- const PROTOCOLS = ['2025-06-18', '2025-03-26', '2024-11-05'];
17
- /** Commands that make no sense as tools: they need a terminal, or set up codz itself. */
18
- const NOT_TOOLS = new Set(['completion', 'init', 'mcp', 'login', 'logout']);
19
- export function toolName(spec) {
20
- return `${spec.cmd}${spec.sub ? `_${spec.sub}` : ''}`.replace(/-/g, '_');
21
- }
22
- export function mcpTools() {
23
- const tools = [];
24
- for (const spec of COMMAND_LIST) {
25
- if (NOT_TOOLS.has(spec.cmd))
26
- continue;
27
- const properties = {};
28
- if (spec.usage.replace(/\[?--[^\]]*\]?/g, '').trim())
29
- properties.args = { type: 'array', items: { type: 'string' }, description: `Positional arguments: ${spec.usage}` };
30
- for (const f of spec.flags) {
31
- if (CHANGE_FLAGS.includes(f) || GLOBAL_FLAGS.includes(f) || f.length === 1)
32
- continue;
33
- const def = FLAGS[f];
34
- const description = def.desc + (def.choices ? ` (${def.choices.join(', ')})` : '');
35
- properties[f] = def.value ? (def.repeat ? { type: 'array', items: { type: 'string' }, description } : { type: 'string', description }) : { type: 'boolean', description };
36
- }
37
- if (spec.cmd === 'apply') {
38
- properties['allow-dirty'] = { type: 'boolean', description: FLAGS['allow-dirty'].desc };
39
- properties['no-verify'] = { type: 'boolean', description: FLAGS['no-verify'].desc };
40
- }
41
- const writes = spec.change && !(spec.cmd === 'doctor');
42
- tools.push({
43
- name: toolName(spec),
44
- description: `${spec.summary}.${writes || spec.cmd === 'new' ? ' Returns the diff and a plan id; nothing is written until you call apply with that id.' : ''}${spec.cmd === 'new' ? ' Paid starters are never downloaded from here: the user downloads one once in a terminal (codz new or starters show), then it can be planned.' : ''}${spec.cmd === 'doctor' ? ' With fix: true it returns a plan to apply instead of reporting.' : ''}${spec.notes?.length ? ` ${spec.notes.join(' ')}` : ''}`,
45
- inputSchema: { type: 'object', properties, additionalProperties: false },
46
- spec,
47
- });
48
- }
49
- return tools;
50
- }
51
- /** The CLI arguments for a tool call. Change commands always get --plan, so a tool never writes by itself. */
52
- export function cliArgs(tool, input) {
53
- const { spec } = tool;
54
- const out = [spec.cmd, ...(spec.sub ? [spec.sub] : [])];
55
- const args = Array.isArray(input.args) ? input.args.map(String) : [];
56
- // A positional argument that looks like an option (--cwd, --yes, --force, --root) would let a tool call widen what it
57
- // may do. Options only come through the schema's named properties, which are checked below.
58
- const flagLike = args.find((a) => a.startsWith('-'));
59
- if (flagLike)
60
- throw new Error(`Positional arguments cannot start with "-" (got "${flagLike.slice(0, 40)}"); pass options as named properties.`);
61
- out.push(...args);
62
- for (const [k, v] of Object.entries(input)) {
63
- if (k === 'args' || v === undefined || v === null || v === false)
64
- continue;
65
- if (!(k in tool.inputSchema.properties))
66
- throw new Error(`Unknown argument "${k}" for ${tool.name}.`);
67
- if (v === true)
68
- out.push(`--${k}`);
69
- else if (Array.isArray(v))
70
- for (const x of v)
71
- out.push(`--${k}`, String(x));
72
- else
73
- out.push(`--${k}`, String(v));
74
- }
75
- // codz new only ever plans from an agent; a person applies (and a paid starter must already be downloaded).
76
- if (spec.cmd === 'new')
77
- out.push('--plan');
78
- if (spec.change && spec.cmd !== 'apply' && !(spec.cmd === 'doctor' && input.fix !== true) && !(spec.cmd === 'api' && spec.sub === 'sync' && input.check === true))
79
- out.push('--plan');
80
- out.push('--json', '--no-color');
81
- return out;
82
- }
83
- const BIN = fileURLToPath(new URL('../../bin/codz.js', import.meta.url));
84
- function runCli(args, cwd) {
85
- const r = spawnSync(process.execPath, [BIN, ...args], { cwd, encoding: 'utf8', input: '', env: { ...process.env, NO_COLOR: '1', CODZ_MCP: '1' }, maxBuffer: 64 * 1024 * 1024, timeout: 120_000 });
86
- return { code: r.status ?? 1, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
87
- }
88
- /** Handles one message; returns the response, or null for notifications. */
89
- export function handleMessage(msg, cwd, version, tools = mcpTools()) {
90
- const reply = (result) => ({ jsonrpc: '2.0', id: msg.id, result });
91
- const fail = (code, message) => ({ jsonrpc: '2.0', id: msg.id, error: { code, message } });
92
- if (msg.id === undefined)
93
- return null; // notifications (initialized, cancelled) need no answer
94
- switch (msg.method) {
95
- case 'initialize': {
96
- const asked = String(msg.params?.protocolVersion ?? '');
97
- return reply({
98
- protocolVersion: PROTOCOLS.includes(asked) ? asked : PROTOCOLS[0],
99
- capabilities: { tools: { listChanged: false }, resources: { listChanged: false } },
100
- serverInfo: { name: 'codz', version },
101
- instructions: 'codz understands this Next.js or Flutter project and changes it deterministically. Start with explain. Tools that edit code return a diff and a planId; review it, then call apply with the planId. Use dry runs freely: nothing is written until apply.',
102
- });
103
- }
104
- case 'ping':
105
- return reply({});
106
- case 'tools/list':
107
- return reply({ tools: tools.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })) });
108
- case 'tools/call': {
109
- const tool = tools.find((t) => t.name === msg.params?.name);
110
- if (!tool)
111
- return fail(-32602, `Unknown tool: ${String(msg.params?.name)}`);
112
- let args;
113
- try {
114
- args = cliArgs(tool, msg.params?.arguments ?? {});
115
- }
116
- catch (e) {
117
- return reply({ content: [{ type: 'text', text: e.message }], isError: true });
118
- }
119
- const r = runCli(args, cwd);
120
- let isError = r.code !== 0;
121
- let text = r.stdout.trim();
122
- try {
123
- const json = JSON.parse(text);
124
- // A failed check (exit 1) is still a useful answer; only {"ok": false} is an error.
125
- isError = json.ok === false;
126
- text = JSON.stringify(json, null, 2);
127
- }
128
- catch {
129
- text = [text, r.stderr.trim()].filter(Boolean).join('\n') || `codz exited with code ${r.code}`;
130
- }
131
- return reply({ content: [{ type: 'text', text }], isError });
132
- }
133
- case 'resources/list':
134
- return reply({
135
- resources: [
136
- { uri: 'codz://project', name: 'Project map', description: 'Framework, conventions, endpoints, models, routes and locales codz found', mimeType: 'application/json' },
137
- { uri: 'codz://commands', name: 'codz commands', description: 'Every command and option', mimeType: 'text/markdown' },
138
- ],
139
- });
140
- case 'resources/read': {
141
- const uri = String(msg.params?.uri ?? '');
142
- if (uri === 'codz://commands')
143
- return reply({ contents: [{ uri, mimeType: 'text/markdown', text: renderMarkdown(version) }] });
144
- if (uri === 'codz://project') {
145
- const r = runCli(['explain', '--json', '--no-color'], cwd);
146
- return reply({ contents: [{ uri, mimeType: 'application/json', text: r.stdout.trim() || r.stderr.trim() }] });
147
- }
148
- return fail(-32002, `Unknown resource: ${uri}`);
149
- }
150
- default:
151
- return fail(-32601, `Method not found: ${msg.method}`);
152
- }
153
- }
154
- /** Runs the server until stdin closes. Logs go to stderr; stdout carries only protocol messages. */
155
- export function serveMcp(cwd, version) {
156
- const tools = mcpTools();
157
- process.stderr.write(`codz mcp: serving ${tools.length} tools for ${cwd}\n`);
158
- const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
159
- rl.on('line', (line) => {
160
- if (!line.trim())
161
- return;
162
- let msg;
163
- try {
164
- msg = JSON.parse(line);
165
- }
166
- catch {
167
- process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }) + '\n');
168
- return;
169
- }
170
- const res = handleMessage(msg, cwd, version, tools);
171
- if (res)
172
- process.stdout.write(JSON.stringify(res) + '\n');
173
- });
174
- return new Promise((resolve) => rl.on('close', () => resolve(0)));
175
- }