flavor-code 1.2.11 → 1.2.13

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 (129) hide show
  1. package/README.md +36 -35
  2. package/README.zh-CN.md +36 -35
  3. package/dist/{app-MXWPZBEU.js → app-Z3YWFIBB.js} +5 -3
  4. package/dist/astgraph/db.mjs +237 -0
  5. package/dist/astgraph/extract.mjs +239 -0
  6. package/dist/astgraph/flavor-plugin.json +25 -0
  7. package/dist/astgraph/grammars.mjs +68 -0
  8. package/dist/astgraph/index.js +301 -0
  9. package/dist/astgraph/indexer.mjs +175 -0
  10. package/dist/astgraph/query.mjs +165 -0
  11. package/dist/astgraph/resolve.mjs +93 -0
  12. package/dist/astgraph/vendor/tree-sitter-javascript.wasm +0 -0
  13. package/dist/astgraph/vendor/tree-sitter-tsx.wasm +0 -0
  14. package/dist/astgraph/vendor/tree-sitter-typescript.wasm +0 -0
  15. package/dist/astgraph/vendor/tree-sitter.js +3980 -0
  16. package/dist/astgraph/vendor/tree-sitter.wasm +0 -0
  17. package/dist/astgraph/vendor/zod/LICENSE +21 -0
  18. package/dist/astgraph/vendor/zod/index.js +4 -0
  19. package/dist/astgraph/vendor/zod/locales/index.js +1 -0
  20. package/dist/astgraph/vendor/zod/locales/package.json +7 -0
  21. package/dist/astgraph/vendor/zod/package.json +135 -0
  22. package/dist/astgraph/vendor/zod/v4/classic/checks.js +1 -0
  23. package/dist/astgraph/vendor/zod/v4/classic/coerce.js +17 -0
  24. package/dist/astgraph/vendor/zod/v4/classic/compat.js +31 -0
  25. package/dist/astgraph/vendor/zod/v4/classic/errors.js +48 -0
  26. package/dist/astgraph/vendor/zod/v4/classic/external.js +20 -0
  27. package/dist/astgraph/vendor/zod/v4/classic/from-json-schema.js +599 -0
  28. package/dist/astgraph/vendor/zod/v4/classic/index.js +4 -0
  29. package/dist/astgraph/vendor/zod/v4/classic/iso.js +30 -0
  30. package/dist/astgraph/vendor/zod/v4/classic/package.json +7 -0
  31. package/dist/astgraph/vendor/zod/v4/classic/parse.js +15 -0
  32. package/dist/astgraph/vendor/zod/v4/classic/schemas.js +1395 -0
  33. package/dist/astgraph/vendor/zod/v4/core/api.js +1087 -0
  34. package/dist/astgraph/vendor/zod/v4/core/checks.js +575 -0
  35. package/dist/astgraph/vendor/zod/v4/core/core.js +78 -0
  36. package/dist/astgraph/vendor/zod/v4/core/doc.js +35 -0
  37. package/dist/astgraph/vendor/zod/v4/core/errors.js +185 -0
  38. package/dist/astgraph/vendor/zod/v4/core/index.js +16 -0
  39. package/dist/astgraph/vendor/zod/v4/core/json-schema-generator.js +95 -0
  40. package/dist/astgraph/vendor/zod/v4/core/json-schema-processors.js +601 -0
  41. package/dist/astgraph/vendor/zod/v4/core/json-schema.js +1 -0
  42. package/dist/astgraph/vendor/zod/v4/core/package.json +7 -0
  43. package/dist/astgraph/vendor/zod/v4/core/parse.js +93 -0
  44. package/dist/astgraph/vendor/zod/v4/core/regexes.js +139 -0
  45. package/dist/astgraph/vendor/zod/v4/core/registries.js +51 -0
  46. package/dist/astgraph/vendor/zod/v4/core/schemas.js +2239 -0
  47. package/dist/astgraph/vendor/zod/v4/core/standard-schema.js +1 -0
  48. package/dist/astgraph/vendor/zod/v4/core/to-json-schema.js +448 -0
  49. package/dist/astgraph/vendor/zod/v4/core/util.js +674 -0
  50. package/dist/astgraph/vendor/zod/v4/core/versions.js +5 -0
  51. package/dist/astgraph/vendor/zod/v4/index.js +3 -0
  52. package/dist/astgraph/vendor/zod/v4/locales/ar.js +106 -0
  53. package/dist/astgraph/vendor/zod/v4/locales/az.js +105 -0
  54. package/dist/astgraph/vendor/zod/v4/locales/be.js +156 -0
  55. package/dist/astgraph/vendor/zod/v4/locales/bg.js +120 -0
  56. package/dist/astgraph/vendor/zod/v4/locales/ca.js +107 -0
  57. package/dist/astgraph/vendor/zod/v4/locales/cs.js +111 -0
  58. package/dist/astgraph/vendor/zod/v4/locales/da.js +115 -0
  59. package/dist/astgraph/vendor/zod/v4/locales/de.js +108 -0
  60. package/dist/astgraph/vendor/zod/v4/locales/el.js +109 -0
  61. package/dist/astgraph/vendor/zod/v4/locales/en.js +113 -0
  62. package/dist/astgraph/vendor/zod/v4/locales/eo.js +109 -0
  63. package/dist/astgraph/vendor/zod/v4/locales/es.js +132 -0
  64. package/dist/astgraph/vendor/zod/v4/locales/fa.js +114 -0
  65. package/dist/astgraph/vendor/zod/v4/locales/fi.js +112 -0
  66. package/dist/astgraph/vendor/zod/v4/locales/fr-CA.js +107 -0
  67. package/dist/astgraph/vendor/zod/v4/locales/fr.js +125 -0
  68. package/dist/astgraph/vendor/zod/v4/locales/he.js +214 -0
  69. package/dist/astgraph/vendor/zod/v4/locales/hr.js +122 -0
  70. package/dist/astgraph/vendor/zod/v4/locales/hu.js +108 -0
  71. package/dist/astgraph/vendor/zod/v4/locales/hy.js +147 -0
  72. package/dist/astgraph/vendor/zod/v4/locales/id.js +106 -0
  73. package/dist/astgraph/vendor/zod/v4/locales/index.js +52 -0
  74. package/dist/astgraph/vendor/zod/v4/locales/is.js +109 -0
  75. package/dist/astgraph/vendor/zod/v4/locales/it.js +108 -0
  76. package/dist/astgraph/vendor/zod/v4/locales/ja.js +107 -0
  77. package/dist/astgraph/vendor/zod/v4/locales/ka.js +112 -0
  78. package/dist/astgraph/vendor/zod/v4/locales/kh.js +5 -0
  79. package/dist/astgraph/vendor/zod/v4/locales/km.js +110 -0
  80. package/dist/astgraph/vendor/zod/v4/locales/ko.js +111 -0
  81. package/dist/astgraph/vendor/zod/v4/locales/lt.js +203 -0
  82. package/dist/astgraph/vendor/zod/v4/locales/mk.js +109 -0
  83. package/dist/astgraph/vendor/zod/v4/locales/ms.js +107 -0
  84. package/dist/astgraph/vendor/zod/v4/locales/nl.js +110 -0
  85. package/dist/astgraph/vendor/zod/v4/locales/no.js +108 -0
  86. package/dist/astgraph/vendor/zod/v4/locales/ota.js +109 -0
  87. package/dist/astgraph/vendor/zod/v4/locales/package.json +7 -0
  88. package/dist/astgraph/vendor/zod/v4/locales/pl.js +109 -0
  89. package/dist/astgraph/vendor/zod/v4/locales/ps.js +114 -0
  90. package/dist/astgraph/vendor/zod/v4/locales/pt.js +108 -0
  91. package/dist/astgraph/vendor/zod/v4/locales/ro.js +119 -0
  92. package/dist/astgraph/vendor/zod/v4/locales/ru.js +156 -0
  93. package/dist/astgraph/vendor/zod/v4/locales/sl.js +109 -0
  94. package/dist/astgraph/vendor/zod/v4/locales/sv.js +110 -0
  95. package/dist/astgraph/vendor/zod/v4/locales/ta.js +110 -0
  96. package/dist/astgraph/vendor/zod/v4/locales/th.js +110 -0
  97. package/dist/astgraph/vendor/zod/v4/locales/tr.js +105 -0
  98. package/dist/astgraph/vendor/zod/v4/locales/ua.js +5 -0
  99. package/dist/astgraph/vendor/zod/v4/locales/uk.js +108 -0
  100. package/dist/astgraph/vendor/zod/v4/locales/ur.js +110 -0
  101. package/dist/astgraph/vendor/zod/v4/locales/uz.js +110 -0
  102. package/dist/astgraph/vendor/zod/v4/locales/vi.js +108 -0
  103. package/dist/astgraph/vendor/zod/v4/locales/yo.js +107 -0
  104. package/dist/astgraph/vendor/zod/v4/locales/zh-CN.js +109 -0
  105. package/dist/astgraph/vendor/zod/v4/locales/zh-TW.js +107 -0
  106. package/dist/astgraph/vendor/zod/v4/mini/checks.js +1 -0
  107. package/dist/astgraph/vendor/zod/v4/mini/coerce.js +22 -0
  108. package/dist/astgraph/vendor/zod/v4/mini/external.js +14 -0
  109. package/dist/astgraph/vendor/zod/v4/mini/index.js +3 -0
  110. package/dist/astgraph/vendor/zod/v4/mini/iso.js +34 -0
  111. package/dist/astgraph/vendor/zod/v4/mini/package.json +7 -0
  112. package/dist/astgraph/vendor/zod/v4/mini/parse.js +1 -0
  113. package/dist/astgraph/vendor/zod/v4/mini/schemas.js +961 -0
  114. package/dist/astgraph/vendor/zod/v4/package.json +7 -0
  115. package/dist/{chunk-EY6KE7L2.js → chunk-G6Q2Y2A7.js} +1 -1
  116. package/dist/{chunk-OT5XIN6G.js → chunk-Y5MZDG7I.js} +161 -18
  117. package/dist/cli.js +3 -3
  118. package/dist/desktop/main.js +161 -18
  119. package/dist/desktop-renderer/assets/{index-sgEm4kjf.js → index-CCnSpff0.js} +1 -1
  120. package/dist/desktop-renderer/index.html +1 -1
  121. package/dist/init/project.d.ts +8 -0
  122. package/dist/models/structured.d.ts +14 -0
  123. package/dist/permissions/engine.d.ts +2 -0
  124. package/dist/plugins/types.d.ts +2 -2
  125. package/dist/sdk/index.js +2 -2
  126. package/dist/tools/shell.d.ts +1 -1
  127. package/dist/tools/types.d.ts +2 -0
  128. package/dist/ui/session.d.ts +4 -1
  129. package/package.json +5 -3
