trickle-cli 0.1.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 (125) hide show
  1. package/dist/api-client.d.ts +208 -0
  2. package/dist/api-client.js +237 -0
  3. package/dist/commands/annotate.d.ts +6 -0
  4. package/dist/commands/annotate.js +433 -0
  5. package/dist/commands/audit.d.ts +7 -0
  6. package/dist/commands/audit.js +82 -0
  7. package/dist/commands/auto.d.ts +8 -0
  8. package/dist/commands/auto.js +268 -0
  9. package/dist/commands/capture.d.ts +14 -0
  10. package/dist/commands/capture.js +271 -0
  11. package/dist/commands/check.d.ts +6 -0
  12. package/dist/commands/check.js +408 -0
  13. package/dist/commands/codegen.d.ts +21 -0
  14. package/dist/commands/codegen.js +129 -0
  15. package/dist/commands/coverage.d.ts +13 -0
  16. package/dist/commands/coverage.js +126 -0
  17. package/dist/commands/dashboard.d.ts +1 -0
  18. package/dist/commands/dashboard.js +83 -0
  19. package/dist/commands/dev.d.ts +14 -0
  20. package/dist/commands/dev.js +319 -0
  21. package/dist/commands/diff.d.ts +7 -0
  22. package/dist/commands/diff.js +79 -0
  23. package/dist/commands/docs.d.ts +13 -0
  24. package/dist/commands/docs.js +383 -0
  25. package/dist/commands/errors.d.ts +7 -0
  26. package/dist/commands/errors.js +180 -0
  27. package/dist/commands/export.d.ts +18 -0
  28. package/dist/commands/export.js +238 -0
  29. package/dist/commands/functions.d.ts +6 -0
  30. package/dist/commands/functions.js +71 -0
  31. package/dist/commands/infer.d.ts +14 -0
  32. package/dist/commands/infer.js +275 -0
  33. package/dist/commands/init.d.ts +5 -0
  34. package/dist/commands/init.js +395 -0
  35. package/dist/commands/mock.d.ts +5 -0
  36. package/dist/commands/mock.js +232 -0
  37. package/dist/commands/openapi.d.ts +8 -0
  38. package/dist/commands/openapi.js +82 -0
  39. package/dist/commands/overview.d.ts +11 -0
  40. package/dist/commands/overview.js +266 -0
  41. package/dist/commands/pack.d.ts +11 -0
  42. package/dist/commands/pack.js +133 -0
  43. package/dist/commands/proxy.d.ts +13 -0
  44. package/dist/commands/proxy.js +312 -0
  45. package/dist/commands/replay.d.ts +14 -0
  46. package/dist/commands/replay.js +289 -0
  47. package/dist/commands/run.d.ts +17 -0
  48. package/dist/commands/run.js +997 -0
  49. package/dist/commands/sample.d.ts +13 -0
  50. package/dist/commands/sample.js +260 -0
  51. package/dist/commands/search.d.ts +5 -0
  52. package/dist/commands/search.js +80 -0
  53. package/dist/commands/stubs.d.ts +6 -0
  54. package/dist/commands/stubs.js +187 -0
  55. package/dist/commands/tail.d.ts +4 -0
  56. package/dist/commands/tail.js +76 -0
  57. package/dist/commands/test-gen.d.ts +13 -0
  58. package/dist/commands/test-gen.js +237 -0
  59. package/dist/commands/trace.d.ts +14 -0
  60. package/dist/commands/trace.js +417 -0
  61. package/dist/commands/types.d.ts +7 -0
  62. package/dist/commands/types.js +128 -0
  63. package/dist/commands/unpack.d.ts +11 -0
  64. package/dist/commands/unpack.js +166 -0
  65. package/dist/commands/validate.d.ts +13 -0
  66. package/dist/commands/validate.js +310 -0
  67. package/dist/commands/watch.d.ts +9 -0
  68. package/dist/commands/watch.js +267 -0
  69. package/dist/config.d.ts +1 -0
  70. package/dist/config.js +66 -0
  71. package/dist/formatters/diff-formatter.d.ts +5 -0
  72. package/dist/formatters/diff-formatter.js +43 -0
  73. package/dist/formatters/type-formatter.d.ts +22 -0
  74. package/dist/formatters/type-formatter.js +135 -0
  75. package/dist/index.d.ts +2 -0
  76. package/dist/index.js +419 -0
  77. package/dist/local-codegen.d.ts +22 -0
  78. package/dist/local-codegen.js +762 -0
  79. package/dist/ui/badges.d.ts +16 -0
  80. package/dist/ui/badges.js +71 -0
  81. package/dist/ui/helpers.d.ts +13 -0
  82. package/dist/ui/helpers.js +85 -0
  83. package/package.json +23 -0
  84. package/src/api-client.ts +407 -0
  85. package/src/commands/annotate.ts +450 -0
  86. package/src/commands/audit.ts +103 -0
  87. package/src/commands/auto.ts +268 -0
  88. package/src/commands/capture.ts +257 -0
  89. package/src/commands/check.ts +437 -0
  90. package/src/commands/codegen.ts +128 -0
  91. package/src/commands/coverage.ts +170 -0
  92. package/src/commands/dashboard.ts +46 -0
  93. package/src/commands/dev.ts +323 -0
  94. package/src/commands/diff.ts +99 -0
  95. package/src/commands/docs.ts +392 -0
  96. package/src/commands/errors.ts +205 -0
  97. package/src/commands/export.ts +287 -0
  98. package/src/commands/functions.ts +81 -0
  99. package/src/commands/infer.ts +260 -0
  100. package/src/commands/init.ts +419 -0
  101. package/src/commands/mock.ts +220 -0
  102. package/src/commands/openapi.ts +53 -0
  103. package/src/commands/overview.ts +310 -0
  104. package/src/commands/pack.ts +139 -0
  105. package/src/commands/proxy.ts +314 -0
  106. package/src/commands/replay.ts +356 -0
  107. package/src/commands/run.ts +1190 -0
  108. package/src/commands/sample.ts +259 -0
  109. package/src/commands/search.ts +107 -0
  110. package/src/commands/stubs.ts +211 -0
  111. package/src/commands/tail.ts +94 -0
  112. package/src/commands/test-gen.ts +236 -0
  113. package/src/commands/trace.ts +440 -0
  114. package/src/commands/types.ts +161 -0
  115. package/src/commands/unpack.ts +179 -0
  116. package/src/commands/validate.ts +368 -0
  117. package/src/commands/watch.ts +277 -0
  118. package/src/config.ts +38 -0
  119. package/src/formatters/diff-formatter.ts +51 -0
  120. package/src/formatters/type-formatter.ts +161 -0
  121. package/src/index.ts +454 -0
  122. package/src/local-codegen.ts +859 -0
  123. package/src/ui/badges.ts +66 -0
  124. package/src/ui/helpers.ts +80 -0
  125. package/tsconfig.json +8 -0
