windmill-cli 1.691.0 → 1.692.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 (3) hide show
  1. package/README.md +1 -0
  2. package/esm/main.js +1808 -300
  3. package/package.json +1 -1
package/esm/main.js CHANGED
@@ -16224,6 +16224,14 @@ var execFile5, __dirname2, localXdgOpenPath, platform, arch, pTryEach = async (a
16224
16224
  }
16225
16225
  subprocess.unref();
16226
16226
  return subprocess;
16227
+ }, open = (target, options) => {
16228
+ if (typeof target !== "string") {
16229
+ throw new TypeError("Expected a `target`");
16230
+ }
16231
+ return baseOpen({
16232
+ ...options,
16233
+ target
16234
+ });
16227
16235
  }, openApp = (name, options) => {
16228
16236
  if (typeof name !== "string" && !Array.isArray(name)) {
16229
16237
  throw new TypeError("Expected a valid `name`");
@@ -16239,7 +16247,7 @@ var execFile5, __dirname2, localXdgOpenPath, platform, arch, pTryEach = async (a
16239
16247
  arguments: appArguments
16240
16248
  }
16241
16249
  });
16242
- }, apps;
16250
+ }, apps, open_default;
16243
16251
  var init_open = __esm(() => {
16244
16252
  init_wsl_utils();
16245
16253
  init_default_browser();
@@ -16285,6 +16293,7 @@ var init_open = __esm(() => {
16285
16293
  }));
16286
16294
  defineLazyProperty(apps, "browser", () => "browser");
16287
16295
  defineLazyProperty(apps, "browserPrivate", () => "browserPrivate");
16296
+ open_default = open;
16288
16297
  });
16289
16298
 
16290
16299
  // node_modules/@cliffy/prompt/secret.js