@@ -0,0 +1,7 @@
1
+ {
2
+ "type": "module",
3
+ "main": "./index.cjs",
4
+ "module": "./index.js",
5
+ "types": "./index.d.cts",
6
+ "sideEffects": false
7
+ }
@@ -4,7 +4,7 @@ import {
4
4
  message,
5
5
  redactErrorText,
6
6
  redactSecrets
7
- } from "./chunk-OT5XIN6G.js";
7
+ } from "./chunk-Y5MZDG7I.js";
8
8
 
9
9
  // src/rpc/server.ts
10
10
  import { createInterface } from "readline";
@@ -152,11 +152,12 @@ var PermissionEngine = class {
152
152
  }
153
153
  }
154
154
  const inside = paths.every((path) => classifyPath(this.#lexicalWorkspace, this.#workspace, path).inside);
155
+ const isRead = READ_TOOLS.has(request.tool) || request.readOnly === true;
155
156
  if (this.#profile === "d2c") return this.#d2cDecision(request, inside);
156
157
  if (this.#mode === "plan") {
157
- return READ_TOOLS.has(request.tool) ? { decision: "allow" } : { decision: "deny", reason: "Plan mode is read-only" };
158
+ return isRead ? { decision: "allow" } : { decision: "deny", reason: "Plan mode is read-only" };
158
159
  }
159
- if (READ_TOOLS.has(request.tool)) {
160
+ if (isRead) {
160
161
  return { decision: "allow" };
161
162
  }
162
163
  if (DESTRUCTIVE_TOOLS.has(request.tool)) {
@@ -179,7 +180,7 @@ var PermissionEngine = class {
179
180
  if ((request.paths?.length ?? 0) > 0 && !inside) {
180
181
  return { decision: "deny", reason: "D2C tools must remain in the workspace" };
181
182
  }
182
- if (READ_TOOLS.has(request.tool)) return { decision: "allow" };
183
+ if (READ_TOOLS.has(request.tool) || request.readOnly === true) return { decision: "allow" };
183
184
  if (isDeletionTool(request.tool)) {
184
185
  return { decision: "ask", reason: "Deletion requires approval", allowAlways: false };
185
186
  }
@@ -504,7 +505,7 @@ function redactSecrets(input, secrets) {
504
505
 
505
506
  // src/init/project.ts
506
507
  import { lstat, mkdir, readdir, readFile, realpath, writeFile } from "fs/promises";
507
- import { readFileSync } from "fs";
508
+ import { copyFileSync, mkdirSync, readdirSync, readFileSync } from "fs";
508
509
  import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join, relative as relative2, sep as sep2 } from "path";
509
510
  import { fileURLToPath } from "url";
510
511
  var GENERATED_START = "<!-- flavor-code:start -->";
@@ -769,6 +770,7 @@ async function inspectProject(cwd) {
769
770
  languages.add("Python");
770
771
  }
771
772
  const gitignorePath = await pathKind(join(cwd, ".gitignore"), root) === "file" ? ".gitignore" : void 0;
773
+ const hasCodeGraph = await pathKind(join(cwd, ".flavor", "astgraph", "index.db"), root) === "file";
772
774
  return {
773
775
  cwd,
774
776
  projectName,
@@ -784,6 +786,7 @@ async function inspectProject(cwd) {
784
786
  instructionFiles,
785
787
  configFiles,
786
788
  ...gitignorePath === void 0 ? {} : { gitignorePath },
789
+ hasCodeGraph,
787
790
  cautions
788
791
  };
789
792
  }
@@ -804,6 +807,12 @@ function renderGeneratedSection(facts, newline) {
804
807
  "Do not inspect dependency directories or generated output unless explicitly required.",
805
808
  ...facts.cautions
806
809
  ];
810
+ const searchSection = facts.hasCodeGraph ? [
811
+ "",
812
+ "## Search",
813
+ "",
814
+ "- A code graph index is available (`.flavor/astgraph/index.db`): pair `ast_search` with `grep`/`glob` to locate symbols, and use `ast_callers`/`ast_callees`/`ast_impact`/`ast_context` to trace reachability instead of reading files broadly."
815
+ ] : [];
807
816
  return [
808
817
  GENERATED_START,
809
818
  "## Overview",
@@ -815,6 +824,7 @@ function renderGeneratedSection(facts, newline) {
815
824
  "## Layout",
816
825
  "",
817
826
  bulletList(facts.sourceDirectories, "No conventional source directories detected.", true, newline),
827
+ ...searchSection,
818
828
  "",
819
829
  "## Build",
820
830
  "",
@@ -964,9 +974,30 @@ async function copyCodeIslandPlugin(cwd) {
964
974
  await writeFile(join(pluginDir, name), content);
965
975
  }
966
976
  }
977
+ var ASTGRAPH_SRC_DIR = join(dirname2(fileURLToPath(import.meta.url)), "astgraph");
978
+ async function copyAstgraphPlugin(cwd) {
979
+ const pluginDir = join(cwd, ".flavor", "plugins", "astgraph");
980
+ try {
981
+ await lstat(pluginDir);
982
+ return;
983
+ } catch (error) {
984
+ if (!isMissingPathError(error)) throw error;
985
+ }
986
+ copyDirectory(ASTGRAPH_SRC_DIR, pluginDir);
987
+ }
988
+ function copyDirectory(from, to) {
989
+ mkdirSync(to, { recursive: true, mode: 448 });
990
+ for (const entry2 of readdirSync(from, { withFileTypes: true })) {
991
+ const source = join(from, entry2.name);
992
+ const target = join(to, entry2.name);
993
+ if (entry2.isDirectory()) copyDirectory(source, target);
994
+ else if (entry2.isFile()) copyFileSync(source, target);
995
+ }
996
+ }
967
997
  async function initializeFlavor(cwd) {
968
998
  await ensureFlavorDirectories(cwd);
969
999
  await copyCodeIslandPlugin(cwd);
1000
+ await copyAstgraphPlugin(cwd);
970
1001
  await createExampleFlavorConfig(cwd);
971
1002
  const facts = await inspectProject(cwd);
972
1003
  const path = join(cwd, "FLAVOR.md");
@@ -3415,7 +3446,7 @@ var FlavorSession = class {
3415
3446
  } catch {
3416
3447
  }
3417
3448
  }
3418
- const command = parseSlashCommand(prompt, this.#services.pluginCommands(), skillNames);
3449
+ const command = parseSlashCommand(prompt, this.#services.pluginCommands().map(({ name }) => name), skillNames);
3419
3450
  if (command !== null) await this.#dispatch(command, controller.signal);
3420
3451
  else for await (const event of this.#services.run(prompt, controller.signal, {
3421
3452
  getSteeringMessages: () => this.#drainMessages("steer").map((item) => item.text),
@@ -6148,6 +6179,7 @@ function withStructuredOutput(options) {
6148
6179
  }
6149
6180
  const tool = modelToolFromZod(options.name, options.description, options.schema);
6150
6181
  const maxAttempts = retry.maxRetries + 1;
6182
+ const jsonSchema = tool.inputSchema;
6151
6183
  const model = {
6152
6184
  async *stream(request) {
6153
6185
  let candidate;
@@ -6181,6 +6213,7 @@ function withStructuredOutput(options) {
6181
6213
  candidate = void 0;
6182
6214
  rawCandidate = void 0;
6183
6215
  let candidateCount = 0;
6216
+ let textCandidate = "";
6184
6217
  let attemptError;
6185
6218
  let usage;
6186
6219
  for await (const event of adapter.stream(modelRequest)) {
@@ -6198,6 +6231,8 @@ function withStructuredOutput(options) {
6198
6231
  rawCandidate = event.rawInput;
6199
6232
  secrets.add(event.rawInput);
6200
6233
  attemptError = event.error;
6234
+ } else if (event.type === "text") {
6235
+ textCandidate += event.text;
6201
6236
  } else if (event.type === "usage") {
6202
6237
  usage = { inputTokens: event.inputTokens, outputTokens: event.outputTokens };
6203
6238
  } else if (event.type === "done") {
@@ -6208,6 +6243,17 @@ function withStructuredOutput(options) {
6208
6243
  }
6209
6244
  if (usage !== void 0) yield { type: "usage", ...usage };
6210
6245
  if (attemptError === void 0) {
6246
+ if (candidateCount === 0 && textCandidate.trim() !== "") {
6247
+ const extracted = extractJsonObject(textCandidate);
6248
+ if (extracted !== void 0) {
6249
+ candidate = extracted;
6250
+ candidateCount = 1;
6251
+ rawCandidate = textCandidate;
6252
+ }
6253
+ }
6254
+ if (candidateCount === 1 && candidate !== null && typeof candidate === "object") {
6255
+ candidate = coerceByJsonSchema(candidate, jsonSchema);
6256
+ }
6211
6257
  if (candidateCount !== 1) {
6212
6258
  attemptError = {
6213
6259
  code: "invalid_tool_arguments",
@@ -6280,6 +6326,86 @@ function withStructuredOutput(options) {
6280
6326
  function strictJsonSchema(schema) {
6281
6327
  return ensureStrictSchema(z7.toJSONSchema(schema));
6282
6328
  }
6329
+ function extractJsonObject(text) {
6330
+ const trimmed = text.trim();
6331
+ const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/u);
6332
+ const candidates = fenced !== null && fenced[1] !== void 0 ? [fenced[1].trim(), trimmed] : [trimmed];
6333
+ for (const candidate of candidates) {
6334
+ try {
6335
+ const parsed = JSON.parse(candidate);
6336
+ if (parsed !== null && typeof parsed === "object") return parsed;
6337
+ } catch {
6338
+ }
6339
+ }
6340
+ const start = trimmed.indexOf("{");
6341
+ if (start >= 0) {
6342
+ let depth = 0;
6343
+ let inString = false;
6344
+ let escaped = false;
6345
+ for (let index = start; index < trimmed.length; index += 1) {
6346
+ const char = trimmed[index];
6347
+ if (inString) {
6348
+ if (escaped) escaped = false;
6349
+ else if (char === "\\") escaped = true;
6350
+ else if (char === '"') inString = false;
6351
+ continue;
6352
+ }
6353
+ if (char === '"') inString = true;
6354
+ else if (char === "{") depth += 1;
6355
+ else if (char === "}") {
6356
+ depth -= 1;
6357
+ if (depth === 0) {
6358
+ try {
6359
+ const parsed = JSON.parse(trimmed.slice(start, index + 1));
6360
+ if (parsed !== null && typeof parsed === "object") return parsed;
6361
+ } catch {
6362
+ return void 0;
6363
+ }
6364
+ }
6365
+ }
6366
+ }
6367
+ }
6368
+ return void 0;
6369
+ }
6370
+ function coerceByJsonSchema(value, schema) {
6371
+ if (schema === null || schema === void 0 || typeof schema !== "object") return value;
6372
+ const node = schema;
6373
+ const branches = [
6374
+ ...Array.isArray(node.anyOf) ? node.anyOf : [],
6375
+ ...Array.isArray(node.allOf) ? node.allOf : []
6376
+ ].filter((branch) => branch !== null && typeof branch === "object");
6377
+ const types = [];
6378
+ const pushTypes = (candidate) => {
6379
+ if (typeof candidate.type === "string") types.push(candidate.type);
6380
+ else if (Array.isArray(candidate.type)) types.push(...candidate.type);
6381
+ };
6382
+ pushTypes(node);
6383
+ for (const branch of branches) pushTypes(branch);
6384
+ if (types.includes("null") && (value === null || value === void 0)) return value;
6385
+ if ((types.includes("number") || types.includes("integer")) && typeof value === "string" && value.trim() !== "") {
6386
+ const numeric = Number(value);
6387
+ if (Number.isFinite(numeric)) return numeric;
6388
+ }
6389
+ if (types.includes("boolean") && typeof value === "string") {
6390
+ const lowered = value.trim().toLowerCase();
6391
+ if (lowered === "true") return true;
6392
+ if (lowered === "false") return false;
6393
+ }
6394
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
6395
+ const properties = node.properties ?? branches.find((branch) => branch.properties)?.properties;
6396
+ if (properties !== void 0) {
6397
+ const output = {};
6398
+ for (const [key, child] of Object.entries(value)) {
6399
+ output[key] = properties[key] !== void 0 ? coerceByJsonSchema(child, properties[key]) : child;
6400
+ }
6401
+ return output;
6402
+ }
6403
+ }
6404
+ if (types.includes("array") && Array.isArray(value) && node.items !== void 0) {
6405
+ return value.map((item) => coerceByJsonSchema(item, node.items));
6406
+ }
6407
+ return value;
6408
+ }
6283
6409
  function modelToolFromZod(name, description, schema) {
6284
6410
  return { name, description, inputSchema: strictJsonSchema(schema) };
6285
6411
  }
@@ -6642,6 +6768,14 @@ var AgentLoop = class {
6642
6768
  toolCalls.push({ id: collected.id, name: collected.name, input: validation.input });
6643
6769
  continue;
6644
6770
  }
6771
+ if (collected.kind === "valid") {
6772
+ const coerced = coerceByJsonSchema(collected.input, strictJsonSchema(definition.inputSchema));
6773
+ const revalidation = this.#options.runtime.validate({ ...collected, input: coerced });
6774
+ if (revalidation.ok) {
6775
+ toolCalls.push({ id: collected.id, name: collected.name, input: revalidation.input });
6776
+ continue;
6777
+ }
6778
+ }
6645
6779
  const repairModelId = this.#options.fallbackModelId ?? this.#options.modelId;
6646
6780
  const invalidOutput = collected.kind === "invalid" ? collected.rawInput : serializeToolInput(collected.input);
6647
6781
  const structured = withStructuredOutput({
@@ -7141,7 +7275,8 @@ var ToolRuntime = class {
7141
7275
  const request = {
7142
7276
  agent: context.agent,
7143
7277
  tool: tool.name,
7144
- ...tool.permissions?.(input) ?? { paths: tool.paths(input) }
7278
+ ...tool.permissions?.(input) ?? { paths: tool.paths(input) },
7279
+ ...tool.readOnly === true ? { readOnly: true } : {}
7145
7280
  };
7146
7281
  let permission = this.#permissions.decide(request);
7147
7282
  const nonCacheableApproval = request.allowAlways === false || permission.allowAlways === false;
@@ -9214,7 +9349,9 @@ var ShellInput = z14.object({
9214
9349
  args: z14.array(z14.string()),
9215
9350
  cwd: z14.string().min(1).nullable().optional(),
9216
9351
  timeoutMs: z14.coerce.number().int().positive().max(864e5).nullable().optional(),
9217
- background: z14.boolean().optional()
9352
+ // Accepts booleans and their string forms (weak-typed models emit "true");
9353
+ // kept transform-free so the schema converts to JSON Schema for providers.
9354
+ background: z14.union([z14.boolean(), z14.string().refine((value) => value === "true" || value === "false")]).optional()
9218
9355
  });
9219
9356
  function createShellTool(workspace, options = {}) {
9220
9357
  const root = resolve11(workspace);
@@ -9230,7 +9367,7 @@ function createShellTool(workspace, options = {}) {
9230
9367
  inputSchema: ShellInput,
9231
9368
  paths: (input) => [workingDirectory(root, input.cwd ?? void 0)],
9232
9369
  summarize: (input) => [input.command, ...input.args].join(" "),
9233
- presentCall: (input) => ({ kind: "terminal", variant: "command", title: input.background ? "Starting background command" : "Running command", command: [input.command, ...input.args].join(" "), state: "running" }),
9370
+ presentCall: (input) => ({ kind: "terminal", variant: "command", title: backgroundFlag(input.background) ? "Starting background command" : "Running command", command: [input.command, ...input.args].join(" "), state: "running" }),
9234
9371
  permissions: (input) => ({
9235
9372
  paths: [workingDirectory(root, input.cwd ?? void 0)],
9236
9373
  command: input.command,
@@ -9247,7 +9384,7 @@ function createShellTool(workspace, options = {}) {
9247
9384
  } : { kind: "terminal", variant: "command", title: [input.command, ...input.args].join(" "), command: [input.command, ...input.args].join(" "), stdout: output.stdout, stderr: output.stderr, exitCode: output.exitCode, state: output.terminationReason === "cancelled" ? "cancelled" : output.exitCode === 0 ? "completed" : "failed", truncated: output.truncated },
9248
9385
  execute: async (input, signal, context) => {
9249
9386
  const timeoutMs = input.timeoutMs ?? defaultTimeoutMs;
9250
- if (input.background === true) {
9387
+ if (backgroundFlag(input.background)) {
9251
9388
  if (options.jobs === void 0) throw new Error("Background shell requires a JobRegistry");
9252
9389
  const cancellation = new AbortController();
9253
9390
  const job = options.jobs.create({
@@ -9497,6 +9634,9 @@ var AdaptiveOutputDecoder = class {
9497
9634
  return text;
9498
9635
  }
9499
9636
  };
9637
+ function backgroundFlag(value) {
9638
+ return value === true || value === "true";
9639
+ }
9500
9640
  function workingDirectory(root, cwd = ".") {
9501
9641
  const candidate = resolve11(root, cwd);
9502
9642
  const delta = relative10(root, candidate);
@@ -11325,7 +11465,7 @@ var PluginHost = class {
11325
11465
  return writeFile6(allowed, data2);
11326
11466
  }
11327
11467
  }) }),
11328
- registerCommand: (name, value) => register("command", name, [value]),
11468
+ registerCommand: (name, value, description) => register("command", name, [value, description]),
11329
11469
  registerTool: (name, value) => register("tool", name, [value]),
11330
11470
  registerHook: (name, value, options) => register("hook", name, [value, options]),
11331
11471
  registerSkillRoot: (name, root) => {
@@ -16925,7 +17065,8 @@ import { z as z27 } from "zod";
16925
17065
  import { resolve as resolve23 } from "path";
16926
17066
  var Id = z27.string().regex(/^term-[a-f0-9-]+$/);
16927
17067
  var OpenInput = z27.object({ cwd: z27.string().optional(), shell: z27.string().optional(), args: z27.array(z27.string()).optional(), columns: z27.coerce.number().int().min(10).max(500).optional(), rows: z27.coerce.number().int().min(2).max(300).optional() });
16928
- var WriteInput2 = z27.object({ id: Id, data: z27.string().max(1e5), enter: z27.boolean().optional() });
17068
+ var OptionalBoolean = z27.union([z27.boolean(), z27.string().refine((value) => value === "true" || value === "false")]);
17069
+ var WriteInput2 = z27.object({ id: Id, data: z27.string().max(1e5), enter: OptionalBoolean.optional() });
16929
17070
  var ReadInput3 = z27.object({ id: Id, cursor: z27.coerce.number().int().min(0).optional() });
16930
17071
  var ResizeInput = z27.object({ id: Id, columns: z27.coerce.number().int().min(10).max(500), rows: z27.coerce.number().int().min(2).max(300) });
16931
17072
  var CloseInput = z27.object({ id: Id });
@@ -16954,8 +17095,9 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
16954
17095
  paths: () => [],
16955
17096
  permissions: (input) => ({ paths: [], command: input.data, args: [] }),
16956
17097
  execute: async (input, _signal, context) => {
16957
- terminals.write(input.id, owner(context), input.data + (input.enter ? "\r" : ""));
16958
- return { id: input.id, written: input.data.length + (input.enter ? 1 : 0) };
17098
+ const enter = input.enter === true || input.enter === "true";
17099
+ terminals.write(input.id, owner(context), input.data + (enter ? "\r" : ""));
17100
+ return { id: input.id, written: input.data.length + (enter ? 1 : 0) };
16959
17101
  }
16960
17102
  },
16961
17103
  {
@@ -17832,15 +17974,16 @@ async function createProductionRuntime(options) {
17832
17974
  projectPluginDirs: [join19(workspace, ".flavor", "plugins")],
17833
17975
  config,
17834
17976
  registrations: {
17835
- command(name, handler) {
17977
+ command(name, handler, description) {
17836
17978
  if (typeof handler !== "function") throw new Error(`Plugin command "${name}" must be a function.`);
17837
17979
  if (name !== name.toLowerCase()) throw new Error(`Plugin command "${name}" must be lowercase.`);
17838
17980
  if (MVP_COMMANDS.includes(name) || name === "ide" || pluginCommands.has(name)) {
17839
17981
  throw new Error(`Plugin command "${name}" conflicts with a built-in or registered command.`);
17840
17982
  }
17841
- pluginCommands.set(name, handler);
17983
+ const desc = typeof description === "string" && description.trim().length > 0 ? description.trim() : void 0;
17984
+ pluginCommands.set(name, desc === void 0 ? { handler } : { handler, description: desc });
17842
17985
  return () => {
17843
- if (pluginCommands.get(name) === handler) pluginCommands.delete(name);
17986
+ if (pluginCommands.get(name)?.handler === handler) pluginCommands.delete(name);
17844
17987
  };
17845
17988
  },
17846
17989
  tool(name, tool) {
@@ -18937,9 +19080,9 @@ ${renderMemoryDocument(entries)}`;
18937
19080
  return removed === 0 ? "No cold long-term memories to remove." : `Forgot ${removed} cold memory ${removed === 1 ? "entry" : "entries"} and removed ${filesRemoved} task ${filesRemoved === 1 ? "file" : "files"}.`;
18938
19081
  },
18939
19082
  finishTask: () => finalizeMemoryTask(true),
18940
- pluginCommands: () => [...pluginCommands.keys()].sort(),
19083
+ pluginCommands: () => [...pluginCommands.entries()].map(([name, entry2]) => entry2.description === void 0 ? { name } : { name, description: entry2.description }).sort((left, right) => left.name.localeCompare(right.name)),
18941
19084
  runPluginCommand: async (name, args, signal) => {
18942
- const handler = pluginCommands.get(name);
19085
+ const handler = pluginCommands.get(name)?.handler;
18943
19086
  if (handler === void 0) throw new Error(`Plugin command /${name} is no longer registered.`);
18944
19087
  signal.throwIfAborted();
18945
19088
  return awaitWithSignal(Promise.resolve(handler(args, { workspace, signal })), signal);
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
3
3
  import {
4
4
  FlavorRpcServer,
5
5
  TraceRecorder
6
- } from "./chunk-EY6KE7L2.js";
6
+ } from "./chunk-G6Q2Y2A7.js";
7
7
  import {
8
8
  staticTaskLines
9
9
  } from "./chunk-KKIBYDJI.js";
@@ -22,7 +22,7 @@ import {
22
22
  packageVersion,
23
23
  redactErrorText,
24
24
  runPalBroker
25
- } from "./chunk-OT5XIN6G.js";
25
+ } from "./chunk-Y5MZDG7I.js";
26
26
  import {
27
27
  McpServerConfigSchema,
28
28
  McpServerNameSchema,
@@ -800,7 +800,7 @@ async function runInteractiveCli(props) {
800
800
  const [{ render, AlternateScreen }, { createElement }, { App }] = await Promise.all([
801
801
  import("./claude-ink-ORUA7GWG.js"),
802
802
  import("react"),
803
- import("./app-MXWPZBEU.js")
803
+ import("./app-Z3YWFIBB.js")
804
804
  ]);
805
805
  const instance = await render(createElement(
806
806
  AlternateScreen,