package/dist/index.js ADDED
@@ -0,0 +1,419 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const functions_1 = require("./commands/functions");
10
+ const types_1 = require("./commands/types");
11
+ const errors_1 = require("./commands/errors");
12
+ const tail_1 = require("./commands/tail");
13
+ const codegen_1 = require("./commands/codegen");
14
+ const mock_1 = require("./commands/mock");
15
+ const init_1 = require("./commands/init");
16
+ const diff_1 = require("./commands/diff");
17
+ const openapi_1 = require("./commands/openapi");
18
+ const check_1 = require("./commands/check");
19
+ const dev_1 = require("./commands/dev");
20
+ const test_gen_1 = require("./commands/test-gen");
21
+ const dashboard_1 = require("./commands/dashboard");
22
+ const proxy_1 = require("./commands/proxy");
23
+ const export_1 = require("./commands/export");
24
+ const coverage_1 = require("./commands/coverage");
25
+ const replay_1 = require("./commands/replay");
26
+ const docs_1 = require("./commands/docs");
27
+ const sample_1 = require("./commands/sample");
28
+ const audit_1 = require("./commands/audit");
29
+ const capture_1 = require("./commands/capture");
30
+ const search_1 = require("./commands/search");
31
+ const auto_1 = require("./commands/auto");
32
+ const validate_1 = require("./commands/validate");
33
+ const watch_1 = require("./commands/watch");
34
+ const infer_1 = require("./commands/infer");
35
+ const overview_1 = require("./commands/overview");
36
+ const trace_1 = require("./commands/trace");
37
+ const pack_1 = require("./commands/pack");
38
+ const unpack_1 = require("./commands/unpack");
39
+ const run_1 = require("./commands/run");
40
+ const annotate_1 = require("./commands/annotate");
41
+ const stubs_1 = require("./commands/stubs");
42
+ const program = new commander_1.Command();
43
+ program
44
+ .name("trickle")
45
+ .description("CLI for trickle runtime type observability")
46
+ .version("0.1.0");
47
+ // trickle init
48
+ program
49
+ .command("init")
50
+ .description("Set up trickle in your project — configures types, tsconfig, and npm scripts")
51
+ .option("--dir <path>", "Project directory (defaults to current directory)")
52
+ .option("--python", "Set up for a Python project")
53
+ .action(async (opts) => {
54
+ await (0, init_1.initCommand)(opts);
55
+ });
56
+ // trickle dev [command]
57
+ program
58
+ .command("dev [command]")
59
+ .description("Start your app with auto-instrumentation and live type generation")
60
+ .option("-o, --out <path>", "Types output path (default: .trickle/types.d.ts)")
61
+ .option("--client", "Also generate typed API client (.trickle/api-client.ts)")
62
+ .option("--python", "Generate Python type stubs instead of TypeScript")
63
+ .action(async (command, opts) => {
64
+ await (0, dev_1.devCommand)(command, opts);
65
+ });
66
+ // trickle run <command>
67
+ program
68
+ .command("run [command]")
69
+ .description("Run any command or file with universal type observation — zero code changes needed")
70
+ .option("--module <name>", "Module name for captured functions")
71
+ .option("--include <patterns>", "Comma-separated substrings — only observe matching modules")
72
+ .option("--exclude <patterns>", "Comma-separated substrings — skip matching modules")
73
+ .option("--stubs <dir>", "Auto-generate .d.ts/.pyi type stubs in this directory after the run")
74
+ .option("--annotate <path>", "Auto-annotate this file or directory with types after the run")
75
+ .option("-w, --watch", "Watch source files and re-run on changes")
76
+ .action(async (command, opts) => {
77
+ await (0, run_1.runCommand)(command, opts);
78
+ });
79
+ // trickle functions
80
+ program
81
+ .command("functions")
82
+ .description("List observed functions")
83
+ .option("--env <env>", "Filter by environment")
84
+ .option("--lang <lang>", "Filter by language")
85
+ .option("--search <query>", "Search by function name")
86
+ .action(async (opts) => {
87
+ await (0, functions_1.functionsCommand)(opts);
88
+ });
89
+ // trickle types <function-name>
90
+ program
91
+ .command("types <function-name>")
92
+ .description("Show type snapshots for a function")
93
+ .option("--env <env>", "Filter by environment")
94
+ .option("--diff", "Show diff between latest two snapshots")
95
+ .option("--env1 <env>", "First environment for cross-env diff")
96
+ .option("--env2 <env>", "Second environment for cross-env diff")
97
+ .action(async (functionName, opts) => {
98
+ await (0, types_1.typesCommand)(functionName, opts);
99
+ });
100
+ // trickle errors [id]
101
+ program
102
+ .command("errors [id]")
103
+ .description("List errors or show error detail")
104
+ .option("--env <env>", "Filter by environment")
105
+ .option("--since <timeframe>", "Show errors since (e.g., 2d, 5m, 1h)")
106
+ .option("--function <name>", "Filter by function name")
107
+ .option("--limit <n>", "Limit number of results")
108
+ .action(async (id, opts) => {
109
+ await (0, errors_1.errorsCommand)(id, opts);
110
+ });
111
+ // trickle tail
112
+ program
113
+ .command("tail")
114
+ .description("Stream live events from the backend")
115
+ .option("--filter <pattern>", "Filter by function name pattern")
116
+ .action(async (opts) => {
117
+ await (0, tail_1.tailCommand)(opts);
118
+ });
119
+ // trickle codegen [function-name]
120
+ program
121
+ .command("codegen [function-name]")
122
+ .description("Generate TypeScript (or Python) type definitions from observed runtime types")
123
+ .option("-o, --out <path>", "Write output to a file instead of stdout")
124
+ .option("--env <env>", "Filter by environment")
125
+ .option("--python", "Generate Python type stubs (.pyi) instead of TypeScript")
126
+ .option("--client", "Generate a typed fetch-based API client from observed routes")
127
+ .option("--handlers", "Generate typed Express handler types for route handlers")
128
+ .option("--zod", "Generate Zod validation schemas with inferred types")
129
+ .option("--react-query", "Generate typed TanStack React Query hooks")
130
+ .option("--guards", "Generate runtime type guard functions")
131
+ .option("--middleware", "Generate Express request validation middleware")
132
+ .option("--msw", "Generate Mock Service Worker (MSW) request handlers")
133
+ .option("--json-schema", "Generate JSON Schema definitions from observed types")
134
+ .option("--swr", "Generate typed SWR data-fetching hooks")
135
+ .option("--pydantic", "Generate Pydantic BaseModel classes (Python)")
136
+ .option("--class-validator", "Generate class-validator DTOs for NestJS")
137
+ .option("--graphql", "Generate GraphQL SDL schema from observed routes")
138
+ .option("--trpc", "Generate typed tRPC router from observed routes")
139
+ .option("--axios", "Generate typed Axios client from observed routes")
140
+ .option("--watch", "Watch mode: re-generate when new types are observed")
141
+ .action(async (functionName, opts) => {
142
+ await (0, codegen_1.codegenCommand)(functionName, opts);
143
+ });
144
+ // trickle diff
145
+ program
146
+ .command("diff")
147
+ .description("Show type drift across all functions — what changed and where")
148
+ .option("--since <timeframe>", "Show changes since (e.g., 1h, 2d, 1w)")
149
+ .option("--env <env>", "Filter by environment")
150
+ .option("--env1 <env>", "First environment for cross-env comparison")
151
+ .option("--env2 <env>", "Second environment for cross-env comparison")
152
+ .action(async (opts) => {
153
+ await (0, diff_1.diffCommand)(opts);
154
+ });
155
+ // trickle openapi
156
+ program
157
+ .command("openapi")
158
+ .description("Generate an OpenAPI 3.0 spec from runtime-observed API routes")
159
+ .option("-o, --out <path>", "Write spec to a file (JSON)")
160
+ .option("--env <env>", "Filter by environment")
161
+ .option("--title <title>", "API title in the spec", "API")
162
+ .option("--api-version <version>", "API version in the spec", "1.0.0")
163
+ .option("--server <url>", "Server URL to include in the spec")
164
+ .action(async (opts) => {
165
+ await (0, openapi_1.openapiCommand)(opts);
166
+ });
167
+ // trickle check
168
+ program
169
+ .command("check")
170
+ .description("Detect breaking API changes by comparing against a saved baseline")
171
+ .option("--save <file>", "Save current types as a baseline snapshot")
172
+ .option("--against <file>", "Check current types against a baseline (exit 1 on breaking changes)")
173
+ .option("--env <env>", "Filter by environment")
174
+ .action(async (opts) => {
175
+ await (0, check_1.checkCommand)(opts);
176
+ });
177
+ // trickle mock
178
+ program
179
+ .command("mock")
180
+ .description("Start a mock API server from runtime-observed routes and sample data")
181
+ .option("-p, --port <port>", "Port to listen on", "3000")
182
+ .option("--no-cors", "Disable CORS headers")
183
+ .action(async (opts) => {
184
+ await (0, mock_1.mockCommand)(opts);
185
+ });
186
+ // trickle test --generate
187
+ program
188
+ .command("test")
189
+ .description("Generate API test files from runtime-observed routes and sample data")
190
+ .option("--generate", "Generate test file from observed routes")
191
+ .option("-o, --out <path>", "Write tests to a file")
192
+ .option("--framework <name>", "Test framework: vitest or jest (default: vitest)")
193
+ .option("--base-url <url>", "Base URL for API requests (default: http://localhost:3000)")
194
+ .action(async (opts) => {
195
+ if (!opts.generate) {
196
+ console.log(chalk_1.default.gray("\n Usage: trickle test --generate [--out tests.ts] [--framework vitest|jest]\n"));
197
+ return;
198
+ }
199
+ await (0, test_gen_1.testGenCommand)(opts);
200
+ });
201
+ // trickle dashboard
202
+ program
203
+ .command("dashboard")
204
+ .description("Open the trickle web dashboard to explore observed types visually")
205
+ .action(async () => {
206
+ await (0, dashboard_1.dashboardCommand)();
207
+ });
208
+ // trickle proxy
209
+ program
210
+ .command("proxy")
211
+ .description("Transparent reverse proxy that captures API types without any backend code changes")
212
+ .requiredOption("-t, --target <url>", "Target server URL to proxy to (e.g. http://localhost:3000)")
213
+ .option("-p, --port <port>", "Port for the proxy server", "4000")
214
+ .action(async (opts) => {
215
+ await (0, proxy_1.proxyCommand)(opts);
216
+ });
217
+ // trickle export
218
+ program
219
+ .command("export")
220
+ .description("Generate all output formats into a .trickle/ directory at once")
221
+ .option("-d, --dir <path>", "Output directory (default: .trickle)")
222
+ .option("--env <env>", "Filter by environment")
223
+ .action(async (opts) => {
224
+ await (0, export_1.exportCommand)(opts);
225
+ });
226
+ // trickle coverage
227
+ program
228
+ .command("coverage")
229
+ .description("Type observation health report — coverage, staleness, variants, and overall score")
230
+ .option("--env <env>", "Filter by environment")
231
+ .option("--json", "Output raw JSON (for CI integration)")
232
+ .option("--fail-under <score>", "Exit 1 if health score is below this threshold (0-100)")
233
+ .option("--stale-hours <hours>", "Hours before a function is considered stale (default: 24)")
234
+ .action(async (opts) => {
235
+ await (0, coverage_1.coverageCommand)(opts);
236
+ });
237
+ // trickle replay
238
+ program
239
+ .command("replay")
240
+ .description("Replay captured API requests as regression tests — verify response shapes match")
241
+ .option("-t, --target <url>", "Target server URL (default: http://localhost:3000)")
242
+ .option("--strict", "Compare exact values instead of just shapes")
243
+ .option("--json", "Output JSON results (for CI)")
244
+ .option("--fail-fast", "Stop on first failure")
245
+ .action(async (opts) => {
246
+ await (0, replay_1.replayCommand)(opts);
247
+ });
248
+ // trickle docs
249
+ program
250
+ .command("docs")
251
+ .description("Generate API documentation from observed runtime types and sample data")
252
+ .option("-o, --out <path>", "Write docs to a file instead of stdout")
253
+ .option("--html", "Generate self-contained HTML instead of Markdown")
254
+ .option("--env <env>", "Filter by environment")
255
+ .option("--title <title>", "Documentation title", "API Documentation")
256
+ .action(async (opts) => {
257
+ await (0, docs_1.docsCommand)(opts);
258
+ });
259
+ // trickle sample [route]
260
+ program
261
+ .command("sample [route]")
262
+ .description("Generate test fixtures and factory functions from observed runtime data")
263
+ .option("-f, --format <format>", "Output format: json, ts, or factory (default: json)")
264
+ .option("-o, --out <path>", "Write fixtures to a file")
265
+ .action(async (route, opts) => {
266
+ await (0, sample_1.sampleCommand)(route, opts);
267
+ });
268
+ // trickle audit
269
+ program
270
+ .command("audit")
271
+ .description("Analyze observed API types for quality issues — sensitive data, naming, complexity")
272
+ .option("--env <env>", "Filter by environment")
273
+ .option("--json", "Output raw JSON (for CI integration)")
274
+ .option("--fail-on-error", "Exit 1 if any errors are found")
275
+ .option("--fail-on-warning", "Exit 1 if any errors or warnings are found")
276
+ .action(async (opts) => {
277
+ await (0, audit_1.auditCommand)(opts);
278
+ });
279
+ // trickle capture <method> <url>
280
+ program
281
+ .command("capture <method> <url>")
282
+ .description("Capture types from a live API endpoint — no instrumentation needed")
283
+ .option("-H, --header <header...>", "HTTP headers (e.g. -H 'Authorization: Bearer token')")
284
+ .option("-d, --body <body>", "Request body (JSON string)")
285
+ .option("--env <env>", "Environment label (default: development)")
286
+ .option("--module <module>", "Module label (default: capture)")
287
+ .action(async (method, url, opts) => {
288
+ await (0, capture_1.captureCommand)(method, url, opts);
289
+ });
290
+ // trickle search <query>
291
+ program
292
+ .command("search <query>")
293
+ .description("Search across all observed types — find functions by field names, types, or patterns")
294
+ .option("--env <env>", "Filter by environment")
295
+ .option("--json", "Output raw JSON")
296
+ .action(async (query, opts) => {
297
+ await (0, search_1.searchCommand)(query, opts);
298
+ });
299
+ // trickle auto
300
+ program
301
+ .command("auto")
302
+ .description("Auto-detect project dependencies and generate only the relevant type files")
303
+ .option("-d, --dir <path>", "Output directory (default: .trickle)")
304
+ .option("--env <env>", "Filter by environment")
305
+ .action(async (opts) => {
306
+ await (0, auto_1.autoCommand)(opts);
307
+ });
308
+ // trickle validate <method> <url>
309
+ program
310
+ .command("validate <method> <url>")
311
+ .description("Validate a live API response against previously observed types")
312
+ .option("-H, --header <header...>", "HTTP headers")
313
+ .option("-d, --body <body>", "Request body (JSON string)")
314
+ .option("--env <env>", "Filter by environment")
315
+ .option("--strict", "Treat extra fields as errors (not just warnings)")
316
+ .action(async (method, url, opts) => {
317
+ await (0, validate_1.validateCommand)(method, url, opts);
318
+ });
319
+ // trickle watch
320
+ program
321
+ .command("watch")
322
+ .description("Watch for new type observations and auto-regenerate type files")
323
+ .option("-d, --dir <path>", "Output directory (default: .trickle)")
324
+ .option("--env <env>", "Filter by environment")
325
+ .option("--interval <interval>", "Poll interval (e.g., 3s, 500ms, 1m)", "3s")
326
+ .action(async (opts) => {
327
+ await (0, watch_1.watchCommand)(opts);
328
+ });
329
+ // trickle infer [file]
330
+ program
331
+ .command("infer [file]")
332
+ .description("Infer types from a JSON file or stdin — no live API needed")
333
+ .requiredOption("-n, --name <name>", "Function/route name (e.g., 'GET /api/users')")
334
+ .option("--env <env>", "Environment label (default: development)")
335
+ .option("--module <module>", "Module label (default: infer)")
336
+ .option("--request-body <json>", "Example request body JSON (for documenting input types)")
337
+ .action(async (file, opts) => {
338
+ await (0, infer_1.inferCommand)(file, opts);
339
+ });
340
+ // trickle overview
341
+ program
342
+ .command("overview")
343
+ .description("Compact API overview — all routes with inline type signatures")
344
+ .option("--env <env>", "Filter by environment")
345
+ .option("--json", "Output raw JSON")
346
+ .action(async (opts) => {
347
+ await (0, overview_1.overviewCommand)(opts);
348
+ });
349
+ // trickle trace <method> <url>
350
+ program
351
+ .command("trace <method> <url>")
352
+ .description("Make an HTTP request and show the response with inline type annotations")
353
+ .option("-H, --header <header...>", "HTTP headers")
354
+ .option("-d, --body <body>", "Request body (JSON string)")
355
+ .option("--save", "Save inferred types to the backend")
356
+ .option("--env <env>", "Environment label (default: development)")
357
+ .option("--module <module>", "Module label (default: trace)")
358
+ .action(async (method, url, opts) => {
359
+ await (0, trace_1.traceCommand)(method, url, opts);
360
+ });
361
+ // trickle pack
362
+ program
363
+ .command("pack")
364
+ .description("Export all observed types as a portable bundle")
365
+ .option("-o, --out <file>", "Write bundle to a file (otherwise stdout)")
366
+ .option("--env <env>", "Filter by environment")
367
+ .action(async (opts) => {
368
+ await (0, pack_1.packCommand)(opts);
369
+ });
370
+ // trickle unpack <file>
371
+ program
372
+ .command("unpack <file>")
373
+ .description("Import types from a packed bundle into the backend")
374
+ .option("--env <env>", "Override environment for all imported types")
375
+ .option("--dry-run", "List contents without importing")
376
+ .action(async (file, opts) => {
377
+ await (0, unpack_1.unpackCommand)(file, opts);
378
+ });
379
+ // trickle stubs <dir>
380
+ program
381
+ .command("stubs <dir>")
382
+ .description("Generate .d.ts and .pyi sidecar type stubs next to source files — IDEs pick them up automatically")
383
+ .option("--env <env>", "Filter by environment")
384
+ .option("--dry-run", "Preview which files would be created without writing them")
385
+ .action(async (dir, opts) => {
386
+ await (0, stubs_1.stubsCommand)(dir, opts);
387
+ });
388
+ // trickle annotate <file>
389
+ program
390
+ .command("annotate <file>")
391
+ .description("Add runtime-observed type annotations directly into source files")
392
+ .option("--env <env>", "Filter by environment")
393
+ .option("--dry-run", "Preview changes without modifying the file")
394
+ .option("--jsdoc", "Force JSDoc comments (default for .js files)")
395
+ .action(async (file, opts) => {
396
+ await (0, annotate_1.annotateCommand)(file, opts);
397
+ });
398
+ // Handle unhandled rejections
399
+ process.on("unhandledRejection", (err) => {
400
+ if (err instanceof Error) {
401
+ console.error(chalk_1.default.red(`\n Error: ${err.message}\n`));
402
+ }
403
+ else {
404
+ console.error(chalk_1.default.red("\n An unexpected error occurred.\n"));
405
+ }
406
+ process.exit(1);
407
+ });
408
+ // ── Direct file execution shorthand ──
409
+ // `trickle app.js` → `trickle run app.js`
410
+ // `trickle script.py --watch` → `trickle run script.py --watch`
411
+ const CODE_EXTENSIONS = /\.(js|ts|tsx|jsx|mjs|cjs|mts|py)$/i;
412
+ const firstArg = process.argv[2];
413
+ if (firstArg &&
414
+ !firstArg.startsWith("-") &&
415
+ CODE_EXTENSIONS.test(firstArg)) {
416
+ // Inject "run" before the file argument
417
+ process.argv.splice(2, 0, "run");
418
+ }
419
+ program.parse();
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Local codegen: reads .trickle/observations.jsonl and generates
3
+ * type stubs without needing the backend running.
4
+ *
5
+ * Used by `trickle run` in offline/local mode.
6
+ */
7
+ /**
8
+ * Generate type stubs from a .trickle/observations.jsonl file.
9
+ * Returns { ts, python } content strings, grouped by module.
10
+ */
11
+ export declare function generateFromJsonl(jsonlPath: string): Record<string, {
12
+ ts: string;
13
+ python: string;
14
+ }>;
15
+ /**
16
+ * Generate sidecar type files from local observations.
17
+ * Writes .d.ts or .pyi files next to the source file.
18
+ */
19
+ export declare function generateLocalStubs(sourceFile: string, jsonlPath?: string): {
20
+ written: string[];
21
+ functionCount: number;
22
+ };