@@ -16408,7 +16417,7 @@ async function browserLogin(baseUrl) {
16408
16417
  const url = `${baseUrl}user/cli?port=${port}`;
16409
16418
  info(`Login by going to ${url}`);
16410
16419
  try {
16411
- openApp(apps.browser, { arguments: [url] }).catch((error2) => {
16420
+ open_default(url).catch((error2) => {
16412
16421
  console.error(`Failed to open browser, please navigate to ${url}, error: ${error2}`);
16413
16422
  });
16414
16423
  info("Opened browser for you");
@@ -16701,7 +16710,7 @@ var init_OpenAPI = __esm(() => {
16701
16710
  PASSWORD: undefined,
16702
16711
  TOKEN: getEnv3("WM_TOKEN"),
16703
16712
  USERNAME: undefined,
16704
- VERSION: "1.691.0",
16713
+ VERSION: "1.692.0",
16705
16714
  WITH_CREDENTIALS: true,
16706
16715
  interceptors: {
16707
16716
  request: new Interceptors,
@@ -26226,9 +26235,6 @@ function getFolderSuffixes() {
26226
26235
  function getFolderSuffix(type) {
26227
26236
  return getFolderSuffixes()[type];
26228
26237
  }
26229
- function getFolderSuffixWithSep(type) {
26230
- return getFolderSuffixes()[type] + SEP2;
26231
- }
26232
26238
  function getMetadataFileName(type, format6) {
26233
26239
  return METADATA_FILES[type][format6];
26234
26240
  }
@@ -30232,6 +30238,74 @@ return schema
30232
30238
  rawAppWmillTs_exports = /* @__PURE__ */ __export2({ default: () => rawAppWmillTs_default }, 1);
30233
30239
  });
30234
30240
 
30241
+ // src/utils/port-probe.ts
30242
+ import { createServer as createServer2 } from "node:net";
30243
+ import { execSync as execSync4 } from "node:child_process";
30244
+ function isPortFree(port, host) {
30245
+ return new Promise((resolve6) => {
30246
+ const s = createServer2();
30247
+ s.once("error", (err) => {
30248
+ const code2 = err.code ?? "";
30249
+ resolve6(code2 !== "EADDRINUSE" && code2 !== "EACCES");
30250
+ });
30251
+ s.once("listening", () => s.close(() => resolve6(true)));
30252
+ s.listen(port, host);
30253
+ });
30254
+ }
30255
+ async function isPortFreeOnBothStacks(port) {
30256
+ if (!await isPortFree(port, "0.0.0.0"))
30257
+ return false;
30258
+ if (!await isPortFree(port, "::"))
30259
+ return false;
30260
+ return true;
30261
+ }
30262
+ function findPortHolder(port) {
30263
+ try {
30264
+ const out = execSync4(`lsof -nP -iTCP:${port} -sTCP:LISTEN -F pc 2>/dev/null`, {
30265
+ encoding: "utf-8",
30266
+ stdio: ["ignore", "pipe", "ignore"]
30267
+ });
30268
+ let pid;
30269
+ let cmd;
30270
+ for (const line of out.split(`
30271
+ `)) {
30272
+ if (line.startsWith("p"))
30273
+ pid = parseInt(line.slice(1), 10);
30274
+ else if (line.startsWith("c"))
30275
+ cmd = line.slice(1);
30276
+ if (pid && cmd)
30277
+ return { pid, command: cmd };
30278
+ }
30279
+ } catch {}
30280
+ try {
30281
+ const out = execSync4(`ss -ltnp 2>/dev/null | awk '$4 ~ /:${port}$/ { print $NF }'`, {
30282
+ encoding: "utf-8",
30283
+ stdio: ["ignore", "pipe", "ignore"]
30284
+ }).trim();
30285
+ const m = out.match(/\("([^"]+)",pid=(\d+)/);
30286
+ if (m)
30287
+ return { pid: parseInt(m[2], 10), command: m[1] };
30288
+ } catch {}
30289
+ return;
30290
+ }
30291
+ async function resolveBindPort(requested, flagLabel, log) {
30292
+ const MAX_SHIFT = 20;
30293
+ for (let port = requested;port < requested + MAX_SHIFT; port++) {
30294
+ if (await isPortFreeOnBothStacks(port)) {
30295
+ if (port !== requested) {
30296
+ const holder = findPortHolder(requested);
30297
+ const holderHint = holder ? ` (held by PID ${holder.pid} \`${holder.command}\`)` : "";
30298
+ log.warn(`Port ${requested} is already in use${holderHint}. Using port ${port} instead.`);
30299
+ log.info(`If you need port ${requested} stable (e.g. a launch.json entry pinned to it), stop the holder and re-run with ${flagLabel} ${requested}.`);
30300
+ }
30301
+ return port;
30302
+ }
30303
+ }
30304
+ throw new Error(`Could not find a free port in the range ${requested}-${requested + MAX_SHIFT - 1}. Stop a holder or pass ${flagLabel} <other>.`);
30305
+ }
30306
+ var BIND_HOST = "0.0.0.0";
30307
+ var init_port_probe = () => {};
30308
+
30235
30309
  // node_modules/ws/lib/stream.js
30236
30310
  var require_stream = __commonJS((exports, module) => {
30237
30311
  var { Duplex } = __require("stream");
@@ -30296,7 +30370,7 @@ var require_stream = __commonJS((exports, module) => {
30296
30370
  };
30297
30371
  duplex._final = function(callback) {
30298
30372
  if (ws.readyState === ws.CONNECTING) {
30299
- ws.once("open", function open() {
30373
+ ws.once("open", function open2() {
30300
30374
  duplex._final(callback);
30301
30375
  });
30302
30376
  return;
@@ -30320,7 +30394,7 @@ var require_stream = __commonJS((exports, module) => {
30320
30394
  };
30321
30395
  duplex._write = function(chunk, encoding, callback) {
30322
30396
  if (ws.readyState === ws.CONNECTING) {
30323
- ws.once("open", function open() {
30397
+ ws.once("open", function open2() {
30324
30398
  duplex._write(chunk, encoding, callback);
30325
30399
  });
30326
30400
  return;
@@ -34923,10 +34997,10 @@ globstar while`, file, fr, pattern, pr, swallowee);
34923
34997
  }
34924
34998
  return filtered.join("/");
34925
34999
  }).join("|");
34926
- const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
34927
- re = "^" + open + re + close + "$";
35000
+ const [open2, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
35001
+ re = "^" + open2 + re + close + "$";
34928
35002
  if (this.partial) {
34929
- re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
35003
+ re = "^(?:\\/|" + open2 + re.slice(1, -1) + close + ")$";
34930
35004
  }
34931
35005
  if (this.negate)
34932
35006
  re = "^(?!" + re + ").+$";
@@ -61383,7 +61457,7 @@ import { Buffer as Buffer4 } from "node:buffer";
61383
61457
  import { sep as SEP4 } from "node:path";
61384
61458
  import * as path7 from "node:path";
61385
61459
  import fs8 from "node:fs";
61386
- import { execSync as execSync4 } from "node:child_process";
61460
+ import { execSync as execSync5 } from "node:child_process";
61387
61461
  function isRawAppBackendPath2(filePath) {
61388
61462
  return isRawAppBackendPath(filePath);
61389
61463
  }
@@ -61484,7 +61558,7 @@ async function handleFile(path8, workspace, alreadySynced, message, opts, rawWor
61484
61558
  let outputFiles = [];
61485
61559
  if (codebase.customBundler) {
61486
61560
  info(`Using custom bundler ${codebase.customBundler} for ${path8}`);
61487
- bundleContent = execSync4(codebase.customBundler + " " + path8, {
61561
+ bundleContent = execSync5(codebase.customBundler + " " + path8, {
61488
61562
  maxBuffer: 1024 * 1024 * 50
61489
61563
  }).toString();
61490
61564
  info("Custom bundler executed for " + path8);
@@ -62177,7 +62251,7 @@ async function preview(opts, filePath) {
62177
62251
  if (!opts.silent) {
62178
62252
  info(`Using custom bundler ${codebase.customBundler} for preview`);
62179
62253
  }
62180
- bundledContent = execSync4(codebase.customBundler + " " + filePath, {
62254
+ bundledContent = execSync5(codebase.customBundler + " " + filePath, {
62181
62255
  maxBuffer: 52428800
62182
62256
  }).toString();
62183
62257
  } else {
@@ -63321,8 +63395,8 @@ function getLanguageFromExtension(ext2, defaultTs = "bun") {
63321
63395
  return;
63322
63396
  }
63323
63397
  function sanitizeForFilesystem(summary) {
63324
- const name = summary.toLowerCase().replaceAll(" ", "_").replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "").replace(/_+/g, "_").replace(/^[._]+|[._]+$/g, "");
63325
- return WINDOWS_RESERVED.test(name) ? `_${name}` : name;
63398
+ const name = summary.replaceAll(" ", "_").replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "").replace(/_+/g, "_").replace(/^[._]+|[._]+$/g, "");
63399
+ return WINDOWS_RESERVED.test(name.toLowerCase()) ? `_${name}` : name;
63326
63400
  }
63327
63401
  function newPathAssigner(defaultTs, options) {
63328
63402
  const resolvedOptions = typeof defaultTs === "object" ? defaultTs : { defaultTs, skipInlineScriptSuffix: options?.skipInlineScriptSuffix };
@@ -63337,11 +63411,11 @@ function newPathAssigner(defaultTs, options) {
63337
63411
  original_name = INLINE_SCRIPT_PREFIX;
63338
63412
  name = `${INLINE_SCRIPT_PREFIX}_0`;
63339
63413
  }
63340
- while (seen_names.has(name)) {
63414
+ while (seen_names.has(name.toLowerCase())) {
63341
63415
  counter++;
63342
63416
  name = `${original_name}_${counter}`;
63343
63417
  }
63344
- seen_names.add(name);
63418
+ seen_names.add(name.toLowerCase());
63345
63419
  const ext2 = getLanguageExtension(language, tsRuntime);
63346
63420
  const suffix = skipInlineScriptSuffix ? "." : ".inline_script.";
63347
63421
  return [`${name}${suffix}`, ext2];
@@ -63359,11 +63433,11 @@ function newRawAppPathAssigner(defaultTs) {
63359
63433
  original_name = "runnable";
63360
63434
  name = `runnable_0`;
63361
63435
  }
63362
- while (seen_names.has(name)) {
63436
+ while (seen_names.has(name.toLowerCase())) {
63363
63437
  counter++;
63364
63438
  name = `${original_name}_${counter}`;
63365
63439
  }
63366
- seen_names.add(name);
63440
+ seen_names.add(name.toLowerCase());
63367
63441
  const ext2 = getLanguageExtension(language, defaultTs);
63368
63442
  return [`${name}.`, ext2];
63369
63443
  }
@@ -67552,12 +67626,24 @@ __export(exports_raw_apps, {
67552
67626
  import { sep as SEP10 } from "node:path";
67553
67627
  import path12 from "node:path";
67554
67628
  import { readdir as readdir6 } from "node:fs/promises";
67629
+ async function readSiblingLock(backendPath, runnableId, allFiles) {
67630
+ const target = `${runnableId.toLowerCase()}.lock`;
67631
+ const lockFile = allFiles.find((f) => f.toLowerCase() === target);
67632
+ if (!lockFile)
67633
+ return;
67634
+ try {
67635
+ return await readTextFile(path12.join(backendPath, lockFile));
67636
+ } catch {
67637
+ return;
67638
+ }
67639
+ }
67555
67640
  async function findRunnableContentFile(backendPath, runnableId, allFiles) {
67641
+ const runnableIdLower = runnableId.toLowerCase();
67556
67642
  for (const fileName of allFiles) {
67557
67643
  if (fileName.endsWith(".yaml") || fileName.endsWith(".lock")) {
67558
67644
  continue;
67559
67645
  }
67560
- if (!fileName.startsWith(runnableId + ".")) {
67646
+ if (!fileName.toLowerCase().startsWith(runnableIdLower + ".")) {
67561
67647
  continue;
67562
67648
  }
67563
67649
  const ext2 = fileName.substring(runnableId.length + 1);
@@ -67599,17 +67685,14 @@ async function loadRunnablesFromBackend(backendPath, defaultTs = "bun") {
67599
67685
  continue;
67600
67686
  }
67601
67687
  const runnableId = fileName.replace(".yaml", "");
67602
- processedIds.add(runnableId);
67688
+ processedIds.add(runnableId.toLowerCase());
67603
67689
  const filePath = path12.join(backendPath, fileName);
67604
67690
  const runnable = await yamlParseFile(filePath);
67605
67691
  if (runnable?.type === "inline") {
67606
67692
  const contentFile = await findRunnableContentFile(backendPath, runnableId, allFiles);
67607
67693
  if (contentFile) {
67608
67694
  const language = getLanguageFromExtension(contentFile.ext, defaultTs);
67609
- let lock;
67610
- try {
67611
- lock = await readTextFile(path12.join(backendPath, `${runnableId}.lock`));
67612
- } catch {}
67695
+ const lock = await readSiblingLock(backendPath, runnableId, allFiles);
67613
67696
  runnable.inlineScript = {
67614
67697
  content: contentFile.content,
67615
67698
  language,
@@ -67630,17 +67713,14 @@ async function loadRunnablesFromBackend(backendPath, defaultTs = "bun") {
67630
67713
  if (!runnableId) {
67631
67714
  continue;
67632
67715
  }
67633
- if (processedIds.has(runnableId)) {
67716
+ if (processedIds.has(runnableId.toLowerCase())) {
67634
67717
  continue;
67635
67718
  }
67636
- processedIds.add(runnableId);
67719
+ processedIds.add(runnableId.toLowerCase());
67637
67720
  const contentFile = await findRunnableContentFile(backendPath, runnableId, allFiles);
67638
67721
  if (contentFile) {
67639
67722
  const language = getLanguageFromExtension(contentFile.ext, defaultTs);
67640
- let lock;
67641
- try {
67642
- lock = await readTextFile(path12.join(backendPath, `${runnableId}.lock`));
67643
- } catch {}
67723
+ const lock = await readSiblingLock(backendPath, runnableId, allFiles);
67644
67724
  runnables[runnableId] = {
67645
67725
  type: "inline",
67646
67726
  inlineScript: {
@@ -68602,10 +68682,14 @@ async function dev(opts, appFolder) {
68602
68682
  const rawApp = await yamlParseFile(rawAppPath);
68603
68683
  const appPath = rawApp?.custom_path ?? "u/unknown/newapp";
68604
68684
  const esbuild = await import("esbuild");
68605
- const port = opts.port ?? await getPorts({
68685
+ const host = opts.host ?? DEFAULT_HOST;
68686
+ const probeBothStacks = host === DEFAULT_HOST;
68687
+ const port = opts.port !== undefined ? probeBothStacks ? await resolveBindPort(opts.port, "--port", {
68688
+ info: (m) => info(m),
68689
+ warn: (m) => warn(m)
68690
+ }) : opts.port : await getPorts({
68606
68691
  port: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((p) => p + DEFAULT_PORT)
68607
68692
  });
68608
- const host = opts.host ?? DEFAULT_HOST;
68609
68693
  const shouldOpen = opts.open ?? true;
68610
68694
  const frameworks = detectFrameworks(process16.cwd());
68611
68695
  const defaultEntry = frameworks.svelte || frameworks.vue ? "index.ts" : "index.tsx";
@@ -69708,6 +69792,7 @@ var init_dev = __esm(async () => {
69708
69792
  init_log();
69709
69793
  init_lib_es();
69710
69794
  init_get_port();
69795
+ init_port_probe();
69711
69796
  init_open();
69712
69797
  init_wrapper();
69713
69798
  init_services_gen();
@@ -69874,8 +69959,9 @@ var init_lint2 = __esm(async () => {
69874
69959
  });
69875
69960
 
69876
69961
  // src/commands/app/new.ts
69877
- import { stat as stat9, writeFile as writeFile10, mkdir as mkdir7 } from "node:fs/promises";
69962
+ import { stat as stat9, writeFile as writeFile10, mkdir as mkdir7, rm as rm3 } from "node:fs/promises";
69878
69963
  import path17 from "node:path";
69964
+ import { execSync as execSync6, exec, execFile as execFile6 } from "node:child_process";
69879
69965
  function validateAppPath(appPath) {
69880
69966
  if (!appPath.startsWith("u/") && !appPath.startsWith("f/")) {
69881
69967
  return {
@@ -69942,16 +70028,25 @@ async function newApp(opts) {
69942
70028
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
69943
70029
  warn(colors.yellow(`Could not fetch datatables: ${errorMessage}`));
69944
70030
  }
69945
- const summary = await Input.prompt({
69946
- message: "App summary (short description):",
69947
- minLength: 1,
69948
- validate: (value) => {
69949
- if (value.trim().length === 0) {
69950
- return "Summary cannot be empty";
69951
- }
69952
- return true;
70031
+ let summary;
70032
+ if (opts.summary !== undefined) {
70033
+ if (opts.summary.trim().length === 0) {
70034
+ error(colors.red("--summary cannot be empty"));
70035
+ return;
69953
70036
  }
69954
- });
70037
+ summary = opts.summary;
70038
+ } else {
70039
+ summary = await Input.prompt({
70040
+ message: "App summary (short description):",
70041
+ minLength: 1,
70042
+ validate: (value) => {
70043
+ if (value.trim().length === 0) {
70044
+ return "Summary cannot be empty";
70045
+ }
70046
+ return true;
70047
+ }
70048
+ });
70049
+ }
69955
70050
  const buildPathSuggestions = (input) => {
69956
70051
  const suggestions = [];
69957
70052
  if (input.length < 2) {
@@ -69968,27 +70063,46 @@ async function newApp(opts) {
69968
70063
  return suggestions;
69969
70064
  };
69970
70065
  let appPath;
69971
- while (true) {
69972
- appPath = await Input.prompt({
69973
- message: "App path (e.g., f/my_folder/my_app or u/username/my_app):",
69974
- minLength: 1,
69975
- suggestions: buildPathSuggestions
69976
- });
69977
- const validation = validateAppPath(appPath);
69978
- if (validation.valid) {
69979
- break;
70066
+ if (opts.path !== undefined) {
70067
+ const validation = validateAppPath(opts.path);
70068
+ if (!validation.valid) {
70069
+ error(colors.red(`Invalid --path: ${validation.error}`));
70070
+ return;
70071
+ }
70072
+ appPath = opts.path;
70073
+ } else {
70074
+ while (true) {
70075
+ appPath = await Input.prompt({
70076
+ message: "App path (e.g., f/my_folder/my_app or u/username/my_app):",
70077
+ minLength: 1,
70078
+ suggestions: buildPathSuggestions
70079
+ });
70080
+ const validation = validateAppPath(appPath);
70081
+ if (validation.valid) {
70082
+ break;
70083
+ }
70084
+ error(colors.red(`Invalid path: ${validation.error}`));
69980
70085
  }
69981
- error(colors.red(`Invalid path: ${validation.error}`));
69982
70086
  }
69983
- const framework = await Select.prompt({
69984
- message: "Select a framework:",
69985
- options: [
69986
- { name: "React 19 (Recommended)", value: "react19" },
69987
- { name: "React 18", value: "react18" },
69988
- { name: "Svelte 5", value: "svelte5" },
69989
- { name: "Vue 3", value: "vue" }
69990
- ]
69991
- });
70087
+ const VALID_FRAMEWORKS = ["react19", "react18", "svelte5", "vue"];
70088
+ let framework;
70089
+ if (opts.framework !== undefined) {
70090
+ if (!VALID_FRAMEWORKS.includes(opts.framework)) {
70091
+ error(colors.red(`Invalid --framework: ${opts.framework}. Must be one of: ${VALID_FRAMEWORKS.join(", ")}`));
70092
+ return;
70093
+ }
70094
+ framework = opts.framework;
70095
+ } else {
70096
+ framework = await Select.prompt({
70097
+ message: "Select a framework:",
70098
+ options: [
70099
+ { name: "React 19 (Recommended)", value: "react19" },
70100
+ { name: "React 18", value: "react18" },
70101
+ { name: "Svelte 5", value: "svelte5" },
70102
+ { name: "Vue 3", value: "vue" }
70103
+ ]
70104
+ });
70105
+ }
69992
70106
  const template = templates[framework];
69993
70107
  if (!template) {
69994
70108
  error(colors.red(`Unknown framework: ${framework}`));
@@ -69997,7 +70111,29 @@ async function newApp(opts) {
69997
70111
  let dataConfig = {};
69998
70112
  let createSchemaSQL;
69999
70113
  let schemaName;
70000
- if (datatables.length > 0) {
70114
+ const nonInteractive = opts.summary !== undefined || opts.path !== undefined || opts.framework !== undefined;
70115
+ if (opts.datatable !== undefined) {
70116
+ if (datatables.length > 0 && !datatables.includes(opts.datatable)) {
70117
+ warn(colors.yellow(`--datatable '${opts.datatable}' is not in the workspace's datatable list (${datatables.join(", ")}). Continuing anyway.`));
70118
+ }
70119
+ dataConfig.datatable = opts.datatable;
70120
+ if (opts.schema !== undefined) {
70121
+ if (!/^[a-z_][a-z0-9_]*$/.test(opts.schema)) {
70122
+ error(colors.red(`--schema must start with a letter or underscore and contain only lowercase letters, numbers, and underscores: ${opts.schema}`));
70123
+ return;
70124
+ }
70125
+ schemaName = opts.schema;
70126
+ dataConfig.schema = schemaName;
70127
+ const existingSchemas = datatableSchemas.get(opts.datatable) ?? [];
70128
+ if (!existingSchemas.includes(schemaName)) {
70129
+ createSchemaSQL = `-- Create schema for ${summary}
70130
+ -- This will be executed when you run 'wmill app dev' and confirm in the modal
70131
+ CREATE SCHEMA IF NOT EXISTS ${schemaName};
70132
+ `;
70133
+ }
70134
+ }
70135
+ dataConfig.tables = [];
70136
+ } else if (nonInteractive) {} else if (datatables.length > 0) {
70001
70137
  info("");
70002
70138
  info(colors.bold.cyan("Data Configuration"));
70003
70139
  info(colors.gray("Configure datatables to enable AI to create and query database tables."));
@@ -70081,17 +70217,29 @@ CREATE SCHEMA IF NOT EXISTS ${schemaName};
70081
70217
  await loadNonDottedPathsSetting();
70082
70218
  const folderName2 = buildFolderPath(appPath, "raw_app");
70083
70219
  const appDir = path17.join(process.cwd(), folderName2);
70220
+ let dirExists = false;
70084
70221
  try {
70085
70222
  await stat9(appDir);
70086
- const overwrite = await Confirm.prompt({
70087
- message: `Directory '${folderName2}' already exists. Overwrite?`,
70088
- default: false
70089
- });
70090
- if (!overwrite) {
70091
- info(colors.yellow("Aborted."));
70223
+ dirExists = true;
70224
+ } catch {}
70225
+ if (dirExists) {
70226
+ if (opts.overwrite) {
70227
+ warn(colors.yellow(`Overwriting existing '${folderName2}' (--overwrite)`));
70228
+ } else if (nonInteractive) {
70229
+ error(colors.red(`Directory '${folderName2}' already exists. Pass --overwrite to replace it.`));
70092
70230
  return;
70231
+ } else {
70232
+ const overwrite = await Confirm.prompt({
70233
+ message: `Directory '${folderName2}' already exists. Overwrite?`,
70234
+ default: false
70235
+ });
70236
+ if (!overwrite) {
70237
+ info(colors.yellow("Aborted."));
70238
+ return;
70239
+ }
70093
70240
  }
70094
- } catch {}
70241
+ await rm3(appDir, { recursive: true, force: true });
70242
+ }
70095
70243
  await mkdir7(appDir, { recursive: true });
70096
70244
  await mkdir7(path17.join(appDir, "backend"), { recursive: true });
70097
70245
  await mkdir7(path17.join(appDir, "sql_to_apply"), { recursive: true });
@@ -70196,6 +70344,69 @@ This folder is for SQL migration files that will be applied to datatables during
70196
70344
  }
70197
70345
  info("");
70198
70346
  info(colors.gray(" 4. wmill sync push (to deploy when ready)"));
70347
+ let hasClaudeDesktop = false;
70348
+ if (process.platform === "darwin") {
70349
+ try {
70350
+ execSync6("ls /Applications/Claude.app", { stdio: "ignore" });
70351
+ hasClaudeDesktop = true;
70352
+ } catch {}
70353
+ }
70354
+ if (hasClaudeDesktop && !nonInteractive && opts.openInDesktop !== false) {
70355
+ info("");
70356
+ const openInDesktop = await Confirm.prompt({
70357
+ message: "Open in Claude Desktop?",
70358
+ default: true
70359
+ });
70360
+ if (openInDesktop) {
70361
+ try {
70362
+ const absAppDir = path17.resolve(appDir);
70363
+ const claudeDir = path17.join(absAppDir, ".claude");
70364
+ const launchPath = path17.join(claudeDir, "launch.json");
70365
+ if (!await stat9(launchPath).catch(() => null)) {
70366
+ const launchJson = JSON.stringify({
70367
+ version: "0.0.1",
70368
+ configurations: [{
70369
+ name: `windmill: ${appPath}`,
70370
+ runtimeExecutable: "bash",
70371
+ runtimeArgs: ["-c", "wmill app dev --no-open --port ${PORT:-4000}"],
70372
+ port: 4000,
70373
+ autoPort: true
70374
+ }]
70375
+ }, null, 2) + `
70376
+ `;
70377
+ await mkdir7(claudeDir, { recursive: true });
70378
+ await writeFile10(launchPath, launchJson, "utf-8");
70379
+ info(colors.gray(`Seeded ${path17.relative(process.cwd(), launchPath)}`));
70380
+ }
70381
+ const sessionId = crypto.randomUUID();
70382
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
70383
+ let i = 0;
70384
+ const spinner = setInterval(() => {
70385
+ process.stdout.write(`\r${colors.gray(`${frames[i++ % frames.length]} Creating Claude session...`)}`);
70386
+ }, 80);
70387
+ try {
70388
+ await new Promise((resolve8, reject) => {
70389
+ exec(`claude --session-id "${sessionId}" -p "Say: Your app is ready, click on preview to test it!"`, { cwd: absAppDir }, (error2) => error2 ? reject(error2) : resolve8());
70390
+ });
70391
+ } finally {
70392
+ clearInterval(spinner);
70393
+ process.stdout.write("\r" + " ".repeat(40) + "\r");
70394
+ }
70395
+ const deepLink = `claude://resume?session=${sessionId}&cwd=${encodeURIComponent(absAppDir)}`;
70396
+ execFile6("open", [deepLink], (err) => {
70397
+ if (err) {
70398
+ warn(colors.yellow(`Could not open Claude Desktop deep link (${err.message}). Open it manually: ${deepLink}`));
70399
+ } else {
70400
+ info(colors.bold.green("Opened in Claude Desktop!"));
70401
+ }
70402
+ });
70403
+ } catch (error2) {
70404
+ const errorMessage = error2 instanceof Error ? error2.message : String(error2);
70405
+ warn(colors.yellow(`Could not open in Claude Desktop: ${errorMessage}`));
70406
+ info(colors.gray("You can manually run: cd " + folderName2 + " && claude"));
70407
+ }
70408
+ }
70409
+ }
70199
70410
  }
70200
70411
  var import_yaml22, reactIndex = `
70201
70412
  import React from 'react'
@@ -70266,7 +70477,18 @@ const msg = ref('world');
70266
70477
  import App from './App.vue'
70267
70478
  import "./index.css";
70268
70479
 
70269
- createApp(App).mount('#root')`, indexCss = `.myclass {
70480
+ createApp(App).mount('#root')`, indexCss = `body {
70481
+ margin: 0;
70482
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
70483
+ background-color: #f5f5f5;
70484
+ color: #1a1a1a;
70485
+ }
70486
+
70487
+ #root {
70488
+ padding: 24px;
70489
+ }
70490
+
70491
+ .myclass {
70270
70492
  border: 1px solid gray;
70271
70493
  padding: 2px;
70272
70494
  }`, templates, command11, new_default;
@@ -70354,7 +70576,7 @@ var init_new = __esm(async () => {
70354
70576
  }
70355
70577
  }
70356
70578
  };
70357
- command11 = new Command().description("create a new raw app from a template").action(newApp);
70579
+ command11 = new Command().description("create a new raw app from a template").option("--summary <summary:string>", "App summary (short description). Skips the prompt when provided. Triggers non-interactive mode.").option("--path <path:string>", "App path (e.g., f/folder/my_app or u/username/my_app). Skips the prompt when provided. Triggers non-interactive mode.").option("--framework <framework:string>", "Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode.").option("--datatable <datatable:string>", "Datatable to wire up. Without this flag in non-interactive mode, no datatable is configured.").option("--schema <schema:string>", "Schema to use with --datatable. Created (CREATE SCHEMA IF NOT EXISTS) if it doesn't already exist.").option("--overwrite", "Overwrite the target directory if it already exists, without prompting.").option("--no-open-in-desktop", "Do not prompt to open the new app in Claude Desktop.").action(newApp);
70358
70580
  new_default = command11;
70359
70581
  });
70360
70582
 
@@ -71820,7 +72042,7 @@ var init_settings = __esm(async () => {
71820
72042
  });
71821
72043
 
71822
72044
  // src/commands/instance/instance.ts
71823
- import { writeFile as writeFile15, readdir as readdir9, mkdir as mkdir11, rm as rm3, stat as stat14 } from "node:fs/promises";
72045
+ import { writeFile as writeFile15, readdir as readdir9, mkdir as mkdir11, rm as rm4, stat as stat14 } from "node:fs/promises";
71824
72046
  import { appendFile } from "node:fs/promises";
71825
72047
  import * as path18 from "node:path";
71826
72048
  async function allInstances() {
@@ -72086,7 +72308,7 @@ Pulling workspace ` + remoteWorkspace.id);
72086
72308
  if (confirmDelete) {
72087
72309
  for (const workspace of localWorkspacesToDelete) {
72088
72310
  await removeWorkspace(workspace.id, false, {});
72089
- await rm3(path18.join(rootDir, workspace.dir), {
72311
+ await rm4(path18.join(rootDir, workspace.dir), {
72090
72312
  recursive: true
72091
72313
  });
72092
72314
  }
@@ -74170,6 +74392,10 @@ async function bootstrap2(opts, flowPath) {
74170
74392
  const metadataFile = getMetadataFileName("flow", "yaml");
74171
74393
  const flowYamlPath = `${flowDirFullPath}/${metadataFile}`;
74172
74394
  writeFileSync6(flowYamlPath, newFlowDefinitionYaml, { flag: "wx", encoding: "utf-8" });
74395
+ info(colors.green(`Created flow at ${flowDirFullPath}`));
74396
+ info("");
74397
+ info(colors.bold("To preview this flow:"));
74398
+ info(colors.gray(` wmill dev --path ${flowPath}`));
74173
74399
  }
74174
74400
  async function history2(opts, flowPath) {
74175
74401
  if (opts.json)
@@ -75218,22 +75444,22 @@ var init_gitsync_settings = __esm(async () => {
75218
75444
  async function uploadScripts(tree, workspace) {
75219
75445
  const scriptHashes = {};
75220
75446
  const workspaceDeps = [];
75221
- for (const path20 of tree.allPaths()) {
75222
- const content = tree.getContent(path20);
75223
- const itemType = tree.getItemType(path20);
75447
+ for (const path21 of tree.allPaths()) {
75448
+ const content = tree.getContent(path21);
75449
+ const itemType = tree.getItemType(path21);
75224
75450
  if (itemType === "dependencies") {
75225
75451
  if (content === undefined)
75226
75452
  continue;
75227
- const info2 = workspaceDependenciesPathToLanguageAndFilename(path20);
75453
+ const info2 = workspaceDependenciesPathToLanguageAndFilename(path21);
75228
75454
  if (info2) {
75229
75455
  const hash2 = await generateHash(content);
75230
- workspaceDeps.push({ path: path20, language: info2.language, name: info2.name, hash: hash2 });
75456
+ workspaceDeps.push({ path: path21, language: info2.language, name: info2.name, hash: hash2 });
75231
75457
  }
75232
75458
  } else if (itemType === "script") {
75233
75459
  if (!content)
75234
75460
  continue;
75235
75461
  const hash2 = await generateHash(content);
75236
- scriptHashes[path20] = hash2;
75462
+ scriptHashes[path21] = hash2;
75237
75463
  }
75238
75464
  }
75239
75465
  if (Object.keys(scriptHashes).length === 0 && workspaceDeps.length === 0)
@@ -75245,19 +75471,19 @@ async function uploadScripts(tree, workspace) {
75245
75471
  workspace_deps: workspaceDeps
75246
75472
  }
75247
75473
  });
75248
- for (const path20 of mismatched) {
75249
- const content = tree.getContent(path20);
75250
- const itemType = tree.getItemType(path20);
75474
+ for (const path21 of mismatched) {
75475
+ const content = tree.getContent(path21);
75476
+ const itemType = tree.getItemType(path21);
75251
75477
  if (itemType === "dependencies") {
75252
75478
  if (content !== undefined) {
75253
- tree.setContentHash(path20, "mismatched");
75479
+ tree.setContentHash(path21, "mismatched");
75254
75480
  }
75255
75481
  } else if (content) {
75256
75482
  const hash2 = await storeRawScriptTemp({
75257
75483
  workspace: workspace.workspaceId,
75258
75484
  requestBody: content
75259
75485
  });
75260
- tree.setContentHash(path20, hash2);
75486
+ tree.setContentHash(path21, hash2);
75261
75487
  }
75262
75488
  }
75263
75489
  }
@@ -75268,12 +75494,12 @@ class DoubleLinkedDependencyTree {
75268
75494
  setWorkspaceDeps(deps) {
75269
75495
  this.workspaceDeps = deps;
75270
75496
  }
75271
- async addNode(path20, content, language, metadata, imports, itemType, folder, originalPath, isDirectlyStale, isRawApp) {
75497
+ async addNode(path21, content, language, metadata, imports, itemType, folder, originalPath, isDirectlyStale, isRawApp) {
75272
75498
  const hasWorkspaceDeps = itemType === "script" || itemType === "inline_script";
75273
75499
  const filteredDeps = hasWorkspaceDeps ? filterWorkspaceDependencies(this.workspaceDeps, content, language) : {};
75274
75500
  const stalenessHash = await generateScriptHash({}, content, metadata);
75275
- if (!this.nodes.has(path20)) {
75276
- this.nodes.set(path20, {
75501
+ if (!this.nodes.has(path21)) {
75502
+ this.nodes.set(path21, {
75277
75503
  content: "",
75278
75504
  stalenessHash: "",
75279
75505
  language: "deno",
@@ -75286,7 +75512,7 @@ class DoubleLinkedDependencyTree {
75286
75512
  isDirectlyStale: false
75287
75513
  });
75288
75514
  }
75289
- const node = this.nodes.get(path20);
75515
+ const node = this.nodes.get(path21);
75290
75516
  node.content = content;
75291
75517
  node.stalenessHash = stalenessHash;
75292
75518
  node.language = language;
@@ -75333,59 +75559,59 @@ class DoubleLinkedDependencyTree {
75333
75559
  isDirectlyStale: false
75334
75560
  });
75335
75561
  }
75336
- this.nodes.get(importPath).importedBy.add(path20);
75562
+ this.nodes.get(importPath).importedBy.add(path21);
75337
75563
  }
75338
75564
  }
75339
- getContent(path20) {
75340
- return this.nodes.get(path20)?.content;
75565
+ getContent(path21) {
75566
+ return this.nodes.get(path21)?.content;
75341
75567
  }
75342
- getStalenessHash(path20) {
75343
- return this.nodes.get(path20)?.stalenessHash;
75568
+ getStalenessHash(path21) {
75569
+ return this.nodes.get(path21)?.stalenessHash;
75344
75570
  }
75345
- getContentHash(path20) {
75346
- return this.nodes.get(path20)?.contentHash;
75571
+ getContentHash(path21) {
75572
+ return this.nodes.get(path21)?.contentHash;
75347
75573
  }
75348
- setContentHash(path20, hash2) {
75349
- const node = this.nodes.get(path20);
75574
+ setContentHash(path21, hash2) {
75575
+ const node = this.nodes.get(path21);
75350
75576
  if (node) {
75351
75577
  node.contentHash = hash2;
75352
75578
  }
75353
75579
  }
75354
- getLanguage(path20) {
75355
- return this.nodes.get(path20)?.language;
75580
+ getLanguage(path21) {
75581
+ return this.nodes.get(path21)?.language;
75356
75582
  }
75357
- getMetadata(path20) {
75358
- return this.nodes.get(path20)?.metadata;
75583
+ getMetadata(path21) {
75584
+ return this.nodes.get(path21)?.metadata;
75359
75585
  }
75360
- getStaleReason(path20) {
75361
- return this.nodes.get(path20)?.staleReason;
75586
+ getStaleReason(path21) {
75587
+ return this.nodes.get(path21)?.staleReason;
75362
75588
  }
75363
- getItemType(path20) {
75364
- return this.nodes.get(path20)?.itemType;
75589
+ getItemType(path21) {
75590
+ return this.nodes.get(path21)?.itemType;
75365
75591
  }
75366
- getFolder(path20) {
75367
- return this.nodes.get(path20)?.folder;
75592
+ getFolder(path21) {
75593
+ return this.nodes.get(path21)?.folder;
75368
75594
  }
75369
- getIsRawApp(path20) {
75370
- return this.nodes.get(path20)?.isRawApp;
75595
+ getIsRawApp(path21) {
75596
+ return this.nodes.get(path21)?.isRawApp;
75371
75597
  }
75372
- getIsDirectlyStale(path20) {
75373
- return this.nodes.get(path20)?.isDirectlyStale ?? false;
75598
+ getIsDirectlyStale(path21) {
75599
+ return this.nodes.get(path21)?.isDirectlyStale ?? false;
75374
75600
  }
75375
- getOriginalPath(path20) {
75376
- return this.nodes.get(path20)?.originalPath;
75601
+ getOriginalPath(path21) {
75602
+ return this.nodes.get(path21)?.originalPath;
75377
75603
  }
75378
- getImports(path20) {
75379
- return this.nodes.get(path20)?.imports;
75604
+ getImports(path21) {
75605
+ return this.nodes.get(path21)?.imports;
75380
75606
  }
75381
- isStale(path20) {
75382
- return this.nodes.get(path20)?.staleReason !== undefined;
75607
+ isStale(path21) {
75608
+ return this.nodes.get(path21)?.staleReason !== undefined;
75383
75609
  }
75384
75610
  propagateStaleness() {
75385
75611
  const directlyStale = new Set;
75386
- for (const [path20, node] of this.nodes.entries()) {
75612
+ for (const [path21, node] of this.nodes.entries()) {
75387
75613
  if (node.isDirectlyStale) {
75388
- directlyStale.add(path20);
75614
+ directlyStale.add(path21);
75389
75615
  node.staleReason = "content changed";
75390
75616
  }
75391
75617
  }
@@ -75437,20 +75663,20 @@ class DoubleLinkedDependencyTree {
75437
75663
  return this.nodes.keys();
75438
75664
  }
75439
75665
  *stalePaths() {
75440
- for (const [path20, node] of this.nodes.entries()) {
75666
+ for (const [path21, node] of this.nodes.entries()) {
75441
75667
  if (node.staleReason) {
75442
- yield path20;
75668
+ yield path21;
75443
75669
  }
75444
75670
  }
75445
75671
  }
75446
- has(path20) {
75447
- return this.nodes.has(path20);
75672
+ has(path21) {
75673
+ return this.nodes.has(path21);
75448
75674
  }
75449
75675
  getMismatchedWorkspaceDeps() {
75450
75676
  const result2 = {};
75451
- for (const [path20, node] of this.nodes.entries()) {
75677
+ for (const [path21, node] of this.nodes.entries()) {
75452
75678
  if (node.itemType === "dependencies" && node.contentHash && node.content !== undefined) {
75453
- result2[path20] = node.content;
75679
+ result2[path21] = node.content;
75454
75680
  }
75455
75681
  }
75456
75682
  return result2;
@@ -75465,11 +75691,11 @@ class DoubleLinkedDependencyTree {
75465
75691
  return result2;
75466
75692
  }
75467
75693
  async persistDepsHashes(depsPaths) {
75468
- for (const path20 of depsPaths) {
75469
- const node = this.nodes.get(path20);
75694
+ for (const path21 of depsPaths) {
75695
+ const node = this.nodes.get(path21);
75470
75696
  if (node?.itemType === "dependencies" && node.content !== undefined) {
75471
- const hash2 = await generateHash(node.content + path20);
75472
- await updateMetadataGlobalLock(path20, hash2);
75697
+ const hash2 = await generateHash(node.content + path21);
75698
+ await updateMetadataGlobalLock(path21, hash2);
75473
75699
  }
75474
75700
  }
75475
75701
  }
@@ -76861,9 +77087,11 @@ await init_lint();
76861
77087
  init_mod3();
76862
77088
  init_log();
76863
77089
  init_wrapper();
76864
- init_get_port();
76865
77090
  init_open();
76866
77091
  init_script_common();
77092
+ init_extractor();
77093
+ init_path_assigner();
77094
+ init_port_probe();
76867
77095
  await __promiseAll([
76868
77096
  init_yaml(),
76869
77097
  init_utils(),
@@ -76878,13 +77106,223 @@ await __promiseAll([
76878
77106
  init_codebase(),
76879
77107
  init_local_path_scripts()
76880
77108
  ]);
77109
+ var import_yaml40 = __toESM(require_dist(), 1);
76881
77110
  import { sep as SEP20 } from "node:path";
76882
77111
  import * as http3 from "node:http";
76883
- import { realpath } from "node:fs/promises";
77112
+ import * as https from "node:https";
77113
+ import { access, readdir as readdir10, realpath, stat as stat17, unlink, writeFile as writeFile19 } from "node:fs/promises";
76884
77114
  import { watch as watch2 } from "node:fs";
77115
+ import * as path20 from "node:path";
77116
+ import * as fs13 from "node:fs";
77117
+
77118
+ // src/commands/dev/pathscript-restore.ts
77119
+ var TAG_KEY = "_originalPathScript";
77120
+ function walkModules(modules, visitor) {
77121
+ for (const module of modules) {
77122
+ if (!module.value)
77123
+ continue;
77124
+ const val = module.value;
77125
+ if (val.type === "forloopflow" || val.type === "whileloopflow") {
77126
+ walkModules(val.modules, visitor);
77127
+ } else if (val.type === "branchall") {
77128
+ for (const branch of val.branches ?? []) {
77129
+ walkModules(branch.modules, visitor);
77130
+ }
77131
+ } else if (val.type === "branchone") {
77132
+ for (const branch of val.branches ?? []) {
77133
+ walkModules(branch.modules, visitor);
77134
+ }
77135
+ if (val.default) {
77136
+ walkModules(val.default, visitor);
77137
+ }
77138
+ } else if (val.type === "aiagent") {
77139
+ for (const tool of val.tools ?? []) {
77140
+ visitor.onTool(tool);
77141
+ }
77142
+ } else {
77143
+ visitor.onModule(module);
77144
+ }
77145
+ }
77146
+ }
77147
+ function walkFlow(flowValue, visitor) {
77148
+ if (flowValue?.modules)
77149
+ walkModules(flowValue.modules, visitor);
77150
+ if (flowValue?.failure_module)
77151
+ walkModules([flowValue.failure_module], visitor);
77152
+ if (flowValue?.preprocessor_module)
77153
+ walkModules([flowValue.preprocessor_module], visitor);
77154
+ }
77155
+ function snapshotPathScripts(flowValue) {
77156
+ walkFlow(flowValue, {
77157
+ onModule(module) {
77158
+ if (module.value.type === "script") {
77159
+ module[TAG_KEY] = JSON.parse(JSON.stringify(module.value));
77160
+ }
77161
+ },
77162
+ onTool(tool) {
77163
+ const tv = tool.value;
77164
+ if (tv && "tool_type" in tv && tv.tool_type === "flowmodule" && tv.type === "script") {
77165
+ tool[TAG_KEY] = JSON.parse(JSON.stringify(tv));
77166
+ }
77167
+ }
77168
+ });
77169
+ }
77170
+ function tagReplacedPathScripts(flowValue) {
77171
+ walkFlow(flowValue, {
77172
+ onModule(module) {
77173
+ if (module[TAG_KEY] && module.value.type === "rawscript") {
77174
+ module.value[TAG_KEY] = module[TAG_KEY];
77175
+ delete module[TAG_KEY];
77176
+ } else if (module[TAG_KEY]) {
77177
+ delete module[TAG_KEY];
77178
+ }
77179
+ },
77180
+ onTool(tool) {
77181
+ const tv = tool.value;
77182
+ if (tool[TAG_KEY] && tv && "tool_type" in tv && tv.tool_type === "flowmodule" && tv.type === "rawscript") {
77183
+ tv[TAG_KEY] = tool[TAG_KEY];
77184
+ delete tool[TAG_KEY];
77185
+ } else if (tool[TAG_KEY]) {
77186
+ delete tool[TAG_KEY];
77187
+ }
77188
+ }
77189
+ });
77190
+ }
77191
+ function restorePathScripts(flowValue) {
77192
+ walkFlow(flowValue, {
77193
+ onModule(module) {
77194
+ if (module.value[TAG_KEY]) {
77195
+ module.value = module.value[TAG_KEY];
77196
+ }
77197
+ },
77198
+ onTool(tool) {
77199
+ if (tool.value?.[TAG_KEY]) {
77200
+ tool.value = tool.value[TAG_KEY];
77201
+ }
77202
+ }
77203
+ });
77204
+ }
77205
+
77206
+ // src/commands/dev/dev.ts
76885
77207
  var PORT = 3001;
77208
+ var FLOW_SUFFIXES = [".flow", "__flow"];
77209
+ var APP_SUFFIXES = [".app", "__app", ".raw_app", "__raw_app"];
77210
+ var INLINE_SCRIPT_EXTS = new Set([
77211
+ ...exts.map((e) => path20.extname("x" + e)).filter((e) => e !== ".yml"),
77212
+ ".js"
77213
+ ]);
77214
+ function stripFolderSuffix(rel, suffixes) {
77215
+ for (const s of suffixes) {
77216
+ if (rel.endsWith(s))
77217
+ return rel.slice(0, -s.length);
77218
+ }
77219
+ return rel;
77220
+ }
77221
+ function isFlowFolderName(name) {
77222
+ return FLOW_SUFFIXES.some((s) => name.endsWith(s));
77223
+ }
77224
+ function normalizeWmPath(p) {
77225
+ return stripFolderSuffix(p.replace(/\/$/, ""), FLOW_SUFFIXES);
77226
+ }
77227
+ function isInsideFlowFolder(cpath) {
77228
+ return cpath.split("/").some(isFlowFolderName);
77229
+ }
77230
+ function findFlowFolderPrefix(cpath) {
77231
+ const segs = cpath.split("/");
77232
+ for (let i = 0;i < segs.length; i++) {
77233
+ if (isFlowFolderName(segs[i])) {
77234
+ return segs.slice(0, i + 1).join("/") + "/";
77235
+ }
77236
+ }
77237
+ return;
77238
+ }
77239
+ async function listWorkspacePaths() {
77240
+ const items = [];
77241
+ async function walk(dir, rel) {
77242
+ let entries;
77243
+ try {
77244
+ entries = await readdir10(dir, { withFileTypes: true });
77245
+ } catch {
77246
+ return;
77247
+ }
77248
+ for (const entry of entries) {
77249
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
77250
+ continue;
77251
+ const childRel = rel ? `${rel}/${entry.name}` : entry.name;
77252
+ const childAbs = path20.join(dir, entry.name);
77253
+ if (entry.isDirectory()) {
77254
+ if (isFlowFolderName(entry.name)) {
77255
+ items.push({
77256
+ path: stripFolderSuffix(childRel, FLOW_SUFFIXES),
77257
+ kind: "flow",
77258
+ _metaPath: path20.join(childAbs, "flow.yaml")
77259
+ });
77260
+ continue;
77261
+ }
77262
+ if (APP_SUFFIXES.some((s) => entry.name.endsWith(s))) {
77263
+ items.push({ path: stripFolderSuffix(childRel, APP_SUFFIXES), kind: "raw_app" });
77264
+ continue;
77265
+ }
77266
+ await walk(childAbs, childRel);
77267
+ } else if (entry.isFile()) {
77268
+ const matchedExt = exts.find((ext2) => entry.name.endsWith(ext2));
77269
+ if (matchedExt) {
77270
+ const noExtAbs = childAbs.slice(0, -matchedExt.length);
77271
+ items.push({
77272
+ path: childRel.slice(0, -matchedExt.length),
77273
+ kind: "script",
77274
+ _metaPath: noExtAbs + ".script.yaml"
77275
+ });
77276
+ }
77277
+ }
77278
+ }
77279
+ }
77280
+ await walk(process.cwd(), "");
77281
+ await Promise.all(items.map(async (item) => {
77282
+ if (!item._metaPath)
77283
+ return;
77284
+ try {
77285
+ const meta = await yamlParseFile(item._metaPath);
77286
+ if (typeof meta?.summary === "string" && meta.summary.length > 0) {
77287
+ item.summary = meta.summary;
77288
+ }
77289
+ } catch {}
77290
+ }));
77291
+ items.sort((a, b) => a.path.localeCompare(b.path));
77292
+ return items.map(({ _metaPath, ...item }) => item);
77293
+ }
76886
77294
  async function dev2(opts) {
77295
+ if (!opts.path) {
77296
+ const cwd = process.cwd();
77297
+ const cwdBasename = path20.basename(cwd);
77298
+ await loadNonDottedPathsSetting();
77299
+ if (isFlowFolderName(cwdBasename)) {
77300
+ GLOBAL_CONFIG_OPT.noCdToRoot = true;
77301
+ let searchDir = cwd;
77302
+ let workspaceRoot;
77303
+ while (true) {
77304
+ const wmillYaml = path20.join(searchDir, "wmill.yaml");
77305
+ if (fs13.existsSync(wmillYaml)) {
77306
+ workspaceRoot = searchDir;
77307
+ break;
77308
+ }
77309
+ const parentDir = path20.dirname(searchDir);
77310
+ if (parentDir === searchDir)
77311
+ break;
77312
+ searchDir = parentDir;
77313
+ }
77314
+ if (workspaceRoot) {
77315
+ const relPath = path20.relative(workspaceRoot, cwd).replaceAll("\\", "/");
77316
+ opts.path = stripFolderSuffix(relPath, FLOW_SUFFIXES);
77317
+ info(`Detected flow folder, path: ${opts.path}`);
77318
+ process.chdir(workspaceRoot);
77319
+ }
77320
+ }
77321
+ }
76887
77322
  opts = await mergeConfigWithConfigFile(opts);
77323
+ if (opts.path) {
77324
+ opts.path = normalizeWmPath(opts.path);
77325
+ }
76888
77326
  const workspace = await resolveWorkspace(opts);
76889
77327
  await requireLogin(opts);
76890
77328
  info("Started dev mode");
@@ -76916,39 +77354,50 @@ async function dev2(opts) {
76916
77354
  });
76917
77355
  });
76918
77356
  }
76919
- const flowFolderSuffix = getFolderSuffixWithSep("flow");
76920
77357
  const flowMetadataFile = getMetadataFileName("flow", "yaml");
76921
77358
  async function loadPaths(pathsToLoad) {
76922
- const paths = pathsToLoad.filter((path20) => exts.some((ext2) => path20.endsWith(ext2) || path20.endsWith(flowFolderSuffix + flowMetadataFile)));
77359
+ const paths = pathsToLoad.filter((p) => exts.some((ext2) => p.endsWith(ext2) || p.endsWith(".flow/" + flowMetadataFile) || p.endsWith("__flow/" + flowMetadataFile)));
76923
77360
  if (paths.length == 0) {
76924
77361
  return;
76925
77362
  }
76926
77363
  const nativePath = (await realpath(paths[0])).replace(base + SEP20, "");
76927
77364
  const cpath = nativePath.replaceAll("\\", "/");
76928
- if (!ignore(nativePath, false)) {
76929
- const typ = getTypeStrFromPath(cpath);
77365
+ const insideFlow = isInsideFlowFolder(cpath);
77366
+ if (insideFlow || !ignore(nativePath, false)) {
77367
+ let typ;
77368
+ if (insideFlow) {
77369
+ typ = "flow";
77370
+ } else {
77371
+ typ = getTypeStrFromPath(cpath);
77372
+ }
76930
77373
  info("Detected change in " + cpath + " (" + typ + ")");
76931
77374
  if (typ == "flow") {
76932
- const localPath = extractFolderPath(cpath, "flow");
77375
+ let localPath = extractFolderPath(cpath, "flow") ?? findFlowFolderPrefix(cpath);
77376
+ if (!localPath)
77377
+ return;
77378
+ const wmFlowPath = stripFolderSuffix(localPath.replace(/\/$/, ""), FLOW_SUFFIXES);
76933
77379
  const localFlow = await yamlParseFile(localPath + "flow.yaml");
76934
- await replaceInlineScripts(localFlow.value.modules, async (path20) => await readTextFile(localPath + path20), exports_log, localPath, SEP20, undefined);
77380
+ await replaceInlineScripts(localFlow.value.modules, async (path21) => await readTextFile(localPath + path21), exports_log, localPath, SEP20, undefined);
77381
+ snapshotPathScripts(localFlow.value);
76935
77382
  const localScriptReader = createPreviewLocalScriptReader({
76936
77383
  exts,
76937
77384
  defaultTs: opts.defaultTs,
76938
77385
  codebases
76939
77386
  });
76940
77387
  await replaceAllPathScriptsWithLocal(localFlow.value, localScriptReader, exports_log);
77388
+ tagReplacedPathScripts(localFlow.value);
76941
77389
  currentLastEdit = {
76942
77390
  type: "flow",
76943
77391
  flow: localFlow,
76944
- uriPath: localPath
77392
+ uriPath: localPath,
77393
+ path: wmFlowPath
76945
77394
  };
76946
- info("Updated " + localPath);
77395
+ info("Updated " + wmFlowPath);
76947
77396
  broadcastChanges(currentLastEdit);
76948
77397
  } else if (typ == "script") {
76949
- const content = await readTextFile(cpath);
76950
77398
  const splitted = cpath.split(".");
76951
77399
  const wmPath = splitted[0];
77400
+ const content = await readTextFile(cpath);
76952
77401
  const lang = inferContentTypeFromFilePath(cpath, opts.defaultTs);
76953
77402
  const typed = (await parseMetadataFile(removeExtensionToPath(cpath), undefined))?.payload;
76954
77403
  currentLastEdit = {
@@ -76964,29 +77413,164 @@ async function dev2(opts) {
76964
77413
  }
76965
77414
  }
76966
77415
  }
77416
+ async function loadWmPath(wmPath) {
77417
+ wmPath = normalizeWmPath(wmPath);
77418
+ let flowDir;
77419
+ let flowYaml;
77420
+ for (const suffix of [".flow", "__flow"]) {
77421
+ const candidate = wmPath + suffix + "/";
77422
+ try {
77423
+ await access(candidate + "flow.yaml");
77424
+ flowDir = candidate;
77425
+ flowYaml = candidate + "flow.yaml";
77426
+ break;
77427
+ } catch {}
77428
+ }
77429
+ try {
77430
+ if (!flowDir || !flowYaml)
77431
+ throw new Error("not a flow");
77432
+ const localFlow = await yamlParseFile(flowYaml);
77433
+ await replaceInlineScripts(localFlow.value.modules, async (p) => await readTextFile(flowDir + p), exports_log, flowDir, SEP20, undefined);
77434
+ snapshotPathScripts(localFlow.value);
77435
+ const localScriptReader = createPreviewLocalScriptReader({
77436
+ exts,
77437
+ defaultTs: opts.defaultTs,
77438
+ codebases
77439
+ });
77440
+ await replaceAllPathScriptsWithLocal(localFlow.value, localScriptReader, exports_log);
77441
+ tagReplacedPathScripts(localFlow.value);
77442
+ const edit = {
77443
+ type: "flow",
77444
+ flow: localFlow,
77445
+ uriPath: flowDir,
77446
+ path: wmPath
77447
+ };
77448
+ currentLastEdit = edit;
77449
+ return edit;
77450
+ } catch {}
77451
+ for (const ext2 of exts) {
77452
+ const filePath = wmPath + ext2;
77453
+ try {
77454
+ await access(filePath);
77455
+ const content = await readTextFile(filePath);
77456
+ const lang = inferContentTypeFromFilePath(filePath, opts.defaultTs);
77457
+ const typed = (await parseMetadataFile(removeExtensionToPath(filePath), undefined))?.payload;
77458
+ const edit = {
77459
+ type: "script",
77460
+ content,
77461
+ path: wmPath,
77462
+ language: lang,
77463
+ tag: typed?.tag,
77464
+ lock: typed?.lock
77465
+ };
77466
+ currentLastEdit = edit;
77467
+ return edit;
77468
+ } catch {
77469
+ continue;
77470
+ }
77471
+ }
77472
+ error(`Could not find file for path: ${wmPath}`);
77473
+ return;
77474
+ }
77475
+ async function handleFlowRoundTrip(data3) {
77476
+ if (!data3.uriPath || !data3.flow?.value)
77477
+ return;
77478
+ let flowDir = data3.uriPath;
77479
+ if (!flowDir.endsWith("/"))
77480
+ flowDir += "/";
77481
+ if (flowDir.includes("://")) {
77482
+ flowDir = new URL(flowDir).pathname;
77483
+ }
77484
+ restorePathScripts(data3.flow.value);
77485
+ const flowYamlPath = flowDir + "flow.yaml";
77486
+ let currentLoadedFlow;
77487
+ let currentLoadedFailureModule;
77488
+ let currentLoadedPreprocessorModule;
77489
+ try {
77490
+ const currentFlow = await yamlParseFile(flowYamlPath);
77491
+ currentLoadedFlow = currentFlow.value?.modules;
77492
+ currentLoadedFailureModule = currentFlow.value?.failure_module;
77493
+ currentLoadedPreprocessorModule = currentFlow.value?.preprocessor_module;
77494
+ } catch {}
77495
+ const inlineScriptMapping = {};
77496
+ extractCurrentMapping(currentLoadedFlow, inlineScriptMapping, currentLoadedFailureModule, currentLoadedPreprocessorModule);
77497
+ const extractOptions = { skipInlineScriptSuffix: getNonDottedPaths() };
77498
+ const pathAssigner = newPathAssigner(opts.defaultTs ?? "bun", extractOptions);
77499
+ const allExtracted = extractInlineScripts(data3.flow.value.modules ?? [], inlineScriptMapping, "/", opts.defaultTs ?? "bun", pathAssigner, extractOptions);
77500
+ if (data3.flow.value.failure_module?.value?.type === "rawscript") {
77501
+ allExtracted.push(...extractInlineScripts([data3.flow.value.failure_module], inlineScriptMapping, "/", opts.defaultTs ?? "bun", pathAssigner, extractOptions));
77502
+ }
77503
+ if (data3.flow.value.preprocessor_module?.value?.type === "rawscript") {
77504
+ allExtracted.push(...extractInlineScripts([data3.flow.value.preprocessor_module], inlineScriptMapping, "/", opts.defaultTs ?? "bun", pathAssigner, extractOptions));
77505
+ }
77506
+ for (const s of allExtracted) {
77507
+ const filePath = flowDir + s.path;
77508
+ if (s.content.startsWith("!inline ")) {
77509
+ try {
77510
+ await stat17(filePath);
77511
+ } catch {
77512
+ await writeFile19(filePath, "", "utf-8");
77513
+ }
77514
+ continue;
77515
+ }
77516
+ let needsWrite = true;
77517
+ try {
77518
+ const existing = await readTextFile(filePath);
77519
+ if (existing === s.content)
77520
+ needsWrite = false;
77521
+ } catch {}
77522
+ if (needsWrite) {
77523
+ await writeFile19(filePath, s.content, "utf-8");
77524
+ info(`Wrote inline script: ${filePath}`);
77525
+ }
77526
+ }
77527
+ const flowYaml = import_yaml40.stringify(data3.flow);
77528
+ let currentYaml;
77529
+ try {
77530
+ currentYaml = await readTextFile(flowYamlPath);
77531
+ } catch {}
77532
+ if (currentYaml?.trimEnd() !== flowYaml.trimEnd()) {
77533
+ await writeFile19(flowYamlPath, flowYaml, "utf-8");
77534
+ info(`Wrote flow: ${flowYamlPath}`);
77535
+ }
77536
+ const extractedPaths = new Set(allExtracted.map((s) => s.path));
77537
+ try {
77538
+ const dirFiles = await readdir10(flowDir);
77539
+ for (const file of dirFiles) {
77540
+ if (file === "flow.yaml" || file === "flow.json" || file.startsWith("."))
77541
+ continue;
77542
+ if (!INLINE_SCRIPT_EXTS.has(path20.extname(file)))
77543
+ continue;
77544
+ if (!extractedPaths.has(file)) {
77545
+ await unlink(flowDir + file);
77546
+ info(`Removed orphaned file: ${flowDir + file}`);
77547
+ }
77548
+ }
77549
+ } catch {}
77550
+ }
76967
77551
  const connectedClients = new Set;
76968
77552
  function broadcastChanges(lastEdit) {
77553
+ if (opts.path && normalizeWmPath(lastEdit.path) !== opts.path) {
77554
+ return;
77555
+ }
76969
77556
  for (const client of connectedClients.values()) {
76970
77557
  client.send(JSON.stringify(lastEdit));
76971
77558
  }
76972
77559
  }
76973
- async function startApp() {
76974
- const server = http3.createServer((_req, res) => {
76975
- res.writeHead(200);
76976
- res.end();
76977
- });
76978
- const wss = new import_websocket_server.default({ server });
77560
+ function setupDevWs(wss) {
76979
77561
  wss.on("connection", (ws) => {
76980
77562
  connectedClients.add(ws);
76981
- console.log("New client connected");
76982
- ws.on("open", () => {
76983
- if (currentLastEdit) {
76984
- broadcastChanges(currentLastEdit);
77563
+ console.log("New dev client connected");
77564
+ if (currentLastEdit && ws.readyState === import_websocket.default.OPEN) {
77565
+ try {
77566
+ ws.send(JSON.stringify(currentLastEdit));
77567
+ } catch (e) {
77568
+ console.error("Failed to push initial state to new client:", e);
76985
77569
  }
76986
- });
77570
+ }
76987
77571
  ws.on("close", () => {
76988
77572
  connectedClients.delete(ws);
76989
- console.log("Client disconnected");
77573
+ console.log("Dev client disconnected");
76990
77574
  });
76991
77575
  ws.on("message", (message) => {
76992
77576
  let data3;
@@ -76998,29 +77582,184 @@ async function dev2(opts) {
76998
77582
  }
76999
77583
  if (data3.type === "load") {
77000
77584
  loadPaths([data3.path]);
77585
+ } else if (data3.type === "flow") {
77586
+ handleFlowRoundTrip(data3).catch((err) => {
77587
+ error(`Failed to write flow changes: ${err}`);
77588
+ });
77589
+ } else if (data3.type === "loadWmPath") {
77590
+ loadWmPath(data3.path).then((edit) => {
77591
+ if (edit && ws.readyState === import_websocket.default.OPEN) {
77592
+ ws.send(JSON.stringify(edit));
77593
+ }
77594
+ }).catch((err) => {
77595
+ error(`Failed to load path ${data3.path}: ${err}`);
77596
+ });
77597
+ } else if (data3.type === "listPaths") {
77598
+ listWorkspacePaths().then((items) => {
77599
+ if (ws.readyState === import_websocket.default.OPEN) {
77600
+ ws.send(JSON.stringify({ type: "paths", items }));
77601
+ }
77602
+ }).catch((err) => {
77603
+ error(`Failed to list paths: ${err}`);
77604
+ });
77001
77605
  }
77002
77606
  });
77003
77607
  });
77004
- const port = await getPorts({ port: 3001 });
77005
- const url = `${workspace.remote}dev?workspace=${workspace.workspaceId}&local=true&wm_token=${workspace.token}` + (port === PORT ? "" : `&port=${port}`);
77006
- console.log(`Go to ${url}`);
77608
+ }
77609
+ function maybeOpenBrowser(url) {
77610
+ if (opts.open === false)
77611
+ return;
77007
77612
  try {
77008
- openApp(apps.browser, { arguments: [url] }).catch((error2) => {
77613
+ open_default(url).catch((error2) => {
77009
77614
  console.error(`Failed to open browser, please navigate to ${url}, error: ${error2}`);
77010
77615
  });
77011
- console.log("Opened browser for you");
77616
+ console.log(`Opened browser at ${url}`);
77012
77617
  } catch (error2) {
77013
77618
  console.error(`Failed to open browser, please navigate to ${url}, ${error2}`);
77014
77619
  }
77015
- console.log("Dev server will automatically point to the last script edited locally");
77016
- server.listen(port, () => {
77017
- console.log(`Server listening on port ${port}`);
77620
+ }
77621
+ async function startProxyServer(requestedPort) {
77622
+ const proxyPort = await resolveBindPort(requestedPort, "--proxy-port", {
77623
+ info: (m) => console.log(m),
77624
+ warn: (m) => console.warn(m)
77625
+ });
77626
+ const remote = new URL(workspace.remote);
77627
+ const isHttps = remote.protocol === "https:";
77628
+ const remoteHost = remote.hostname;
77629
+ const remotePort = remote.port ? parseInt(remote.port) : isHttps ? 443 : 80;
77630
+ const httpModule = isHttps ? https : http3;
77631
+ const devWss = new import_websocket_server.default({ noServer: true });
77632
+ setupDevWs(devWss);
77633
+ const proxyWss = new import_websocket_server.default({ noServer: true });
77634
+ const proxyServer = http3.createServer((clientReq, clientRes) => {
77635
+ const parsedUrl = new URL(clientReq.url ?? "/", `http://localhost`);
77636
+ if (parsedUrl.pathname === "/" || parsedUrl.pathname === "") {
77637
+ let devUrl = `/dev?workspace=${workspace.workspaceId}&local=true&wm_token=${workspace.token}&port=${proxyPort}`;
77638
+ if (opts.path) {
77639
+ devUrl += `&path=${opts.path}`;
77640
+ }
77641
+ clientRes.writeHead(302, { Location: devUrl });
77642
+ clientRes.end();
77643
+ return;
77644
+ }
77645
+ const fwdHeaders = {
77646
+ ...clientReq.headers,
77647
+ host: remote.host
77648
+ };
77649
+ delete fwdHeaders["connection"];
77650
+ delete fwdHeaders["keep-alive"];
77651
+ delete fwdHeaders["transfer-encoding"];
77652
+ delete fwdHeaders["accept-encoding"];
77653
+ const proxyOpts = {
77654
+ hostname: remoteHost,
77655
+ port: remotePort,
77656
+ path: clientReq.url,
77657
+ method: clientReq.method,
77658
+ headers: fwdHeaders
77659
+ };
77660
+ const proxyReq = httpModule.request(proxyOpts, (proxyRes) => {
77661
+ const setCookie = proxyRes.headers["set-cookie"];
77662
+ if (setCookie) {
77663
+ proxyRes.headers["set-cookie"] = setCookie.map((cookie) => cookie.replace(/domain=[^;]+/gi, "domain=localhost"));
77664
+ }
77665
+ clientRes.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
77666
+ proxyRes.pipe(clientRes, { end: true });
77667
+ });
77668
+ proxyReq.on("error", (err) => {
77669
+ console.error("Proxy error:", err.message);
77670
+ clientRes.writeHead(502);
77671
+ clientRes.end("Bad Gateway");
77672
+ });
77673
+ clientReq.pipe(proxyReq, { end: true });
77674
+ });
77675
+ proxyServer.on("upgrade", (req, socket, head) => {
77676
+ const pathname = req.url?.split("?")[0] ?? "";
77677
+ if (pathname === "/ws_dev" || pathname === "/ws") {
77678
+ devWss.handleUpgrade(req, socket, head, (ws) => {
77679
+ devWss.emit("connection", ws, req);
77680
+ });
77681
+ return;
77682
+ }
77683
+ if (pathname.startsWith("/ws/") || pathname.startsWith("/ws_mp/") || pathname.startsWith("/ws_debug/")) {
77684
+ const wsProtocol = isHttps ? "wss" : "ws";
77685
+ const remoteWsUrl = `${wsProtocol}://${remote.host}${req.url}`;
77686
+ const remoteWs = new import_websocket.default(remoteWsUrl, {
77687
+ headers: {
77688
+ ...req.headers,
77689
+ host: remote.host
77690
+ }
77691
+ });
77692
+ remoteWs.on("open", () => {
77693
+ proxyWss.handleUpgrade(req, socket, head, (clientWs) => {
77694
+ clientWs.on("message", (data3) => {
77695
+ if (remoteWs.readyState === import_websocket.default.OPEN) {
77696
+ remoteWs.send(data3);
77697
+ }
77698
+ });
77699
+ remoteWs.on("message", (data3) => {
77700
+ if (clientWs.readyState === import_websocket.default.OPEN) {
77701
+ clientWs.send(data3);
77702
+ }
77703
+ });
77704
+ clientWs.on("close", () => remoteWs.close());
77705
+ remoteWs.on("close", () => clientWs.close());
77706
+ });
77707
+ });
77708
+ remoteWs.on("error", (err) => {
77709
+ console.error("WebSocket proxy error:", err.message);
77710
+ socket.destroy();
77711
+ });
77712
+ return;
77713
+ }
77714
+ socket.destroy();
77715
+ });
77716
+ return new Promise((resolve8) => {
77717
+ proxyServer.listen(proxyPort, BIND_HOST, () => {
77718
+ console.log(`Dev proxy listening on http://localhost:${proxyPort}`);
77719
+ if (opts.path) {
77720
+ console.log(`Watching ${opts.path} — edits will live-reload in the dev page`);
77721
+ } else {
77722
+ console.log("Open the dev page and pick a flow or script to preview — edits will live-reload");
77723
+ console.log("(pass --path <path> to skip the picker)");
77724
+ }
77725
+ maybeOpenBrowser(`http://localhost:${proxyPort}/`);
77726
+ resolve8();
77727
+ });
77728
+ });
77729
+ }
77730
+ async function startDirectServer() {
77731
+ const server = http3.createServer((_req, res) => {
77732
+ res.writeHead(200);
77733
+ res.end();
77734
+ });
77735
+ const wss = new import_websocket_server.default({ server });
77736
+ setupDevWs(wss);
77737
+ const port = await resolveBindPort(PORT, "wmill dev", {
77738
+ info: (m) => console.log(m),
77739
+ warn: (m) => console.warn(m)
77740
+ });
77741
+ const url = `${workspace.remote}dev?workspace=${workspace.workspaceId}&local=true&wm_token=${workspace.token}` + (port === PORT ? "" : `&port=${port}`) + (opts.path ? `&path=${opts.path}` : "");
77742
+ if (opts.open === false) {
77743
+ console.log(`Go to ${url}`);
77744
+ }
77745
+ maybeOpenBrowser(url);
77746
+ if (opts.path) {
77747
+ console.log(`Watching ${opts.path} — edits will live-reload in the dev page`);
77748
+ } else {
77749
+ console.log("Open the dev page and pick a flow or script to preview — edits will live-reload");
77750
+ }
77751
+ server.listen(port, BIND_HOST, () => {
77752
+ console.log(`Dev WebSocket listening on ws://localhost:${port}/ws`);
77018
77753
  });
77019
77754
  }
77020
- await Promise.all([startApp(), watchChanges()]);
77755
+ if (opts.path) {
77756
+ await loadWmPath(opts.path);
77757
+ }
77758
+ const startServer = opts.proxyPort ? () => startProxyServer(opts.proxyPort) : () => startDirectServer();
77759
+ await Promise.all([startServer(), watchChanges()]);
77021
77760
  console.log("Stopped dev mode");
77022
77761
  }
77023
- var command24 = new Command().description("Launch a dev server that watches for local file changes and auto-pushes them to the remote workspace. Provides live reload for scripts and flows during development.").option("--includes <pattern...:string>", "Filter paths givena glob pattern or path").action(dev2);
77762
+ var command24 = new Command().description("Watch local file changes and live-reload the dev page for preview. Does NOT deploy to the remote workspace use wmill sync push for that.").option("--includes <pattern...:string>", "Filter paths given a glob pattern or path").option("--proxy-port <port:number>", "Port for a localhost reverse proxy to the remote Windmill server").option("--path <path:string>", "Watch a specific windmill path (e.g., u/admin/my_script or f/my_flow)").option("--no-open", "Do not open the browser automatically").action(dev2);
77024
77763
  var dev_default2 = command24;
77025
77764
 
77026
77765
  // src/main.ts
@@ -77262,12 +78001,12 @@ await __promiseAll([
77262
78001
  init_workspace(),
77263
78002
  init_resource_type()
77264
78003
  ]);
77265
- import { stat as stat18, writeFile as writeFile20, rm as rm4 } from "node:fs/promises";
78004
+ import { stat as stat19, writeFile as writeFile21, rm as rm5 } from "node:fs/promises";
77266
78005
 
77267
78006
  // src/guidance/writer.ts
77268
78007
  await init_utils();
77269
- import { cp, mkdir as mkdir13, readdir as readdir10, stat as stat17, writeFile as writeFile19 } from "node:fs/promises";
77270
- import { join as join17 } from "node:path";
78008
+ import { cp, mkdir as mkdir13, readdir as readdir11, stat as stat18, writeFile as writeFile20 } from "node:fs/promises";
78009
+ import { join as join18 } from "node:path";
77271
78010
 
77272
78011
  // src/guidance/core.ts
77273
78012
  function generateAgentsMdContent(skillsReference) {
@@ -77287,11 +78026,12 @@ You MUST use the \`write-script-<language>\` skill to write or modify scripts in
77287
78026
  ## Flow Writing Guide
77288
78027
 
77289
78028
  You MUST use the \`write-flow\` skill to create or modify flows.
78029
+ When a new flow needs to be created, YOU run \`wmill flow new <path>\` yourself (with \`--summary\` and optional \`--description\`) to scaffold the folder and \`flow.yaml\`, then edit \`flow.yaml\` to fill in modules and schema. Do NOT scaffold the folder + yaml by hand and do NOT tell the user to run \`wmill flow new\`. If path or summary are missing from the user's request, ask via \`AskUserQuestion\` (one call, all missing fields) — never invent them. See the \`write-flow\` skill for the procedure.
77290
78030
 
77291
78031
  ## Raw App Development
77292
78032
 
77293
78033
  You MUST use the \`raw-app\` skill to create or modify raw apps.
77294
- Whenever a new app needs to be created you MUST ask the user to run \`wmill app new\` in its terminal first.
78034
+ When a new app needs to be created, YOU run \`wmill app new\` yourself with \`--summary\`, \`--path\`, and \`--framework\` flags (and any other relevant flags). Do NOT ask the user to run it. If you don't have the values for those flags, ask the user via \`AskUserQuestion\` (one call, all missing fields) — never invent them. See the \`raw-app\` skill for the full procedure.
77295
78035
 
77296
78036
  ## Triggers
77297
78037
 
@@ -77305,6 +78045,10 @@ You MUST use the \`schedules\` skill to configure cron schedules.
77305
78045
 
77306
78046
  You MUST use the \`resources\` skill to manage resource types and credentials.
77307
78047
 
78048
+ ## Visual Preview
78049
+
78050
+ You MUST use the \`preview\` skill any time the user wants to see/open/visualize/preview a flow, script, or app in the dev page — and after writing one, when offering visual verification. The skill picks between an MCP-embedded proxy (one named \`launch.json\` entry per target) and direct mode (URL handed to the user) based on what tools you have.
78051
+
77308
78052
  ## CLI Reference
77309
78053
 
77310
78054
  You MUST use the \`cli-commands\` skill to use the CLI.
@@ -77357,7 +78101,8 @@ var SKILLS = [
77357
78101
  { name: "triggers", description: "MUST use when configuring triggers." },
77358
78102
  { name: "schedules", description: "MUST use when configuring schedules." },
77359
78103
  { name: "resources", description: "MUST use when managing resources." },
77360
- { name: "cli-commands", description: "MUST use when using the CLI, including debugging job failures and inspecting run history via `wmill job`." }
78104
+ { name: "cli-commands", description: "MUST use when using the CLI, including debugging job failures and inspecting run history via `wmill job`." },
78105
+ { name: "preview", description: "MUST use when opening the Windmill dev page / visual preview of a flow, script, or app. Triggers on words like preview, open, navigate to, visualize, see the flow/app/script, and after writing a flow/script/app for visual verification." }
77361
78106
  ];
77362
78107
  var SKILL_CONTENT = {
77363
78108
  "write-script-bash": `---
@@ -77367,11 +78112,36 @@ description: MUST use when writing Bash scripts.
77367
78112
 
77368
78113
  ## CLI Commands
77369
78114
 
77370
- Place scripts in a folder. After writing, tell the user they can run:
77371
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
77372
- - \`wmill sync push\` - Deploy to Windmill
78115
+ Place scripts in a folder.
78116
+
78117
+ After writing, tell the user which command fits what they want to do:
78118
+
78119
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
78120
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
78121
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
78122
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
78123
+
78124
+ ### Preview vs run — choose by intent, not habit
78125
+
78126
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
78127
+
78128
+ Only use \`script run\` when:
78129
+ - The user explicitly says "run the deployed version" / "run what's on the server".
78130
+ - There is no local script being edited (you're just invoking an existing script).
77373
78131
 
77374
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
78132
+ Only use \`sync push\` when:
78133
+ - The user explicitly asks to deploy, publish, push, or ship.
78134
+ - The preview has already validated the change and the user wants it in the workspace.
78135
+
78136
+ ### After writing — offer to test, don't wait passively
78137
+
78138
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
78139
+
78140
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
78141
+
78142
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
78143
+
78144
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
77375
78145
 
77376
78146
  Use \`wmill resource-type list --schema\` to discover available resource types.
77377
78147
 
@@ -77432,11 +78202,36 @@ description: MUST use when writing BigQuery queries.
77432
78202
 
77433
78203
  ## CLI Commands
77434
78204
 
77435
- Place scripts in a folder. After writing, tell the user they can run:
77436
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
77437
- - \`wmill sync push\` - Deploy to Windmill
78205
+ Place scripts in a folder.
78206
+
78207
+ After writing, tell the user which command fits what they want to do:
78208
+
78209
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
78210
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
78211
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
78212
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
78213
+
78214
+ ### Preview vs run — choose by intent, not habit
78215
+
78216
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
78217
+
78218
+ Only use \`script run\` when:
78219
+ - The user explicitly says "run the deployed version" / "run what's on the server".
78220
+ - There is no local script being edited (you're just invoking an existing script).
78221
+
78222
+ Only use \`sync push\` when:
78223
+ - The user explicitly asks to deploy, publish, push, or ship.
78224
+ - The preview has already validated the change and the user wants it in the workspace.
78225
+
78226
+ ### After writing — offer to test, don't wait passively
77438
78227
 
77439
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
78228
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
78229
+
78230
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
78231
+
78232
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
78233
+
78234
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
77440
78235
 
77441
78236
  Use \`wmill resource-type list --schema\` to discover available resource types.
77442
78237
 
@@ -77459,11 +78254,36 @@ description: MUST use when writing Bun/TypeScript scripts.
77459
78254
 
77460
78255
  ## CLI Commands
77461
78256
 
77462
- Place scripts in a folder. After writing, tell the user they can run:
77463
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
77464
- - \`wmill sync push\` - Deploy to Windmill
78257
+ Place scripts in a folder.
78258
+
78259
+ After writing, tell the user which command fits what they want to do:
78260
+
78261
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
78262
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
78263
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
78264
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
78265
+
78266
+ ### Preview vs run — choose by intent, not habit
78267
+
78268
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
78269
+
78270
+ Only use \`script run\` when:
78271
+ - The user explicitly says "run the deployed version" / "run what's on the server".
78272
+ - There is no local script being edited (you're just invoking an existing script).
78273
+
78274
+ Only use \`sync push\` when:
78275
+ - The user explicitly asks to deploy, publish, push, or ship.
78276
+ - The preview has already validated the change and the user wants it in the workspace.
78277
+
78278
+ ### After writing — offer to test, don't wait passively
78279
+
78280
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
77465
78281
 
77466
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
78282
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
78283
+
78284
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
78285
+
78286
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
77467
78287
 
77468
78288
  Use \`wmill resource-type list --schema\` to discover available resource types.
77469
78289
 
@@ -78126,11 +78946,36 @@ description: MUST use when writing Bun Native scripts.
78126
78946
 
78127
78947
  ## CLI Commands
78128
78948
 
78129
- Place scripts in a folder. After writing, tell the user they can run:
78130
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
78131
- - \`wmill sync push\` - Deploy to Windmill
78949
+ Place scripts in a folder.
78950
+
78951
+ After writing, tell the user which command fits what they want to do:
78952
+
78953
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
78954
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
78955
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
78956
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
78957
+
78958
+ ### Preview vs run — choose by intent, not habit
78959
+
78960
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
78961
+
78962
+ Only use \`script run\` when:
78963
+ - The user explicitly says "run the deployed version" / "run what's on the server".
78964
+ - There is no local script being edited (you're just invoking an existing script).
78965
+
78966
+ Only use \`sync push\` when:
78967
+ - The user explicitly asks to deploy, publish, push, or ship.
78968
+ - The preview has already validated the change and the user wants it in the workspace.
78969
+
78970
+ ### After writing — offer to test, don't wait passively
78971
+
78972
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
78973
+
78974
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
78132
78975
 
78133
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
78976
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
78977
+
78978
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
78134
78979
 
78135
78980
  Use \`wmill resource-type list --schema\` to discover available resource types.
78136
78981
 
@@ -78791,11 +79636,36 @@ description: MUST use when writing C# scripts.
78791
79636
 
78792
79637
  ## CLI Commands
78793
79638
 
78794
- Place scripts in a folder. After writing, tell the user they can run:
78795
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
78796
- - \`wmill sync push\` - Deploy to Windmill
79639
+ Place scripts in a folder.
79640
+
79641
+ After writing, tell the user which command fits what they want to do:
79642
+
79643
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
79644
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
79645
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
79646
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
79647
+
79648
+ ### Preview vs run — choose by intent, not habit
79649
+
79650
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
79651
+
79652
+ Only use \`script run\` when:
79653
+ - The user explicitly says "run the deployed version" / "run what's on the server".
79654
+ - There is no local script being edited (you're just invoking an existing script).
79655
+
79656
+ Only use \`sync push\` when:
79657
+ - The user explicitly asks to deploy, publish, push, or ship.
79658
+ - The preview has already validated the change and the user wants it in the workspace.
79659
+
79660
+ ### After writing — offer to test, don't wait passively
79661
+
79662
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
79663
+
79664
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
79665
+
79666
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
78797
79667
 
78798
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
79668
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
78799
79669
 
78800
79670
  Use \`wmill resource-type list --schema\` to discover available resource types.
78801
79671
 
@@ -78848,11 +79718,36 @@ description: MUST use when writing Deno/TypeScript scripts.
78848
79718
 
78849
79719
  ## CLI Commands
78850
79720
 
78851
- Place scripts in a folder. After writing, tell the user they can run:
78852
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
78853
- - \`wmill sync push\` - Deploy to Windmill
79721
+ Place scripts in a folder.
78854
79722
 
78855
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
79723
+ After writing, tell the user which command fits what they want to do:
79724
+
79725
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
79726
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
79727
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
79728
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
79729
+
79730
+ ### Preview vs run — choose by intent, not habit
79731
+
79732
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
79733
+
79734
+ Only use \`script run\` when:
79735
+ - The user explicitly says "run the deployed version" / "run what's on the server".
79736
+ - There is no local script being edited (you're just invoking an existing script).
79737
+
79738
+ Only use \`sync push\` when:
79739
+ - The user explicitly asks to deploy, publish, push, or ship.
79740
+ - The preview has already validated the change and the user wants it in the workspace.
79741
+
79742
+ ### After writing — offer to test, don't wait passively
79743
+
79744
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
79745
+
79746
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
79747
+
79748
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
79749
+
79750
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
78856
79751
 
78857
79752
  Use \`wmill resource-type list --schema\` to discover available resource types.
78858
79753
 
@@ -79519,11 +80414,36 @@ description: MUST use when writing DuckDB queries.
79519
80414
 
79520
80415
  ## CLI Commands
79521
80416
 
79522
- Place scripts in a folder. After writing, tell the user they can run:
79523
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
79524
- - \`wmill sync push\` - Deploy to Windmill
80417
+ Place scripts in a folder.
80418
+
80419
+ After writing, tell the user which command fits what they want to do:
80420
+
80421
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
80422
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
80423
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
80424
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80425
+
80426
+ ### Preview vs run — choose by intent, not habit
80427
+
80428
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
80429
+
80430
+ Only use \`script run\` when:
80431
+ - The user explicitly says "run the deployed version" / "run what's on the server".
80432
+ - There is no local script being edited (you're just invoking an existing script).
80433
+
80434
+ Only use \`sync push\` when:
80435
+ - The user explicitly asks to deploy, publish, push, or ship.
80436
+ - The preview has already validated the change and the user wants it in the workspace.
80437
+
80438
+ ### After writing — offer to test, don't wait passively
79525
80439
 
79526
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
80440
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80441
+
80442
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
80443
+
80444
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
80445
+
80446
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
79527
80447
 
79528
80448
  Use \`wmill resource-type list --schema\` to discover available resource types.
79529
80449
 
@@ -79586,11 +80506,36 @@ description: MUST use when writing Go scripts.
79586
80506
 
79587
80507
  ## CLI Commands
79588
80508
 
79589
- Place scripts in a folder. After writing, tell the user they can run:
79590
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
79591
- - \`wmill sync push\` - Deploy to Windmill
80509
+ Place scripts in a folder.
80510
+
80511
+ After writing, tell the user which command fits what they want to do:
80512
+
80513
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
80514
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
80515
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
80516
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80517
+
80518
+ ### Preview vs run — choose by intent, not habit
80519
+
80520
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
80521
+
80522
+ Only use \`script run\` when:
80523
+ - The user explicitly says "run the deployed version" / "run what's on the server".
80524
+ - There is no local script being edited (you're just invoking an existing script).
79592
80525
 
79593
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
80526
+ Only use \`sync push\` when:
80527
+ - The user explicitly asks to deploy, publish, push, or ship.
80528
+ - The preview has already validated the change and the user wants it in the workspace.
80529
+
80530
+ ### After writing — offer to test, don't wait passively
80531
+
80532
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80533
+
80534
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
80535
+
80536
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
80537
+
80538
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
79594
80539
 
79595
80540
  Use \`wmill resource-type list --schema\` to discover available resource types.
79596
80541
 
@@ -79660,11 +80605,36 @@ description: MUST use when writing GraphQL queries.
79660
80605
 
79661
80606
  ## CLI Commands
79662
80607
 
79663
- Place scripts in a folder. After writing, tell the user they can run:
79664
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
79665
- - \`wmill sync push\` - Deploy to Windmill
80608
+ Place scripts in a folder.
80609
+
80610
+ After writing, tell the user which command fits what they want to do:
80611
+
80612
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
80613
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
80614
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
80615
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80616
+
80617
+ ### Preview vs run — choose by intent, not habit
79666
80618
 
79667
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
80619
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
80620
+
80621
+ Only use \`script run\` when:
80622
+ - The user explicitly says "run the deployed version" / "run what's on the server".
80623
+ - There is no local script being edited (you're just invoking an existing script).
80624
+
80625
+ Only use \`sync push\` when:
80626
+ - The user explicitly asks to deploy, publish, push, or ship.
80627
+ - The preview has already validated the change and the user wants it in the workspace.
80628
+
80629
+ ### After writing — offer to test, don't wait passively
80630
+
80631
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80632
+
80633
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
80634
+
80635
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
80636
+
80637
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
79668
80638
 
79669
80639
  Use \`wmill resource-type list --schema\` to discover available resource types.
79670
80640
 
@@ -79721,11 +80691,36 @@ description: MUST use when writing Java scripts.
79721
80691
 
79722
80692
  ## CLI Commands
79723
80693
 
79724
- Place scripts in a folder. After writing, tell the user they can run:
79725
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
79726
- - \`wmill sync push\` - Deploy to Windmill
80694
+ Place scripts in a folder.
80695
+
80696
+ After writing, tell the user which command fits what they want to do:
80697
+
80698
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
80699
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
80700
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
80701
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80702
+
80703
+ ### Preview vs run — choose by intent, not habit
80704
+
80705
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
80706
+
80707
+ Only use \`script run\` when:
80708
+ - The user explicitly says "run the deployed version" / "run what's on the server".
80709
+ - There is no local script being edited (you're just invoking an existing script).
80710
+
80711
+ Only use \`sync push\` when:
80712
+ - The user explicitly asks to deploy, publish, push, or ship.
80713
+ - The preview has already validated the change and the user wants it in the workspace.
80714
+
80715
+ ### After writing — offer to test, don't wait passively
80716
+
80717
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80718
+
80719
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
79727
80720
 
79728
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
80721
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
80722
+
80723
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
79729
80724
 
79730
80725
  Use \`wmill resource-type list --schema\` to discover available resource types.
79731
80726
 
@@ -79775,11 +80770,36 @@ description: MUST use when writing MS SQL Server queries.
79775
80770
 
79776
80771
  ## CLI Commands
79777
80772
 
79778
- Place scripts in a folder. After writing, tell the user they can run:
79779
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
79780
- - \`wmill sync push\` - Deploy to Windmill
80773
+ Place scripts in a folder.
80774
+
80775
+ After writing, tell the user which command fits what they want to do:
80776
+
80777
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
80778
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
80779
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
80780
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80781
+
80782
+ ### Preview vs run — choose by intent, not habit
80783
+
80784
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
80785
+
80786
+ Only use \`script run\` when:
80787
+ - The user explicitly says "run the deployed version" / "run what's on the server".
80788
+ - There is no local script being edited (you're just invoking an existing script).
80789
+
80790
+ Only use \`sync push\` when:
80791
+ - The user explicitly asks to deploy, publish, push, or ship.
80792
+ - The preview has already validated the change and the user wants it in the workspace.
80793
+
80794
+ ### After writing — offer to test, don't wait passively
79781
80795
 
79782
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
80796
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80797
+
80798
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
80799
+
80800
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
80801
+
80802
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
79783
80803
 
79784
80804
  Use \`wmill resource-type list --schema\` to discover available resource types.
79785
80805
 
@@ -79802,11 +80822,36 @@ description: MUST use when writing MySQL queries.
79802
80822
 
79803
80823
  ## CLI Commands
79804
80824
 
79805
- Place scripts in a folder. After writing, tell the user they can run:
79806
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
79807
- - \`wmill sync push\` - Deploy to Windmill
80825
+ Place scripts in a folder.
80826
+
80827
+ After writing, tell the user which command fits what they want to do:
80828
+
80829
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
80830
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
80831
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
80832
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80833
+
80834
+ ### Preview vs run — choose by intent, not habit
80835
+
80836
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
80837
+
80838
+ Only use \`script run\` when:
80839
+ - The user explicitly says "run the deployed version" / "run what's on the server".
80840
+ - There is no local script being edited (you're just invoking an existing script).
79808
80841
 
79809
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
80842
+ Only use \`sync push\` when:
80843
+ - The user explicitly asks to deploy, publish, push, or ship.
80844
+ - The preview has already validated the change and the user wants it in the workspace.
80845
+
80846
+ ### After writing — offer to test, don't wait passively
80847
+
80848
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80849
+
80850
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
80851
+
80852
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
80853
+
80854
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
79810
80855
 
79811
80856
  Use \`wmill resource-type list --schema\` to discover available resource types.
79812
80857
 
@@ -79829,11 +80874,36 @@ description: MUST use when writing Native TypeScript scripts.
79829
80874
 
79830
80875
  ## CLI Commands
79831
80876
 
79832
- Place scripts in a folder. After writing, tell the user they can run:
79833
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
79834
- - \`wmill sync push\` - Deploy to Windmill
80877
+ Place scripts in a folder.
80878
+
80879
+ After writing, tell the user which command fits what they want to do:
80880
+
80881
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
80882
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
80883
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
80884
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80885
+
80886
+ ### Preview vs run — choose by intent, not habit
80887
+
80888
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
79835
80889
 
79836
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
80890
+ Only use \`script run\` when:
80891
+ - The user explicitly says "run the deployed version" / "run what's on the server".
80892
+ - There is no local script being edited (you're just invoking an existing script).
80893
+
80894
+ Only use \`sync push\` when:
80895
+ - The user explicitly asks to deploy, publish, push, or ship.
80896
+ - The preview has already validated the change and the user wants it in the workspace.
80897
+
80898
+ ### After writing — offer to test, don't wait passively
80899
+
80900
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80901
+
80902
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
80903
+
80904
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
80905
+
80906
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
79837
80907
 
79838
80908
  Use \`wmill resource-type list --schema\` to discover available resource types.
79839
80909
 
@@ -80461,11 +81531,36 @@ description: MUST use when writing PHP scripts.
80461
81531
 
80462
81532
  ## CLI Commands
80463
81533
 
80464
- Place scripts in a folder. After writing, tell the user they can run:
80465
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
80466
- - \`wmill sync push\` - Deploy to Windmill
81534
+ Place scripts in a folder.
81535
+
81536
+ After writing, tell the user which command fits what they want to do:
81537
+
81538
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
81539
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
81540
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
81541
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
80467
81542
 
80468
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
81543
+ ### Preview vs run choose by intent, not habit
81544
+
81545
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
81546
+
81547
+ Only use \`script run\` when:
81548
+ - The user explicitly says "run the deployed version" / "run what's on the server".
81549
+ - There is no local script being edited (you're just invoking an existing script).
81550
+
81551
+ Only use \`sync push\` when:
81552
+ - The user explicitly asks to deploy, publish, push, or ship.
81553
+ - The preview has already validated the change and the user wants it in the workspace.
81554
+
81555
+ ### After writing — offer to test, don't wait passively
81556
+
81557
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
81558
+
81559
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
81560
+
81561
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
81562
+
81563
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
80469
81564
 
80470
81565
  Use \`wmill resource-type list --schema\` to discover available resource types.
80471
81566
 
@@ -80534,11 +81629,36 @@ description: MUST use when writing PostgreSQL queries.
80534
81629
 
80535
81630
  ## CLI Commands
80536
81631
 
80537
- Place scripts in a folder. After writing, tell the user they can run:
80538
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
80539
- - \`wmill sync push\` - Deploy to Windmill
81632
+ Place scripts in a folder.
81633
+
81634
+ After writing, tell the user which command fits what they want to do:
81635
+
81636
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
81637
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
81638
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
81639
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
81640
+
81641
+ ### Preview vs run — choose by intent, not habit
81642
+
81643
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
81644
+
81645
+ Only use \`script run\` when:
81646
+ - The user explicitly says "run the deployed version" / "run what's on the server".
81647
+ - There is no local script being edited (you're just invoking an existing script).
81648
+
81649
+ Only use \`sync push\` when:
81650
+ - The user explicitly asks to deploy, publish, push, or ship.
81651
+ - The preview has already validated the change and the user wants it in the workspace.
81652
+
81653
+ ### After writing — offer to test, don't wait passively
81654
+
81655
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
80540
81656
 
80541
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
81657
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
81658
+
81659
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
81660
+
81661
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
80542
81662
 
80543
81663
  Use \`wmill resource-type list --schema\` to discover available resource types.
80544
81664
 
@@ -80561,11 +81681,36 @@ description: MUST use when writing PowerShell scripts.
80561
81681
 
80562
81682
  ## CLI Commands
80563
81683
 
80564
- Place scripts in a folder. After writing, tell the user they can run:
80565
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
80566
- - \`wmill sync push\` - Deploy to Windmill
81684
+ Place scripts in a folder.
81685
+
81686
+ After writing, tell the user which command fits what they want to do:
81687
+
81688
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
81689
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
81690
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
81691
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
81692
+
81693
+ ### Preview vs run — choose by intent, not habit
81694
+
81695
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
81696
+
81697
+ Only use \`script run\` when:
81698
+ - The user explicitly says "run the deployed version" / "run what's on the server".
81699
+ - There is no local script being edited (you're just invoking an existing script).
81700
+
81701
+ Only use \`sync push\` when:
81702
+ - The user explicitly asks to deploy, publish, push, or ship.
81703
+ - The preview has already validated the change and the user wants it in the workspace.
80567
81704
 
80568
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
81705
+ ### After writing offer to test, don't wait passively
81706
+
81707
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
81708
+
81709
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
81710
+
81711
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
81712
+
81713
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
80569
81714
 
80570
81715
  Use \`wmill resource-type list --schema\` to discover available resource types.
80571
81716
 
@@ -80632,11 +81777,36 @@ description: MUST use when writing Python scripts.
80632
81777
 
80633
81778
  ## CLI Commands
80634
81779
 
80635
- Place scripts in a folder. After writing, tell the user they can run:
80636
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
80637
- - \`wmill sync push\` - Deploy to Windmill
81780
+ Place scripts in a folder.
81781
+
81782
+ After writing, tell the user which command fits what they want to do:
81783
+
81784
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
81785
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
81786
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
81787
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
81788
+
81789
+ ### Preview vs run — choose by intent, not habit
81790
+
81791
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
80638
81792
 
80639
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
81793
+ Only use \`script run\` when:
81794
+ - The user explicitly says "run the deployed version" / "run what's on the server".
81795
+ - There is no local script being edited (you're just invoking an existing script).
81796
+
81797
+ Only use \`sync push\` when:
81798
+ - The user explicitly asks to deploy, publish, push, or ship.
81799
+ - The preview has already validated the change and the user wants it in the workspace.
81800
+
81801
+ ### After writing — offer to test, don't wait passively
81802
+
81803
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
81804
+
81805
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
81806
+
81807
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
81808
+
81809
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
80640
81810
 
80641
81811
  Use \`wmill resource-type list --schema\` to discover available resource types.
80642
81812
 
@@ -81458,11 +82628,36 @@ description: MUST use when writing R scripts.
81458
82628
 
81459
82629
  ## CLI Commands
81460
82630
 
81461
- Place scripts in a folder. After writing, tell the user they can run:
81462
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
81463
- - \`wmill sync push\` - Deploy to Windmill
82631
+ Place scripts in a folder.
82632
+
82633
+ After writing, tell the user which command fits what they want to do:
82634
+
82635
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
82636
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
82637
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
82638
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
82639
+
82640
+ ### Preview vs run — choose by intent, not habit
82641
+
82642
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
82643
+
82644
+ Only use \`script run\` when:
82645
+ - The user explicitly says "run the deployed version" / "run what's on the server".
82646
+ - There is no local script being edited (you're just invoking an existing script).
82647
+
82648
+ Only use \`sync push\` when:
82649
+ - The user explicitly asks to deploy, publish, push, or ship.
82650
+ - The preview has already validated the change and the user wants it in the workspace.
82651
+
82652
+ ### After writing — offer to test, don't wait passively
82653
+
82654
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
82655
+
82656
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
82657
+
82658
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
81464
82659
 
81465
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
82660
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
81466
82661
 
81467
82662
  Use \`wmill resource-type list --schema\` to discover available resource types.
81468
82663
 
@@ -81559,11 +82754,36 @@ description: MUST use when writing Rust scripts.
81559
82754
 
81560
82755
  ## CLI Commands
81561
82756
 
81562
- Place scripts in a folder. After writing, tell the user they can run:
81563
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
81564
- - \`wmill sync push\` - Deploy to Windmill
82757
+ Place scripts in a folder.
81565
82758
 
81566
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
82759
+ After writing, tell the user which command fits what they want to do:
82760
+
82761
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
82762
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
82763
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
82764
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
82765
+
82766
+ ### Preview vs run — choose by intent, not habit
82767
+
82768
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
82769
+
82770
+ Only use \`script run\` when:
82771
+ - The user explicitly says "run the deployed version" / "run what's on the server".
82772
+ - There is no local script being edited (you're just invoking an existing script).
82773
+
82774
+ Only use \`sync push\` when:
82775
+ - The user explicitly asks to deploy, publish, push, or ship.
82776
+ - The preview has already validated the change and the user wants it in the workspace.
82777
+
82778
+ ### After writing — offer to test, don't wait passively
82779
+
82780
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
82781
+
82782
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
82783
+
82784
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
82785
+
82786
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
81567
82787
 
81568
82788
  Use \`wmill resource-type list --schema\` to discover available resource types.
81569
82789
 
@@ -81650,11 +82870,36 @@ description: MUST use when writing Snowflake queries.
81650
82870
 
81651
82871
  ## CLI Commands
81652
82872
 
81653
- Place scripts in a folder. After writing, tell the user they can run:
81654
- - \`wmill generate-metadata\` - Generate .script.yaml and .lock files
81655
- - \`wmill sync push\` - Deploy to Windmill
82873
+ Place scripts in a folder.
82874
+
82875
+ After writing, tell the user which command fits what they want to do:
82876
+
82877
+ - \`wmill script preview <script_path>\` — **default when iterating on a local script.** Runs the local file without deploying.
82878
+ - \`wmill script run <path>\` — runs the script **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
82879
+ - \`wmill generate-metadata\` — generate \`.script.yaml\` and \`.lock\` files for the script you modified.
82880
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
82881
+
82882
+ ### Preview vs run — choose by intent, not habit
82883
+
82884
+ If the user says "run the script", "try it", "test it", "does it work" while there are **local edits to the script file**, use \`script preview\`. Do NOT push the script to then \`script run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
82885
+
82886
+ Only use \`script run\` when:
82887
+ - The user explicitly says "run the deployed version" / "run what's on the server".
82888
+ - There is no local script being edited (you're just invoking an existing script).
82889
+
82890
+ Only use \`sync push\` when:
82891
+ - The user explicitly asks to deploy, publish, push, or ship.
82892
+ - The preview has already validated the change and the user wants it in the workspace.
82893
+
82894
+ ### After writing — offer to test, don't wait passively
81656
82895
 
81657
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
82896
+ If the user hasn't already told you to run/test/preview the script, offer it as a one-sentence next step (e.g. "Want me to run \`wmill script preview\` with sample args?"). Do not present a multi-option menu.
82897
+
82898
+ If the user already asked to test/run/try the script in their original request, skip the offer and just execute \`wmill script preview <path> -d '<args>'\` directly — pick plausible args from the script's declared parameters. The shape varies by language: \`main(...)\` for code languages, the SQL dialect's own placeholder syntax (\`$1\` for PostgreSQL, \`?\` for MySQL/Snowflake, \`@P1\` for MSSQL, \`@name\` for BigQuery, etc.), positional \`$1\`, \`$2\`, … for Bash, \`param(...)\` for PowerShell.
82899
+
82900
+ \`wmill script preview\` does not deploy, but it still executes script code and may cause side effects; run it yourself when the user asked to test/preview (or after confirming that execution is intended). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
82901
+
82902
+ For a **visual** open-the-script-in-the-dev-page preview (rather than \`script preview\`'s run-and-print-result), use the \`preview\` skill.
81658
82903
 
81659
82904
  Use \`wmill resource-type list --schema\` to discover available resource types.
81660
82905
 
@@ -81677,15 +82922,77 @@ description: MUST use when creating flows.
81677
82922
 
81678
82923
  # Windmill Flow Building Guide
81679
82924
 
81680
- ## CLI Commands
82925
+ ## Creating a Flow
82926
+
82927
+ **You — the AI agent — scaffold the flow yourself by running \`wmill flow new <path>\` with the right flags. Do NOT hand-create the folder + \`flow.yaml\`, and do NOT tell the user to "run \`wmill flow new\` and follow the prompts".**
82928
+
82929
+ \`wmill flow new\` creates the folder with the correct suffix (\`{{FLOW_SUFFIX}}\` or \`.flow\` depending on the workspace's \`nonDottedPaths\` setting), writes a minimal \`flow.yaml\` shell, and prints Claude-specific next-step hints. Scaffolding by hand skips all of that and often picks the wrong suffix.
82930
+
82931
+ ### Step 1 — Gather path + summary by asking the user
82932
+
82933
+ You need two things:
82934
+
82935
+ 1. **path** — the windmill path, e.g. \`f/folder/my_flow\` or \`u/username/my_flow\`.
82936
+ 2. **summary** — a short description of the flow.
82937
+
82938
+ If the user's request didn't supply both, ask for both in a single round-trip. Use whichever interactive question facility your runtime provides — a structured multi-choice tool if available, otherwise plain chat — and provide one or two example values for each (with an "Other" / free-form fallback). Do not guess paths or summaries.
82939
+
82940
+ ### Step 2 — Run the command yourself
82941
+
82942
+ \`\`\`bash
82943
+ wmill flow new f/folder/my_flow --summary "Short description"
82944
+ \`\`\`
82945
+
82946
+ Add \`--description "..."\` when the user provided a longer explanation worth preserving separately from the summary.
82947
+
82948
+ ### Step 3 — Fill in \`flow.yaml\`
82949
+
82950
+ Open the generated \`flow.yaml\` (under the folder the command just created) and replace the empty \`value.modules\` + \`schema\` with the real flow definition.
81681
82951
 
81682
- Create a folder ending with \`{{FLOW_SUFFIX}}\` and add a \`flow.yaml\` file with the flow definition.
81683
82952
  For rawscript modules, use \`!inline path/to/script.ts\` for the content key. {{INLINE_SCRIPT_NAMING}}
81684
- After writing, tell the user they can run:
81685
- - \`wmill generate-metadata\` - Generate lock files for the flow you modified
81686
- - \`wmill sync push\` - Deploy to Windmill
81687
82953
 
81688
- Do NOT run these commands yourself. Instead, inform the user that they should run them.
82954
+ Once the flow has real content, **offer** to open the visual preview as a one-sentence next step (e.g. "Want me to open the visual preview?"). Don't auto-open opening the dev page has side effects (browser window, possibly a \`launch.json\` entry) and the user should consent.
82955
+
82956
+ ### Anti-patterns to avoid
82957
+
82958
+ - ❌ Hand-creating the \`{{FLOW_SUFFIX}}\` folder + \`flow.yaml\` instead of running \`wmill flow new\`. You'll miss the suffix-setting resolution, the default shape, and the Claude hints.
82959
+ - ❌ Telling the user to "run \`wmill flow new <path>\`" — you can and should run it yourself.
82960
+ - ❌ Inventing a path/summary instead of asking the user.
82961
+
82962
+ ## CLI Commands — running, previewing, deploying
82963
+
82964
+ After writing, tell the user which command fits what they want to do:
82965
+
82966
+ - \`wmill flow preview <flow_path>\` — **default when iterating on a local flow.** Runs the local \`flow.yaml\` against local inline scripts without deploying. Add \`--remote\` to use deployed workspace scripts for PathScript steps instead of local files.
82967
+ - \`wmill flow run <path>\` — runs the flow **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits.
82968
+ - \`wmill generate-metadata\` — regenerate stale \`.lock\` and \`.script.yaml\` files. By default it scans **scripts, flows, and apps** across the workspace; pass \`--skip-flows --skip-apps\` (or run from a subdirectory) to limit the scope when you only care about the flow you edited.
82969
+ - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test".
82970
+
82971
+ ### Preview vs run — choose by intent, not habit
82972
+
82973
+ If the user says "run the flow", "try it", "test it", "does it work" while there are **local edits to a \`flow.yaml\`**, use \`flow preview\`. Do NOT push the flow to then \`flow run\` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes.
82974
+
82975
+ Only use \`flow run\` when:
82976
+ - The user explicitly says "run the deployed version" / "run what's on the server".
82977
+ - There is no local \`flow.yaml\` being edited (you're just invoking an existing flow).
82978
+
82979
+ Only use \`sync push\` when:
82980
+ - The user explicitly asks to deploy, publish, push, or ship.
82981
+ - The preview has already validated the change and the user wants it in the workspace.
82982
+
82983
+ ### After writing — offer to run, don't wait passively
82984
+
82985
+ This is about **programmatic execution** (\`wmill flow preview -d '<args>'\`), which actually runs the flow and has side effects. Visual preview (the \`preview\` skill) is offered separately — see "Visual preview" below.
82986
+
82987
+ If the user hasn't already told you to run/test the flow, offer it as a one-sentence next step (e.g. "Want me to run \`wmill flow preview\` with sample args?"). Do not present a multi-option menu.
82988
+
82989
+ If the user already asked to test/run/try the flow in their original request, skip the offer and just execute \`wmill flow preview <path> -d '<args>'\` directly — pick plausible args from the flow's input schema.
82990
+
82991
+ \`wmill flow preview\` is safe to run yourself (it does not deploy). \`wmill sync push\` and \`wmill generate-metadata\` modify workspace state or local files — only run these when the user explicitly asks; otherwise tell them which to run.
82992
+
82993
+ ### Visual preview
82994
+
82995
+ To open the flow visually in the dev page (graph + live reload), use the \`preview\` skill. Always **offer** it as a one-sentence next step (e.g. "Want me to open the visual preview?") rather than opening it automatically — opening the dev page has side effects (browser window, possibly a \`launch.json\` entry under MCP-preview branches) the user should consent to. If the user already asked to see/preview/visualize the flow in their original request, skip the offer and just invoke the skill.
81689
82996
 
81690
82997
  ## OpenFlow Schema
81691
82998
 
@@ -81994,11 +83301,70 @@ Raw apps let you build custom frontends with React, Svelte, or Vue that connect
81994
83301
 
81995
83302
  ## Creating a Raw App
81996
83303
 
83304
+ **You — the AI agent — create the app yourself by running \`wmill app new\` with the right flags. Do NOT tell the user to "run \`wmill app new\` and follow the prompts" or wait for them to do it.** The bare \`wmill app new\` is an interactive wizard that hangs waiting for stdin in any non-TTY context (which includes you). Always pass flags.
83305
+
83306
+ ### Step 1 — Gather the three required values by asking the user
83307
+
83308
+ You need three things to run the command:
83309
+
83310
+ 1. **summary** — a short description of the app
83311
+ 2. **path** — the windmill path, e.g. \`f/folder/my_app\` or \`u/username/my_app\`
83312
+ 3. **framework** — one of \`react19\` (recommended), \`react18\`, \`svelte5\`, \`vue\`
83313
+
83314
+ If the user's request did not supply *every* one of these explicitly, ask. Do not guess values, do not invent paths, do not pick a framework on the user's behalf, do not "just use react19 because it's the default".
83315
+
83316
+ Use whichever interactive question facility your runtime provides — a structured multi-choice tool if available, otherwise plain chat — and group all missing fields into a single round-trip so the user answers them at once:
83317
+
83318
+ - For \`framework\` — multiple-choice with the four allowed values; mark \`react19\` as \`(Recommended)\` and put it first.
83319
+ - For \`summary\` and \`path\` — provide one or two example values as multiple-choice options (the user can pick "Other" to type a free-form answer).
83320
+
83321
+ Only proceed once you have concrete values for all three. If the user replies with something ambiguous, ask again rather than guessing.
83322
+
83323
+ ### Step 2 — Run the command yourself
83324
+
83325
+ Once you have summary + path + framework, run it:
83326
+
83327
+ \`\`\`bash
83328
+ wmill app new \\
83329
+ --summary "Customer dashboard" \\
83330
+ --path f/sales/dashboard \\
83331
+ --framework react19
83332
+ \`\`\`
83333
+
83334
+ That's the minimum. The datatable wizard and the "Open in Claude Desktop?" prompt are skipped silently because passing any of \`--summary\`/\`--path\`/\`--framework\` puts the command in non-interactive mode.
83335
+
83336
+ ### Optional flags
83337
+
83338
+ Layer these in only when the user asked for them:
83339
+
83340
+ | Flag | When to add it |
83341
+ |---|---|
83342
+ | \`--datatable <name>\` | The user wants this app wired to a specific Windmill datatable. Without it, the app is created with no datatable. |
83343
+ | \`--schema <name>\` | Together with \`--datatable\`. Creates the schema with \`CREATE SCHEMA IF NOT EXISTS\` if it doesn't already exist. |
83344
+ | \`--overwrite\` | The target directory already exists and the user said it's OK to replace. Without it, non-interactive mode aborts with an error so you don't clobber existing work. |
83345
+ | \`--no-open-in-desktop\` | Already implied in non-interactive mode; only needed if you're somehow running interactively. |
83346
+
83347
+ ### Step 3 — Offer the visual preview
83348
+
83349
+ After \`wmill app new\` and any initial edits to \`App.tsx\` / \`index.tsx\`, **offer** to open the visual preview as a one-sentence next step (e.g. "Want me to open the visual preview?"). Don't auto-open — opening the dev page has side effects (browser window, possibly a \`launch.json\` entry when an embedded preview tool is in play) the user should consent to.
83350
+
83351
+ For apps the preview command runs from the app folder (\`cd <app_path>__raw_app && wmill app dev …\`); the \`preview\` skill picks the proxy vs direct branch based on whether the runtime exposes a tool that can embed a localhost URL. If the user already asked to see/preview/visualize the app in their original request, skip the offer and just invoke the skill.
83352
+
83353
+ ### Anti-patterns to avoid
83354
+
83355
+ - ❌ Running \`wmill app new\` with no flags (the prompt will hang).
83356
+ - ❌ Telling the user to "run \`wmill app new\` and follow the prompts" — that's a step backwards from what you can do directly.
83357
+ - ❌ Inventing a path/summary/framework instead of asking the user.
83358
+ - ❌ Defaulting to \`react19\` because the user didn't say — even sensible defaults must be confirmed.
83359
+ - ❌ Passing \`--overwrite\` automatically when the directory exists — confirm with the user first.
83360
+
83361
+ ### Interactive (only when a human is at the terminal)
83362
+
81997
83363
  \`\`\`bash
81998
83364
  wmill app new
81999
83365
  \`\`\`
82000
83366
 
82001
- This interactive command creates a complete app structure with your choice of frontend framework (React, Svelte, or Vue).
83367
+ This is the wizard. It only works when run by a human in a real terminal. Don't call it this way from an agent.
82002
83368
 
82003
83369
  ## App Structure
82004
83370
 
@@ -82222,12 +83588,13 @@ data:
82222
83588
 
82223
83589
  ## CLI Commands
82224
83590
 
82225
- Tell the user they can run these commands (do NOT run them yourself):
83591
+ \`wmill app new\` is the exception: you run it yourself, with flags, per the "Creating a Raw App" section above.
83592
+
83593
+ For everything else, tell the user which command fits their intent and let them run it — these touch the workspace or local lock files, and the user should consent each time:
82226
83594
 
82227
83595
  | Command | Description |
82228
83596
  |---------|-------------|
82229
- | \`wmill app new\` | Create a new raw app interactively |
82230
- | \`wmill app dev\` | Start dev server with live reload |
83597
+ | \`wmill app dev\` | Start dev server with live reload (see the \`preview\` skill for the full open-the-app-in-the-IDE-pane procedure). |
82231
83598
  | \`wmill app generate-agents\` | Refresh AGENTS.md and DATATABLES.md |
82232
83599
  | \`wmill generate-metadata\` | Generate lock files for backend runnables |
82233
83600
  | \`wmill sync push\` | Deploy app to Windmill |
@@ -82616,6 +83983,13 @@ app related commands
82616
83983
  - \`app lint [app_folder:string]\` - Lint a raw app folder to validate structure and buildability
82617
83984
  - \`--fix\` - Attempt to fix common issues (not implemented yet)
82618
83985
  - \`app new\` - create a new raw app from a template
83986
+ - \`--summary <summary:string>\` - App summary (short description). Skips the prompt when provided. Triggers non-interactive mode.
83987
+ - \`--path <path:string>\` - App path (e.g., f/folder/my_app or u/username/my_app). Skips the prompt when provided. Triggers non-interactive mode.
83988
+ - \`--framework <framework:string>\` - Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode.
83989
+ - \`--datatable <datatable:string>\` - Datatable to wire up. Without this flag in non-interactive mode, no datatable is configured.
83990
+ - \`--schema <schema:string>\` - Schema to use with --datatable. Created (CREATE SCHEMA IF NOT EXISTS) if it doesn't already exist.
83991
+ - \`--overwrite\` - Overwrite the target directory if it already exists, without prompting.
83992
+ - \`--no-open-in-desktop\` - Do not prompt to open the new app in Claude Desktop.
82619
83993
  - \`app generate-agents [app_folder:string]\` - regenerate AGENTS.md and DATATABLES.md from remote workspace
82620
83994
  - \`app set-permissioned-as <path:string> <email:string>\` - Set the on_behalf_of_email for an app (requires admin or wm_deployers group)
82621
83995
 
@@ -82652,10 +84026,13 @@ workspace dependencies related commands
82652
84026
 
82653
84027
  ### dev
82654
84028
 
82655
- Launch a dev server that watches for local file changes and auto-pushes them to the remote workspace. Provides live reload for scripts and flows during development.
84029
+ Watch local file changes and live-reload the dev page for preview. Does NOT deploy to the remote workspace use wmill sync push for that.
82656
84030
 
82657
84031
  **Options:**
82658
- - \`--includes <pattern...:string>\` - Filter paths givena glob pattern or path
84032
+ - \`--includes <pattern...:string>\` - Filter paths given a glob pattern or path
84033
+ - \`--proxy-port <port:number>\` - Port for a localhost reverse proxy to the remote Windmill server
84034
+ - \`--path <path:string>\` - Watch a specific windmill path (e.g., u/admin/my_script or f/my_flow)
84035
+ - \`--no-open\` - Do not open the browser automatically
82659
84036
 
82660
84037
  ### docs
82661
84038
 
@@ -83201,6 +84578,133 @@ workspace related commands
83201
84578
  - \`--team-name <team_name:string>\` - Slack team name
83202
84579
  - \`workspace disconnect-slack\`
83203
84580
 
84581
+ `,
84582
+ preview: `---
84583
+ name: preview
84584
+ description: MUST use when opening the Windmill dev page / visual preview of a flow, script, or app. Triggers on words like preview, open, navigate to, visualize, see the flow/app/script, and after writing a flow/script/app for visual verification.
84585
+ ---
84586
+
84587
+ # Windmill Preview Workflow
84588
+
84589
+ Use this skill any time the user wants to **see**, **open**, **navigate to**, **visualize**, or **preview** a flow, script, or app — and any time you've just finished writing one and want to offer visual verification.
84590
+
84591
+ The Windmill dev page renders the flow graph / script editor, lets the user step through steps, and live-reloads on every save. It runs locally via \`wmill dev\` and is reached on a localhost port.
84592
+
84593
+ ## Two independent decisions
84594
+
84595
+ ### 1. Mode: proxy or direct?
84596
+
84597
+ \`wmill dev\` runs in two modes; pick by asking what kind of URL whatever will display the preview needs.
84598
+
84599
+ - **Proxy** (\`--proxy-port <port>\`) — exposes the dev page on \`http://localhost:<port>/\`. Use it when the embedder you'll hand the URL to **only accepts localhost URLs** (most in-IDE / in-chat preview embedders do, because they sandbox cross-origin loads).
84600
+ - **Direct** (default) — the user's browser loads the dev page from the remote workspace's HTTPS URL; the local \`wmill dev\` only runs the WebSocket back-channel for live reload. Use it when the URL will be opened in a regular browser tab.
84601
+
84602
+ Default to **direct** unless you have a specific embedder that needs localhost.
84603
+
84604
+ ### 2. Who starts the server?
84605
+
84606
+ - **You start it** in the background. Spawn \`wmill dev …\` (or \`wmill app dev …\`) yourself, capture the URL it prints, do whatever's next (open a tab, hand the URL to an embedder).
84607
+ - **The runtime starts it from \`.claude/launch.json\`.** Some runtimes (currently the Claude Desktop / Claude Code MCP preview integration — tools prefixed with \`mcp__Claude_Preview__\`) can read a \`launch.json\` configuration and launch the dev server on demand when you invoke their preview tool. **Only take this path if you actually have such a tool** — otherwise nothing reads the file and \`wmill dev\` never starts.
84608
+
84609
+ The two decisions compose. The common cases:
84610
+
84611
+ | Embedder | Needs localhost? | launch.json runtime? | What to do |
84612
+ |---|---|---|---|
84613
+ | Regular browser tab | No | n/a | Direct mode, you start it, give URL to user |
84614
+ | IDE / chat preview pane that takes any URL | No | No | Direct mode, you start it, point the embedder at the printed URL |
84615
+ | IDE / chat preview pane that only accepts localhost | Yes | No | Proxy mode, you start it, point the embedder at \`http://localhost:<port>/\` |
84616
+ | Claude Desktop / Code MCP preview | Yes | Yes | Proxy mode, write a \`launch.json\` entry, invoke the MCP tool |
84617
+
84618
+ Never start the proxy "just in case" — it adds the localhost hop for no benefit when no embedder needs it.
84619
+
84620
+ ## Starting the server yourself
84621
+
84622
+ Use this when no \`launch.json\`-aware runtime is available, regardless of mode.
84623
+
84624
+ For flows / scripts:
84625
+ \`\`\`bash
84626
+ # Direct mode — gives you the remote dev-page URL
84627
+ wmill dev --path <wmill_path> --no-open
84628
+
84629
+ # Proxy mode — gives you a localhost URL that 302s to the remote dev page
84630
+ wmill dev --proxy-port 4000 --path <wmill_path> --no-open
84631
+ \`\`\`
84632
+
84633
+ For apps:
84634
+ \`\`\`bash
84635
+ cd <app_path>__raw_app && wmill app dev --no-open --port 4000
84636
+ \`\`\`
84637
+
84638
+ Each command prints the URL on stdout. Line shapes differ:
84639
+
84640
+ - \`wmill dev --no-open\` (direct) prints \`Go to <url>\` with the full remote URL (workspace, token, path baked in).
84641
+ - \`wmill dev --proxy-port\` prints \`Dev proxy listening on http://localhost:<port>\` — the URL to hand to an embedder is \`http://localhost:<port>/\`.
84642
+ - \`wmill app dev --no-open\` prints \`\uD83D\uDE80 Dev server running at <url>\` — the local app server.
84643
+
84644
+ Capture the URL with a loose match (the first \`https?://…\` token after startup) and either hand it to your embedder or relay it to the user: *"Preview is running — open \`<url>\` in your browser."* Don't construct the URL yourself; you don't have the workspace ID or auth token.
84645
+
84646
+ These commands are long-running — start them in the background, don't block waiting.
84647
+
84648
+ ## Letting \`launch.json\` start the server (Claude Desktop / Code MCP only)
84649
+
84650
+ Take this path when **and only when** an \`mcp__Claude_Preview__*\` MCP tool is exposed in your tool list. Skip it otherwise — without an MCP tool reading the file, \`wmill dev\` never starts.
84651
+
84652
+ **Each flow / script / app gets its own named entry** in the user's \`.claude/launch.json\` so multiple previews coexist without colliding — each entry pins a different port + path. Never reuse a generic "windmill" entry for different targets.
84653
+
84654
+ ### Step 1 — Reuse or add a per-target entry in \`.claude/launch.json\`
84655
+
84656
+ Convention: name the entry \`windmill: <wmill_path>\` (e.g. \`windmill: f/test/my_flow\`).
84657
+
84658
+ - **Entry already exists** → reuse it; note its \`port\` for the next step.
84659
+ - **Not there** → add one. Pick a port not already taken by another entry (start at 4000 and bump). Shape:
84660
+
84661
+ For flows / scripts:
84662
+ \`\`\`json
84663
+ {
84664
+ "name": "windmill: f/test/my_flow",
84665
+ "runtimeExecutable": "bash",
84666
+ "runtimeArgs": ["-c", "wmill dev --proxy-port \${PORT:-4000} --path f/test/my_flow --no-open"],
84667
+ "port": 4000,
84668
+ "autoPort": true
84669
+ }
84670
+ \`\`\`
84671
+
84672
+ For apps (\`*__raw_app/\`), \`wmill app dev\` is the equivalent — runs from the app folder, no \`--path\`:
84673
+ \`\`\`json
84674
+ {
84675
+ "name": "windmill: f/test/my_app",
84676
+ "runtimeExecutable": "bash",
84677
+ "runtimeArgs": ["-c", "cd f/test/my_app__raw_app && wmill app dev --no-open --port \${PORT:-4001}"],
84678
+ "port": 4001,
84679
+ "autoPort": true
84680
+ }
84681
+ \`\`\`
84682
+
84683
+ If \`.claude/launch.json\` doesn't exist yet, create it with the standard shell \`{ "version": "0.0.1", "configurations": [...] }\`.
84684
+
84685
+ ### Step 2 — Invoke the MCP preview tool
84686
+
84687
+ Point it at the entry you just added/found. Use \`http://localhost:<port>/\` as the URL — the proxy's redirect at \`/\` is what appends the workspace ID, the auth token, and the path. Do **NOT** construct a \`/dev?...\` URL yourself.
84688
+
84689
+ The MCP tool launches the configuration on demand, so you don't need to start the \`wmill dev\` process manually.
84690
+
84691
+ ## Non-visual alternative
84692
+
84693
+ If the user wants a programmatic test rather than a visual one:
84694
+ - Flow: \`wmill flow preview <path> -d '<args>'\`
84695
+ - Script: \`wmill script preview <path> -d '<args>'\`
84696
+
84697
+ Both print the job result, are safe to run yourself, and don't deploy.
84698
+
84699
+ ## Anti-patterns to avoid
84700
+
84701
+ - ❌ Writing a \`.claude/launch.json\` entry when no \`mcp__Claude_Preview__*\` tool is in your tool list. Nothing will read the file; the server never starts. Spawn \`wmill dev\` yourself instead.
84702
+ - ❌ Starting the proxy when no embedder needs a localhost URL. Direct mode is the right choice — the proxy is overhead with no purpose.
84703
+ - ❌ Reusing a single generic \`launch.json\` entry for every preview target. Each flow/script/app gets its own named entry on its own port — that's how multiple sessions coexist without one preview clobbering another.
84704
+ - ❌ Mutating an existing entry's \`--path\` to retarget it. Add a new entry instead.
84705
+ - ❌ Constructing \`http://localhost:<port>/dev?path=<X>\` yourself. The proxy's \`/\` redirect is what appends the workspace ID and auth token; bypassing it gives a broken page. Always use \`http://localhost:<port>/\`.
84706
+ - ❌ Starting \`wmill dev\` in the foreground (you'll hang). Always background.
84707
+ - ❌ Listing both "open in IDE pane" and "open in browser" as a menu — pick one based on context.
83204
84708
  `
83205
84709
  };
83206
84710
  var SCHEMAS = {
@@ -84228,16 +85732,17 @@ var WMILL_INIT_AI_AGENTS_SOURCE_ENV = "WMILL_INIT_AI_AGENTS_SOURCE";
84228
85732
  var WMILL_INIT_AI_CLAUDE_SOURCE_ENV = "WMILL_INIT_AI_CLAUDE_SOURCE";
84229
85733
  var CLAUDE_MD_DEFAULT = `Instructions are in @AGENTS.md
84230
85734
  `;
85735
+ var SKILL_TARGET_ROOTS = [".claude", ".agents"];
84231
85736
  async function writeAiGuidanceFiles(options) {
84232
85737
  const nonDottedPaths = options.nonDottedPaths ?? true;
84233
85738
  const skillMetadata = options.skillsSourcePath ? await readSkillMetadataFromDirectory(options.skillsSourcePath) : getGeneratedSkillMetadata();
84234
85739
  const agentsWritten = await writeProjectGuidanceFile({
84235
- targetPath: join17(options.targetDir, "AGENTS.md"),
85740
+ targetPath: join18(options.targetDir, "AGENTS.md"),
84236
85741
  overwrite: options.overwriteProjectGuidance ?? false,
84237
85742
  content: options.agentsSourcePath != null ? await readTextFile(options.agentsSourcePath) : generateAgentsMdContent(buildSkillsReference(skillMetadata))
84238
85743
  });
84239
85744
  const claudeWritten = await writeProjectGuidanceFile({
84240
- targetPath: join17(options.targetDir, "CLAUDE.md"),
85745
+ targetPath: join18(options.targetDir, "CLAUDE.md"),
84241
85746
  overwrite: options.overwriteProjectGuidance ?? false,
84242
85747
  content: options.claudeSourcePath != null ? await readTextFile(options.claudeSourcePath) : CLAUDE_MD_DEFAULT
84243
85748
  });
@@ -84257,17 +85762,17 @@ function buildSkillsReference(skills) {
84257
85762
  `);
84258
85763
  }
84259
85764
  async function copySkillsFromSource(targetDir, skillsSourcePath) {
84260
- const skillsDir = await ensureSkillsDirectory(targetDir);
84261
- await copyDirectoryContents(skillsSourcePath, skillsDir);
84262
- return await readSkillMetadataFromDirectory(skillsDir);
85765
+ const skillsDirs = await ensureSkillsDirectories(targetDir);
85766
+ await Promise.all(skillsDirs.map((skillsDir) => copyDirectoryContents(skillsSourcePath, skillsDir)));
85767
+ return await readSkillMetadataFromDirectory(skillsDirs[0]);
84263
85768
  }
84264
85769
  async function writeGeneratedSkills(targetDir, nonDottedPaths) {
84265
- const skillsDir = await ensureSkillsDirectory(targetDir);
84266
- await Promise.all(SKILLS.map(async (skill) => {
84267
- const skillDir = join17(skillsDir, skill.name);
85770
+ const skillsDirs = await ensureSkillsDirectories(targetDir);
85771
+ await Promise.all(skillsDirs.flatMap((skillsDir) => SKILLS.map(async (skill) => {
85772
+ const skillDir = join18(skillsDir, skill.name);
84268
85773
  await mkdir13(skillDir, { recursive: true });
84269
- await writeFile19(join17(skillDir, "SKILL.md"), renderGeneratedSkillContent(skill.name, nonDottedPaths), "utf8");
84270
- }));
85774
+ await writeFile20(join18(skillDir, "SKILL.md"), renderGeneratedSkillContent(skill.name, nonDottedPaths), "utf8");
85775
+ })));
84271
85776
  return SKILLS.map((skill) => ({
84272
85777
  ...skill,
84273
85778
  directoryName: skill.name
@@ -84279,15 +85784,15 @@ function getGeneratedSkillMetadata() {
84279
85784
  directoryName: skill.name
84280
85785
  }));
84281
85786
  }
84282
- async function ensureSkillsDirectory(targetDir) {
84283
- const skillsDir = join17(targetDir, ".claude", "skills");
84284
- await mkdir13(skillsDir, { recursive: true });
84285
- return skillsDir;
85787
+ async function ensureSkillsDirectories(targetDir) {
85788
+ const skillsDirs = SKILL_TARGET_ROOTS.map((root) => join18(targetDir, root, "skills"));
85789
+ await Promise.all(skillsDirs.map((skillsDir) => mkdir13(skillsDir, { recursive: true })));
85790
+ return skillsDirs;
84286
85791
  }
84287
85792
  async function copyDirectoryContents(sourceDir, targetDir) {
84288
- const entries = await readdir10(sourceDir, { withFileTypes: true });
85793
+ const entries = await readdir11(sourceDir, { withFileTypes: true });
84289
85794
  await Promise.all(entries.map(async (entry) => {
84290
- await cp(join17(sourceDir, entry.name), join17(targetDir, entry.name), {
85795
+ await cp(join18(sourceDir, entry.name), join18(targetDir, entry.name), {
84291
85796
  recursive: true,
84292
85797
  force: true
84293
85798
  });
@@ -84324,14 +85829,14 @@ ${schemaDocs.join(`
84324
85829
  `)}`;
84325
85830
  }
84326
85831
  async function readSkillMetadataFromDirectory(skillsDir) {
84327
- const entries = await readdir10(skillsDir, { withFileTypes: true });
85832
+ const entries = await readdir11(skillsDir, { withFileTypes: true });
84328
85833
  const skills = [];
84329
85834
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
84330
85835
  if (!entry.isDirectory()) {
84331
85836
  continue;
84332
85837
  }
84333
- const skillPath = join17(skillsDir, entry.name, "SKILL.md");
84334
- if (!await stat17(skillPath).catch(() => null)) {
85838
+ const skillPath = join18(skillsDir, entry.name, "SKILL.md");
85839
+ if (!await stat18(skillPath).catch(() => null)) {
84335
85840
  continue;
84336
85841
  }
84337
85842
  const content = await readTextFile(skillPath);
@@ -84367,10 +85872,10 @@ function parseSkillMetadata(content, fallbackName) {
84367
85872
  return { name, description, directoryName: fallbackName };
84368
85873
  }
84369
85874
  async function writeProjectGuidanceFile(options) {
84370
- if (!options.overwrite && await stat17(options.targetPath).catch(() => null)) {
85875
+ if (!options.overwrite && await stat18(options.targetPath).catch(() => null)) {
84371
85876
  return false;
84372
85877
  }
84373
- await writeFile19(options.targetPath, options.content, "utf8");
85878
+ await writeFile20(options.targetPath, options.content, "utf8");
84374
85879
  return true;
84375
85880
  }
84376
85881
  function formatSchemaForMarkdown(schemaYaml, schemaName, filePattern) {
@@ -84854,7 +86359,7 @@ function formatConfigReferenceJson() {
84854
86359
  async function initAction(opts) {
84855
86360
  let didBindWorkspace = false;
84856
86361
  let boundProfile;
84857
- if (await stat18("wmill.yaml").catch(() => null)) {
86362
+ if (await stat19("wmill.yaml").catch(() => null)) {
84858
86363
  info("wmill.yaml already exists, skipping config generation");
84859
86364
  } else {
84860
86365
  const { isGitRepository: isGitRepository2, getCurrentGitBranch: getCurrentGitBranch2 } = await Promise.resolve().then(() => (init_git(), exports_git));
@@ -84880,13 +86385,16 @@ async function initAction(opts) {
84880
86385
  selectedProfile = profiles.length > 0 ? await getActiveWorkspace(opts) : undefined;
84881
86386
  } else {
84882
86387
  const activeProfile = await getActiveWorkspace(opts);
86388
+ const orderedProfiles = activeProfile ? [
86389
+ ...profiles.filter((p) => p.name === activeProfile.name),
86390
+ ...profiles.filter((p) => p.name !== activeProfile.name)
86391
+ ] : profiles;
84883
86392
  const selectedName = await Select.prompt({
84884
86393
  message: "Select workspace profile",
84885
- options: profiles.map((p) => ({
84886
- name: `${p.name} (${p.workspaceId} on ${p.remote})`,
86394
+ options: orderedProfiles.map((p) => ({
86395
+ name: `${p.name} (${p.workspaceId} on ${p.remote})${activeProfile?.name === p.name ? " — active" : ""}`,
84887
86396
  value: p.name
84888
- })),
84889
- default: activeProfile?.name
86397
+ }))
84890
86398
  });
84891
86399
  selectedProfile = profiles.find((p) => p.name === selectedName);
84892
86400
  }
@@ -84926,7 +86434,7 @@ async function initAction(opts) {
84926
86434
  boundProfile = activeWorkspace;
84927
86435
  }
84928
86436
  }
84929
- await writeFile20("wmill.yaml", generateCommentedTemplate(branchName, undefined, wsBindings), "utf-8");
86437
+ await writeFile21("wmill.yaml", generateCommentedTemplate(branchName, undefined, wsBindings), "utf-8");
84930
86438
  info(colors.green("wmill.yaml created with default settings"));
84931
86439
  if (wsBindings && wsBindings.length > 0) {
84932
86440
  didBindWorkspace = true;
@@ -84961,8 +86469,8 @@ async function initAction(opts) {
84961
86469
  });
84962
86470
  if (choice === "cancel") {
84963
86471
  try {
84964
- await rm4("wmill.yaml");
84965
- await rm4("wmill-lock.yaml");
86472
+ await rm5("wmill.yaml");
86473
+ await rm5("wmill-lock.yaml");
84966
86474
  } catch {}
84967
86475
  info("Init cancelled");
84968
86476
  process.exit(0);
@@ -85012,7 +86520,7 @@ async function initAction(opts) {
85012
86520
  if (guidanceResult.claudeWritten) {
85013
86521
  info(colors.green("Created CLAUDE.md"));
85014
86522
  }
85015
- info(colors.green(`Created .claude/skills/ with ${guidanceResult.skillCount} skills`));
86523
+ info(colors.green(`Created .claude/skills/ and .agents/skills/ with ${guidanceResult.skillCount} skills`));
85016
86524
  } catch (error2) {
85017
86525
  if (error2 instanceof Error) {
85018
86526
  warn(`Could not create guidance files: ${error2.message}`);
@@ -85047,7 +86555,7 @@ await __promiseAll([
85047
86555
  init_conf(),
85048
86556
  init_utils()
85049
86557
  ]);
85050
- import * as fs13 from "node:fs/promises";
86558
+ import * as fs14 from "node:fs/promises";
85051
86559
  async function pullJobs(opts, workspace) {
85052
86560
  opts = await mergeConfigWithConfigFile(opts);
85053
86561
  const ws = await resolveWorkspace({ ...opts, workspace });
@@ -85091,7 +86599,7 @@ Warning: Found ${workers.length} active worker(s) on the instance.`));
85091
86599
  page++;
85092
86600
  }
85093
86601
  const completedPath = opts.completedOutput || "completed_jobs.json";
85094
- await fs13.writeFile(completedPath, JSON.stringify(completedJobs, null, 2));
86602
+ await fs14.writeFile(completedPath, JSON.stringify(completedJobs, null, 2));
85095
86603
  info(colors.green(`Successfully pulled ${completedJobs.length} completed jobs to ${completedPath}`));
85096
86604
  let queuedJobs = [];
85097
86605
  page = 1;
@@ -85109,7 +86617,7 @@ Warning: Found ${workers.length} active worker(s) on the instance.`));
85109
86617
  page++;
85110
86618
  }
85111
86619
  const queuedPath = opts.queuedOutput || "queued_jobs.json";
85112
- await fs13.writeFile(queuedPath, JSON.stringify(queuedJobs, null, 2));
86620
+ await fs14.writeFile(queuedPath, JSON.stringify(queuedJobs, null, 2));
85113
86621
  info(colors.green(`Successfully pulled ${queuedJobs.length} queued jobs to ${queuedPath}`));
85114
86622
  const allJobs = [...queuedJobs, ...completedJobs];
85115
86623
  if (allJobs.length > 0) {
@@ -85984,8 +87492,8 @@ async function generateMetadata2(opts, folder) {
85984
87492
  info("");
85985
87493
  if (errors.length > 0) {
85986
87494
  info(`Done. Updated ${colors.bold(String(succeeded))}/${total} item(s). ${colors.red(String(errors.length) + " failed")}:`);
85987
- for (const { path: path20, error: error2 } of errors) {
85988
- error(` ${path20}: ${error2}`);
87495
+ for (const { path: path21, error: error2 } of errors) {
87496
+ error(` ${path21}: ${error2}`);
85989
87497
  }
85990
87498
  process.exitCode = 1;
85991
87499
  } else {
@@ -86072,8 +87580,8 @@ var docs_default = command34;
86072
87580
  init_mod3();
86073
87581
  init_log();
86074
87582
  init_colors2();
86075
- var import_yaml40 = __toESM(require_dist(), 1);
86076
- import { writeFile as writeFile22 } from "node:fs/promises";
87583
+ var import_yaml41 = __toESM(require_dist(), 1);
87584
+ import { writeFile as writeFile23 } from "node:fs/promises";
86077
87585
  await __promiseAll([
86078
87586
  init_conf(),
86079
87587
  init_yaml()
@@ -86127,7 +87635,7 @@ async function migrateAction() {
86127
87635
  } else {
86128
87636
  newConf.workspaces = workspaces;
86129
87637
  }
86130
- await writeFile22(wmillYamlPath, import_yaml40.stringify(newConf), "utf-8");
87638
+ await writeFile23(wmillYamlPath, import_yaml41.stringify(newConf), "utf-8");
86131
87639
  info(colors.green(`✅ Migrated '${legacyKey}' to 'workspaces' in ${wmillYamlPath}`));
86132
87640
  if (wsNames.length > 0) {
86133
87641
  info(` Workspace entries: ${wsNames.join(", ")}`);
@@ -86141,7 +87649,7 @@ var config_default = command35;
86141
87649
 
86142
87650
  // src/main.ts
86143
87651
  await init_context();
86144
- var VERSION = "1.691.0";
87652
+ var VERSION = "1.692.0";
86145
87653
  async function checkVersionSafe(cmd) {
86146
87654
  const mainCommand = cmd.getMainCommand();
86147
87655
  const upgradeCommand = mainCommand.getCommand("upgrade");