open-multi-agent-kit 0.90.6 → 0.90.7

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 (51) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +9 -9
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +1 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/command-safety.d.ts.map +1 -1
  7. package/dist/core/command-safety.js +7 -0
  8. package/dist/core/command-safety.js.map +1 -1
  9. package/dist/core/reasoning-router-v4-normalize.d.ts +11 -0
  10. package/dist/core/reasoning-router-v4-normalize.d.ts.map +1 -0
  11. package/dist/core/reasoning-router-v4-normalize.js +224 -0
  12. package/dist/core/reasoning-router-v4-normalize.js.map +1 -0
  13. package/dist/core/reasoning-router-v4-weights.d.ts +13 -5
  14. package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
  15. package/dist/core/reasoning-router-v4-weights.js +8 -4
  16. package/dist/core/reasoning-router-v4-weights.js.map +1 -1
  17. package/dist/core/reasoning-router-v4.d.ts +3 -3
  18. package/dist/core/reasoning-router-v4.d.ts.map +1 -1
  19. package/dist/core/reasoning-router-v4.js +394 -58
  20. package/dist/core/reasoning-router-v4.js.map +1 -1
  21. package/dist/core/router-feedback-collector.d.ts +1 -1
  22. package/dist/core/router-feedback-collector.d.ts.map +1 -1
  23. package/dist/core/router-feedback-collector.js +9 -1
  24. package/dist/core/router-feedback-collector.js.map +1 -1
  25. package/dist/core/settings-manager.d.ts +3 -3
  26. package/dist/core/settings-manager.d.ts.map +1 -1
  27. package/dist/core/settings-manager.js.map +1 -1
  28. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  29. package/dist/modes/interactive/components/settings-selector.js +1 -0
  30. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  31. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  32. package/dist/modes/interactive/components/thinking-selector.js +1 -0
  33. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  34. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  35. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  36. package/dist/modes/interactive/theme/theme.js +2 -0
  37. package/dist/modes/interactive/theme/theme.js.map +1 -1
  38. package/docs/adaptorch-preview.md +84 -0
  39. package/docs/loadout-domains/frontend-ui.md +5 -1
  40. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  41. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  42. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  43. package/examples/extensions/gondolin/package-lock.json +2 -2
  44. package/examples/extensions/gondolin/package.json +1 -1
  45. package/examples/extensions/preset.ts +2 -2
  46. package/examples/extensions/sandbox/package-lock.json +2 -2
  47. package/examples/extensions/sandbox/package.json +1 -1
  48. package/examples/extensions/with-deps/package-lock.json +2 -2
  49. package/examples/extensions/with-deps/package.json +1 -1
  50. package/npm-shrinkwrap.json +12 -12
  51. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,13 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [0.90.7] - 2026-07-11
4
+
5
+ ### Added
6
+
7
+ - Added the `ultra` thinking level (maximum reasoning with automatic task delegation, GPT-5.6 Sol/Terra via `openai-codex`) to the CLI `--thinking` flag, thinking selector, settings, theme colors, and router feedback levels; the reasoning router never auto-selects `ultra`, and models without it clamp down to their highest supported level.
4
8
 
5
9
  ### Changed
6
10
 
11
+ - Refreshed built-in model metadata via `omk-ai` regeneration so OpenRouter latest tool-capable models and OpenCode Go (`opencode-go`) catalog entries stay current for `/model` and provider defaults.
7
12
  - Changed the root README project demo link to Mario's Threads profile.
8
13
 
9
14
  ### Fixed
10
15
 
16
+ - Fixed `secret.read_path` command-safety false positives for search commands: the pattern argument immediately after `--` in `grep`/`egrep`/`fgrep`/`rg` is no longer treated as a secret file operand, while real secret-file operands (e.g. `rg -- needle .env`, `grep -f .env needle`) stay confirm-tier.
11
17
  - Fixed npm trusted-publishing identity after the GitHub repository rename by aligning package metadata with `dmae97/omk`.
12
18
 
13
19
  ## [0.90.6] - 2026-07-09
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
  <p align="center">
16
16
  <a href="https://discord.com/invite/3cU7Bz4UPx"><img alt="Discord" src="https://img.shields.io/badge/discord-community-5865F2?style=flat-square&logo=discord&logoColor=white" /></a>
17
17
  <a href="https://www.npmjs.com/package/open-multi-agent-kit"><img alt="npm" src="https://img.shields.io/npm/v/open-multi-agent-kit?style=flat-square" /></a>
18
- <a href="https://github.com/dmae97/open-multi-agent-kit/releases/tag/v0.90.6"><img alt="Release" src="https://img.shields.io/badge/release-v0.90.6-00d7ff?style=flat-square" /></a>
18
+ <a href="https://github.com/dmae97/omk/releases/tag/v0.90.7"><img alt="Release" src="https://img.shields.io/badge/release-v0.90.7-00d7ff?style=flat-square" /></a>
19
19
  </p>
20
20
 
21
21
  > New issues and PRs from new contributors are auto-closed by default. Maintainers review auto-closed issues daily. See [CONTRIBUTING.md](https://github.com/dmae97/open-multi-agent-kit/blob/v0.90.4/CONTRIBUTING.md).
@@ -32,19 +32,19 @@ The OMK//CONTROL startup surface is the default operator view. The header reads
32
32
 
33
33
  The default dark TUI theme uses the `omk-control-grid-dark` Night City palette and keeps the control sidebar focused on route, evidence, loop, MCP, runtime, skills, and context budget state.
34
34
 
35
- ## Release v0.90.6
35
+ ## Release v0.90.7
36
36
 
37
- This release documents the operator package/slash loadout (with the omkgirl hero asset), lands the experimental B2C Correctness Wall and Grok OAuth harness wiring, and continues the v4 reasoning-router generalization track.
37
+ This release ships the `ultra` thinking level with the GPT-5.6 model family, opt-in parallel tool batching in the agent loop, TUI skill autocomplete plus Korean/CJK cursor fixes, and command-safety search-pattern false-positive fixes.
38
38
 
39
39
  | Area | What changed |
40
40
  |------|--------------|
41
- | Docs / loadout | Root README now documents pinned pi.dev package installs, slash-command map, and skills.sh install flow; added `readmeasset/omkgirl.png`. |
42
- | Correctness Wall | Experimental B2C Correctness Wall extension with soft/hard/shadow modes; evidence-gated advisory evaluation only. |
43
- | Grok harness | Grok OAuth domain profile, playbook auto-apply, `grok-4.5` compaction preference, Imagine chat-model rejection on completion paths. |
44
- | Extension host | `callMcpTool` bind path for extensions at load time. |
45
- | Reasoning router | Table-driven intent clusters + normalize module and governed held-out generalization gate. |
41
+ | Thinking / models | New `ultra` thinking level (GPT-5.6 Sol/Terra via `openai-codex`) and the GPT-5.6 family across OpenAI, Azure, OpenRouter, AI Gateway, and Codex; regenerated model catalog. |
42
+ | Agent loop | Opt-in parallel tool batching: policy-gated `shouldParallelizeToolBatch`, unknown tools stay sequential. |
43
+ | TUI | Bare and mid-message skill autocomplete; vertical cursor movement tracks display cells so wrapped Korean/CJK lines keep the column; narrow-width wide-grapheme wrap guard. |
44
+ | Command safety | `grep`/`rg` pattern arguments after `--` no longer trip `secret.read_path`; real secret-file operands stay gated. |
45
+ | Release infra | npm trusted-publishing identity aligned with the `dmae97/omk` repository rename. |
46
46
 
47
- GitHub-focused release notes live in [RELEASE_NOTES_v0.90.6.md](https://github.com/dmae97/open-multi-agent-kit/blob/v0.90.6/.github/RELEASE_NOTES_v0.90.6.md). The GitHub release workflow also extracts the canonical release body from [packages/coding-agent/CHANGELOG.md](https://github.com/dmae97/open-multi-agent-kit/blob/v0.90.6/packages/coding-agent/CHANGELOG.md).
47
+ GitHub-focused release notes live in [RELEASE_NOTES_v0.90.7.md](https://github.com/dmae97/omk/blob/v0.90.7/.github/RELEASE_NOTES_v0.90.7.md). The GitHub release workflow also extracts the canonical release body from [packages/coding-agent/CHANGELOG.md](https://github.com/dmae97/omk/blob/v0.90.7/packages/coding-agent/CHANGELOG.md).
48
48
 
49
49
  OMK is a minimal terminal coding harness. Adapt omk to your workflows, not the other way around, without having to fork and modify omk internals. Extend it with TypeScript [Extensions](#extensions), [Skills](#skills), [Prompt Templates](#prompt-templates), and [Themes](#themes). Put your extensions, skills, prompt templates, and themes in [OMK Packages](#omk-packages) and share them with others via npm or git.
50
50
 
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;IAC5C,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnE;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+I9C;AAED,wBAAgB,SAAS,CAAC,cAAc,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAkLhE","sourcesContent":["/**\n * CLI argument parsing and help display\n * allow: SIZE_OK - legacy CLI parser; this change only extends existing typed flags.\n */\n\nimport chalk from \"chalk\";\nimport type { ThinkingLevel } from \"omk-agent-core\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from \"../config.ts\";\nimport type { ExtensionFlag } from \"../core/extensions/types.ts\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tname?: string;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionId?: string;\n\tfork?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: string[];\n\texcludeTools?: string[];\n\tnoTools?: boolean;\n\tnoBuiltinTools?: boolean;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n\tdiagnostics: Array<{ type: \"warning\" | \"error\"; message: string }>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t\tdiagnostics: [],\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = result.appendSystemPrompt ?? [];\n\t\t\tresult.appendSystemPrompt.push(args[++i]);\n\t\t} else if (arg === \"--name\" || arg === \"-n\") {\n\t\t\tif (i + 1 < args.length) {\n\t\t\t\tresult.name = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({ type: \"error\", message: \"--name requires a value\" });\n\t\t\t}\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-id\" && i + 1 < args.length) {\n\t\t\tresult.sessionId = args[++i];\n\t\t} else if (arg === \"--fork\" && i + 1 < args.length) {\n\t\t\tresult.fork = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\" || arg === \"-nt\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--no-builtin-tools\" || arg === \"-nbt\") {\n\t\t\tresult.noBuiltinTools = true;\n\t\t} else if ((arg === \"--tools\" || arg === \"-t\") && i + 1 < args.length) {\n\t\t\tresult.tools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if ((arg === \"--exclude-tools\" || arg === \"-xt\") && i + 1 < args.length) {\n\t\t\tresult.excludeTools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t\tconst next = args[i + 1];\n\t\t\tif (next !== undefined && !next.startsWith(\"@\") && (!next.startsWith(\"-\") || next.startsWith(\"---\"))) {\n\t\t\t\tresult.messages.push(next);\n\t\t\t\ti++;\n\t\t\t}\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--no-context-files\" || arg === \"-nc\") {\n\t\t\tresult.noContextFiles = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\")) {\n\t\t\tconst eqIndex = arg.indexOf(\"=\");\n\t\t\tif (eqIndex !== -1) {\n\t\t\t\tresult.unknownFlags.set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1));\n\t\t\t} else {\n\t\t\t\tconst flagName = arg.slice(2);\n\t\t\t\tconst next = args[i + 1];\n\t\t\t\tif (next !== undefined && !next.startsWith(\"-\") && !next.startsWith(\"@\")) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, next);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tresult.diagnostics.push({ type: \"error\", message: `Unknown option: ${arg}` });\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(extensionFlags?: ExtensionFlag[]): void {\n\tconst extensionFlagsText =\n\t\textensionFlags && extensionFlags.length > 0\n\t\t\t? `\\n${chalk.bold(\"Extension CLI Flags:\")}\\n${extensionFlags\n\t\t\t\t\t.map((flag) => {\n\t\t\t\t\t\tconst value = flag.type === \"string\" ? \" <value>\" : \"\";\n\t\t\t\t\t\tconst description = flag.description ?? `Registered by ${flag.extensionPath}`;\n\t\t\t\t\t\treturn ` --${flag.name}${value}`.padEnd(30) + description;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")}\\n`\n\t\t\t: \"\";\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] Remove extension source from settings\n ${APP_NAME} uninstall <source> [-l] Alias for remove\n ${APP_NAME} update [source|self|omk] Update OMK and installed extensions\n ${APP_NAME} list List installed extensions from settings\n ${APP_NAME} config Open TUI to enable/disable package resources\n ${APP_NAME} quota Manage CodexBar quota connector and summaries\n ${APP_NAME} <command> --help Show help for install/remove/uninstall/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path|id> Use specific session file or partial UUID\n --session-id <id> Use exact project session ID, creating it if missing\n --fork <path|id> Fork specific session file or partial UUID into a new session\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --name, -n <name> Set session display name\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools, -nt Disable all tools by default (built-in and extension)\n --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled\n --tools, -t <tools> Comma-separated allowlist of tool names to enable\n Applies to built-in, extension, and custom tools\n --exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable\n Applies to built-in, extension, and custom tools\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --no-context-files, -nc Disable AGENTS.md and CLAUDE.md discovery and loading\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --offline Disable startup network operations (same as OMK_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).${extensionFlagsText}\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Start a named session\n ${APP_NAME} --name \"Refactor auth module\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Disable one tool while keeping the rest available\n ${APP_NAME} --exclude-tools ask_question\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n ANT_LING_API_KEY - Ant Ling API key\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI/Cognitive Services base URL (e.g. https://{resource}.openai.azure.com)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n DEEPSEEK_API_KEY - DeepSeek API key\n NVIDIA_API_KEY - NVIDIA NIM API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n FIREWORKS_API_KEY - Fireworks API key\n TOGETHER_API_KEY - Together AI API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n ZAI_CODING_CN_API_KEY - ZAI Coding Plan API key (China)\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n MOONSHOT_API_KEY - Moonshot AI API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway)\n CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both)\n CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway)\n XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing)\n XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region)\n XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region)\n XIAOMI_TOKEN_PLAN_SGP_API_KEY - Xiaomi MiMo Token Plan API key (Singapore region)\n ZYLOO_API_KEY - Zyloo API key\n AWS_PROFILE - AWS profile for Amazon Bedrock\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Config directory (default: ~/${CONFIG_DIR_NAME}/agent)\n ${ENV_SESSION_DIR.padEnd(32)} - Session storage directory (overridden by --session-dir)\n OMK_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n OMK_OFFLINE - Disable startup network operations when set to 1/true/yes\n OMK_TELEMETRY - Override install telemetry when set to 1/true/yes or 0/false/no\n OMK_SHARE_VIEWER_URL - Base URL for /share command (default: https://omk.dev/session/)\n\n${chalk.bold(\"Built-in Tool Names:\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;IAC5C,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnE;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+I9C;AAED,wBAAgB,SAAS,CAAC,cAAc,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAkLhE","sourcesContent":["/**\n * CLI argument parsing and help display\n * allow: SIZE_OK - legacy CLI parser; this change only extends existing typed flags.\n */\n\nimport chalk from \"chalk\";\nimport type { ThinkingLevel } from \"omk-agent-core\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from \"../config.ts\";\nimport type { ExtensionFlag } from \"../core/extensions/types.ts\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tname?: string;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionId?: string;\n\tfork?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: string[];\n\texcludeTools?: string[];\n\tnoTools?: boolean;\n\tnoBuiltinTools?: boolean;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n\tdiagnostics: Array<{ type: \"warning\" | \"error\"; message: string }>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultra\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t\tdiagnostics: [],\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = result.appendSystemPrompt ?? [];\n\t\t\tresult.appendSystemPrompt.push(args[++i]);\n\t\t} else if (arg === \"--name\" || arg === \"-n\") {\n\t\t\tif (i + 1 < args.length) {\n\t\t\t\tresult.name = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({ type: \"error\", message: \"--name requires a value\" });\n\t\t\t}\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-id\" && i + 1 < args.length) {\n\t\t\tresult.sessionId = args[++i];\n\t\t} else if (arg === \"--fork\" && i + 1 < args.length) {\n\t\t\tresult.fork = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\" || arg === \"-nt\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--no-builtin-tools\" || arg === \"-nbt\") {\n\t\t\tresult.noBuiltinTools = true;\n\t\t} else if ((arg === \"--tools\" || arg === \"-t\") && i + 1 < args.length) {\n\t\t\tresult.tools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if ((arg === \"--exclude-tools\" || arg === \"-xt\") && i + 1 < args.length) {\n\t\t\tresult.excludeTools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t\tconst next = args[i + 1];\n\t\t\tif (next !== undefined && !next.startsWith(\"@\") && (!next.startsWith(\"-\") || next.startsWith(\"---\"))) {\n\t\t\t\tresult.messages.push(next);\n\t\t\t\ti++;\n\t\t\t}\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--no-context-files\" || arg === \"-nc\") {\n\t\t\tresult.noContextFiles = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\")) {\n\t\t\tconst eqIndex = arg.indexOf(\"=\");\n\t\t\tif (eqIndex !== -1) {\n\t\t\t\tresult.unknownFlags.set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1));\n\t\t\t} else {\n\t\t\t\tconst flagName = arg.slice(2);\n\t\t\t\tconst next = args[i + 1];\n\t\t\t\tif (next !== undefined && !next.startsWith(\"-\") && !next.startsWith(\"@\")) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, next);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tresult.diagnostics.push({ type: \"error\", message: `Unknown option: ${arg}` });\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(extensionFlags?: ExtensionFlag[]): void {\n\tconst extensionFlagsText =\n\t\textensionFlags && extensionFlags.length > 0\n\t\t\t? `\\n${chalk.bold(\"Extension CLI Flags:\")}\\n${extensionFlags\n\t\t\t\t\t.map((flag) => {\n\t\t\t\t\t\tconst value = flag.type === \"string\" ? \" <value>\" : \"\";\n\t\t\t\t\t\tconst description = flag.description ?? `Registered by ${flag.extensionPath}`;\n\t\t\t\t\t\treturn ` --${flag.name}${value}`.padEnd(30) + description;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")}\\n`\n\t\t\t: \"\";\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] Remove extension source from settings\n ${APP_NAME} uninstall <source> [-l] Alias for remove\n ${APP_NAME} update [source|self|omk] Update OMK and installed extensions\n ${APP_NAME} list List installed extensions from settings\n ${APP_NAME} config Open TUI to enable/disable package resources\n ${APP_NAME} quota Manage CodexBar quota connector and summaries\n ${APP_NAME} <command> --help Show help for install/remove/uninstall/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path|id> Use specific session file or partial UUID\n --session-id <id> Use exact project session ID, creating it if missing\n --fork <path|id> Fork specific session file or partial UUID into a new session\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --name, -n <name> Set session display name\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools, -nt Disable all tools by default (built-in and extension)\n --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled\n --tools, -t <tools> Comma-separated allowlist of tool names to enable\n Applies to built-in, extension, and custom tools\n --exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable\n Applies to built-in, extension, and custom tools\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --no-context-files, -nc Disable AGENTS.md and CLAUDE.md discovery and loading\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --offline Disable startup network operations (same as OMK_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).${extensionFlagsText}\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Start a named session\n ${APP_NAME} --name \"Refactor auth module\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Disable one tool while keeping the rest available\n ${APP_NAME} --exclude-tools ask_question\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n ANT_LING_API_KEY - Ant Ling API key\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI/Cognitive Services base URL (e.g. https://{resource}.openai.azure.com)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n DEEPSEEK_API_KEY - DeepSeek API key\n NVIDIA_API_KEY - NVIDIA NIM API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n FIREWORKS_API_KEY - Fireworks API key\n TOGETHER_API_KEY - Together AI API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n ZAI_CODING_CN_API_KEY - ZAI Coding Plan API key (China)\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n MOONSHOT_API_KEY - Moonshot AI API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway)\n CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both)\n CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway)\n XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing)\n XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region)\n XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region)\n XIAOMI_TOKEN_PLAN_SGP_API_KEY - Xiaomi MiMo Token Plan API key (Singapore region)\n ZYLOO_API_KEY - Zyloo API key\n AWS_PROFILE - AWS profile for Amazon Bedrock\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Config directory (default: ~/${CONFIG_DIR_NAME}/agent)\n ${ENV_SESSION_DIR.padEnd(32)} - Session storage directory (overridden by --session-dir)\n OMK_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n OMK_OFFLINE - Disable startup network operations when set to 1/true/yes\n OMK_TELEMETRY - Override install telemetry when set to 1/true/yes or 0/false/no\n OMK_SHARE_VIEWER_URL - Base URL for /share command (default: https://omk.dev/session/)\n\n${chalk.bold(\"Built-in Tool Names:\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
package/dist/cli/args.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import chalk from "chalk";
6
6
  import { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from "../config.js";
7
- const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
7
+ const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
8
8
  export function isValidThinkingLevel(level) {
9
9
  return VALID_THINKING_LEVELS.includes(level);
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CLI argument parsing and help display\n * allow: SIZE_OK - legacy CLI parser; this change only extends existing typed flags.\n */\n\nimport chalk from \"chalk\";\nimport type { ThinkingLevel } from \"omk-agent-core\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from \"../config.ts\";\nimport type { ExtensionFlag } from \"../core/extensions/types.ts\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tname?: string;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionId?: string;\n\tfork?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: string[];\n\texcludeTools?: string[];\n\tnoTools?: boolean;\n\tnoBuiltinTools?: boolean;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n\tdiagnostics: Array<{ type: \"warning\" | \"error\"; message: string }>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t\tdiagnostics: [],\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = result.appendSystemPrompt ?? [];\n\t\t\tresult.appendSystemPrompt.push(args[++i]);\n\t\t} else if (arg === \"--name\" || arg === \"-n\") {\n\t\t\tif (i + 1 < args.length) {\n\t\t\t\tresult.name = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({ type: \"error\", message: \"--name requires a value\" });\n\t\t\t}\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-id\" && i + 1 < args.length) {\n\t\t\tresult.sessionId = args[++i];\n\t\t} else if (arg === \"--fork\" && i + 1 < args.length) {\n\t\t\tresult.fork = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\" || arg === \"-nt\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--no-builtin-tools\" || arg === \"-nbt\") {\n\t\t\tresult.noBuiltinTools = true;\n\t\t} else if ((arg === \"--tools\" || arg === \"-t\") && i + 1 < args.length) {\n\t\t\tresult.tools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if ((arg === \"--exclude-tools\" || arg === \"-xt\") && i + 1 < args.length) {\n\t\t\tresult.excludeTools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t\tconst next = args[i + 1];\n\t\t\tif (next !== undefined && !next.startsWith(\"@\") && (!next.startsWith(\"-\") || next.startsWith(\"---\"))) {\n\t\t\t\tresult.messages.push(next);\n\t\t\t\ti++;\n\t\t\t}\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--no-context-files\" || arg === \"-nc\") {\n\t\t\tresult.noContextFiles = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\")) {\n\t\t\tconst eqIndex = arg.indexOf(\"=\");\n\t\t\tif (eqIndex !== -1) {\n\t\t\t\tresult.unknownFlags.set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1));\n\t\t\t} else {\n\t\t\t\tconst flagName = arg.slice(2);\n\t\t\t\tconst next = args[i + 1];\n\t\t\t\tif (next !== undefined && !next.startsWith(\"-\") && !next.startsWith(\"@\")) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, next);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tresult.diagnostics.push({ type: \"error\", message: `Unknown option: ${arg}` });\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(extensionFlags?: ExtensionFlag[]): void {\n\tconst extensionFlagsText =\n\t\textensionFlags && extensionFlags.length > 0\n\t\t\t? `\\n${chalk.bold(\"Extension CLI Flags:\")}\\n${extensionFlags\n\t\t\t\t\t.map((flag) => {\n\t\t\t\t\t\tconst value = flag.type === \"string\" ? \" <value>\" : \"\";\n\t\t\t\t\t\tconst description = flag.description ?? `Registered by ${flag.extensionPath}`;\n\t\t\t\t\t\treturn ` --${flag.name}${value}`.padEnd(30) + description;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")}\\n`\n\t\t\t: \"\";\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] Remove extension source from settings\n ${APP_NAME} uninstall <source> [-l] Alias for remove\n ${APP_NAME} update [source|self|omk] Update OMK and installed extensions\n ${APP_NAME} list List installed extensions from settings\n ${APP_NAME} config Open TUI to enable/disable package resources\n ${APP_NAME} quota Manage CodexBar quota connector and summaries\n ${APP_NAME} <command> --help Show help for install/remove/uninstall/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path|id> Use specific session file or partial UUID\n --session-id <id> Use exact project session ID, creating it if missing\n --fork <path|id> Fork specific session file or partial UUID into a new session\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --name, -n <name> Set session display name\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools, -nt Disable all tools by default (built-in and extension)\n --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled\n --tools, -t <tools> Comma-separated allowlist of tool names to enable\n Applies to built-in, extension, and custom tools\n --exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable\n Applies to built-in, extension, and custom tools\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --no-context-files, -nc Disable AGENTS.md and CLAUDE.md discovery and loading\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --offline Disable startup network operations (same as OMK_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).${extensionFlagsText}\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Start a named session\n ${APP_NAME} --name \"Refactor auth module\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Disable one tool while keeping the rest available\n ${APP_NAME} --exclude-tools ask_question\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n ANT_LING_API_KEY - Ant Ling API key\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI/Cognitive Services base URL (e.g. https://{resource}.openai.azure.com)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n DEEPSEEK_API_KEY - DeepSeek API key\n NVIDIA_API_KEY - NVIDIA NIM API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n FIREWORKS_API_KEY - Fireworks API key\n TOGETHER_API_KEY - Together AI API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n ZAI_CODING_CN_API_KEY - ZAI Coding Plan API key (China)\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n MOONSHOT_API_KEY - Moonshot AI API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway)\n CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both)\n CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway)\n XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing)\n XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region)\n XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region)\n XIAOMI_TOKEN_PLAN_SGP_API_KEY - Xiaomi MiMo Token Plan API key (Singapore region)\n ZYLOO_API_KEY - Zyloo API key\n AWS_PROFILE - AWS profile for Amazon Bedrock\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Config directory (default: ~/${CONFIG_DIR_NAME}/agent)\n ${ENV_SESSION_DIR.padEnd(32)} - Session storage directory (overridden by --session-dir)\n OMK_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n OMK_OFFLINE - Disable startup network operations when set to 1/true/yes\n OMK_TELEMETRY - Override install telemetry when set to 1/true/yes or 0/false/no\n OMK_SHARE_VIEWER_URL - Base URL for /share command (default: https://omk.dev/session/)\n\n${chalk.bold(\"Built-in Tool Names:\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CLI argument parsing and help display\n * allow: SIZE_OK - legacy CLI parser; this change only extends existing typed flags.\n */\n\nimport chalk from \"chalk\";\nimport type { ThinkingLevel } from \"omk-agent-core\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from \"../config.ts\";\nimport type { ExtensionFlag } from \"../core/extensions/types.ts\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tname?: string;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionId?: string;\n\tfork?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: string[];\n\texcludeTools?: string[];\n\tnoTools?: boolean;\n\tnoBuiltinTools?: boolean;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n\tdiagnostics: Array<{ type: \"warning\" | \"error\"; message: string }>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultra\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t\tdiagnostics: [],\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = result.appendSystemPrompt ?? [];\n\t\t\tresult.appendSystemPrompt.push(args[++i]);\n\t\t} else if (arg === \"--name\" || arg === \"-n\") {\n\t\t\tif (i + 1 < args.length) {\n\t\t\t\tresult.name = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({ type: \"error\", message: \"--name requires a value\" });\n\t\t\t}\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-id\" && i + 1 < args.length) {\n\t\t\tresult.sessionId = args[++i];\n\t\t} else if (arg === \"--fork\" && i + 1 < args.length) {\n\t\t\tresult.fork = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\" || arg === \"-nt\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--no-builtin-tools\" || arg === \"-nbt\") {\n\t\t\tresult.noBuiltinTools = true;\n\t\t} else if ((arg === \"--tools\" || arg === \"-t\") && i + 1 < args.length) {\n\t\t\tresult.tools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if ((arg === \"--exclude-tools\" || arg === \"-xt\") && i + 1 < args.length) {\n\t\t\tresult.excludeTools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t\tconst next = args[i + 1];\n\t\t\tif (next !== undefined && !next.startsWith(\"@\") && (!next.startsWith(\"-\") || next.startsWith(\"---\"))) {\n\t\t\t\tresult.messages.push(next);\n\t\t\t\ti++;\n\t\t\t}\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--no-context-files\" || arg === \"-nc\") {\n\t\t\tresult.noContextFiles = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\")) {\n\t\t\tconst eqIndex = arg.indexOf(\"=\");\n\t\t\tif (eqIndex !== -1) {\n\t\t\t\tresult.unknownFlags.set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1));\n\t\t\t} else {\n\t\t\t\tconst flagName = arg.slice(2);\n\t\t\t\tconst next = args[i + 1];\n\t\t\t\tif (next !== undefined && !next.startsWith(\"-\") && !next.startsWith(\"@\")) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, next);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tresult.diagnostics.push({ type: \"error\", message: `Unknown option: ${arg}` });\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(extensionFlags?: ExtensionFlag[]): void {\n\tconst extensionFlagsText =\n\t\textensionFlags && extensionFlags.length > 0\n\t\t\t? `\\n${chalk.bold(\"Extension CLI Flags:\")}\\n${extensionFlags\n\t\t\t\t\t.map((flag) => {\n\t\t\t\t\t\tconst value = flag.type === \"string\" ? \" <value>\" : \"\";\n\t\t\t\t\t\tconst description = flag.description ?? `Registered by ${flag.extensionPath}`;\n\t\t\t\t\t\treturn ` --${flag.name}${value}`.padEnd(30) + description;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")}\\n`\n\t\t\t: \"\";\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] Remove extension source from settings\n ${APP_NAME} uninstall <source> [-l] Alias for remove\n ${APP_NAME} update [source|self|omk] Update OMK and installed extensions\n ${APP_NAME} list List installed extensions from settings\n ${APP_NAME} config Open TUI to enable/disable package resources\n ${APP_NAME} quota Manage CodexBar quota connector and summaries\n ${APP_NAME} <command> --help Show help for install/remove/uninstall/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path|id> Use specific session file or partial UUID\n --session-id <id> Use exact project session ID, creating it if missing\n --fork <path|id> Fork specific session file or partial UUID into a new session\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --name, -n <name> Set session display name\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools, -nt Disable all tools by default (built-in and extension)\n --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled\n --tools, -t <tools> Comma-separated allowlist of tool names to enable\n Applies to built-in, extension, and custom tools\n --exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable\n Applies to built-in, extension, and custom tools\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --no-context-files, -nc Disable AGENTS.md and CLAUDE.md discovery and loading\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --offline Disable startup network operations (same as OMK_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).${extensionFlagsText}\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Start a named session\n ${APP_NAME} --name \"Refactor auth module\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Disable one tool while keeping the rest available\n ${APP_NAME} --exclude-tools ask_question\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n ANT_LING_API_KEY - Ant Ling API key\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI/Cognitive Services base URL (e.g. https://{resource}.openai.azure.com)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n DEEPSEEK_API_KEY - DeepSeek API key\n NVIDIA_API_KEY - NVIDIA NIM API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n FIREWORKS_API_KEY - Fireworks API key\n TOGETHER_API_KEY - Together AI API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n ZAI_CODING_CN_API_KEY - ZAI Coding Plan API key (China)\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n MOONSHOT_API_KEY - Moonshot AI API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway)\n CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both)\n CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway)\n XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing)\n XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region)\n XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region)\n XIAOMI_TOKEN_PLAN_SGP_API_KEY - Xiaomi MiMo Token Plan API key (Singapore region)\n ZYLOO_API_KEY - Zyloo API key\n AWS_PROFILE - AWS profile for Amazon Bedrock\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Config directory (default: ~/${CONFIG_DIR_NAME}/agent)\n ${ENV_SESSION_DIR.padEnd(32)} - Session storage directory (overridden by --session-dir)\n OMK_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n OMK_OFFLINE - Disable startup network operations when set to 1/true/yes\n OMK_TELEMETRY - Override install telemetry when set to 1/true/yes or 0/false/no\n OMK_SHARE_VIEWER_URL - Base URL for /share command (default: https://omk.dev/session/)\n\n${chalk.bold(\"Built-in Tool Names:\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"command-safety.d.ts","sourceRoot":"","sources":["../../src/core/command-safety.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AA+nBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAEpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9D","sourcesContent":["// allow: SIZE_OK - legacy safety rules table; this change only preserves existing typed behavior.\nexport type CommandRisk = \"block\" | \"confirm\" | \"allow\";\n\nexport interface CommandVerdict {\n\trisk: CommandRisk;\n\trule: string;\n\treason: string;\n}\n\ninterface CommandPrefixResult {\n\ttokens: string[];\n\tprivilege: string | undefined;\n}\n\nconst RISK_RANK: Readonly<Record<CommandRisk, number>> = {\n\tallow: 0,\n\tconfirm: 1,\n\tblock: 2,\n};\n\nconst PRIVILEGE_COMMANDS = new Set([\"sudo\", \"su\", \"doas\", \"pkexec\"]);\nconst SUDO_OPTIONS_WITH_VALUE = new Set([\n\t\"-C\",\n\t\"--close-from\",\n\t\"-g\",\n\t\"--group\",\n\t\"-h\",\n\t\"--host\",\n\t\"-p\",\n\t\"--prompt\",\n\t\"-T\",\n\t\"--command-timeout\",\n\t\"-u\",\n\t\"--user\",\n]);\nconst GIT_GLOBAL_OPTIONS_WITH_VALUE = new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]);\n\n/** Maximum recursion depth when unwrapping shell wrappers (bash -c, eval, xargs, find -exec). */\nconst MAX_WRAP_DEPTH = 6;\n\n/** Executables that run an inline command string passed via a `-c`-style flag. */\nconst SHELL_WRAPPERS = new Set([\"bash\", \"sh\", \"zsh\", \"dash\", \"ksh\", \"ash\"]);\n\n/**\n * Script interpreters that execute an inline program string (via a -c/-e/-r\n * style flag). Running inline code can execute arbitrary shell commands, so a\n * matched destructive/exec keyword inside the program escalates the call to\n * confirm-tier. A plain script-file path stays allow-tier.\n */\nconst INTERPRETER_INLINE_FLAGS: Readonly<Record<string, readonly string[]>> = {\n\tpython: [\"-c\"],\n\tpython2: [\"-c\"],\n\tpython3: [\"-c\"],\n\tnode: [\"-e\", \"--eval\", \"-p\", \"--print\"],\n\tperl: [\"-e\", \"-E\"],\n\truby: [\"-e\", \"-E\"],\n\tphp: [\"-r\", \"--run\"],\n\tlua: [\"-e\"],\n\texpect: [\"-c\"],\n};\n\n/**\n * Patterns that indicate an inline program invokes a shell command or\n * performs a destructive filesystem operation. Matching any of these inside\n * a `-c`/`-e`/`-r` program string escalates the call to confirm-tier.\n */\nconst INLINE_DESTRUCTIVE_PATTERNS: readonly RegExp[] = [\n\t/\\bos\\.system\\s*\\(/,\n\t/\\bos\\.remove\\s*\\(/,\n\t/\\bos\\.unlink\\s*\\(/,\n\t/\\bshutil\\.rmtree\\s*\\(/,\n\t/\\bsubprocess\\.(?:call|run|check_call|check_output|Popen)\\s*\\(/,\n\t/\\bexec(?:Sync|File)?\\s*\\(/,\n\t/\\bspawn(?:Sync)?\\s*\\(/,\n\t/\\bchild_process\\b/,\n\t/\\brequire\\s*\\(\\s*['\"]child_process['\"]\\s*\\)/,\n\t/\\b(?:system|popen)\\s*\\(/,\n\t/`[^`]*\\brm\\s+-/m,\n\t/\\$\\(\\s*[^)]*\\brm\\s+-/,\n];\n\n/** xargs options that consume a following value token before the wrapped command begins. */\nconst XARGS_OPTIONS_WITH_VALUE = new Set([\n\t\"-I\",\n\t\"-i\",\n\t\"-n\",\n\t\"-L\",\n\t\"-P\",\n\t\"-d\",\n\t\"-E\",\n\t\"-s\",\n\t\"-a\",\n\t\"--max-args\",\n\t\"--max-procs\",\n\t\"--delimiter\",\n\t\"--arg-file\",\n\t\"--max-lines\",\n\t\"--replace\",\n]);\n\n/**\n * Credential / secret file patterns. A bash argv token matching one of these is\n * treated as a `confirm`-tier access: headless callers (LLM tool calls, RPC bash)\n * deny by default, while an interactive user can still approve reading their own\n * files. These complement the §0.1 freedom safety floor, which hard-denies the\n * same paths on the entry points it guards.\n */\nconst SECRET_FILE_STRICT_PATTERNS: readonly RegExp[] = [\n\t/(^|\\/)\\.env(\\.|$)/i,\n\t/(^|\\/)\\.npmrc$/i,\n\t/(^|\\/)\\.netrc$/i,\n\t/(^|\\/)\\.pgpass$/i,\n\t/(^|\\/)\\.aws\\/credentials$/i,\n\t/(^|\\/)credentials$/i,\n\t/(^|\\/)auth\\.json$/i,\n\t/(^|\\/)id_(rsa|dsa|ecdsa|ed25519)$/i,\n\t/\\.(pem|key|p12|pfx|keystore|jks)$/i,\n\t/(^|\\/)secrets?(\\.[^/]+)?$/i,\n];\n\n/** Benign sibling files that look secret-ish but never hold credentials. */\nconst SECRET_FILE_ALLOW_PATTERNS: readonly RegExp[] = [/(^|\\/)\\.env\\.(example|sample|template|dist|defaults?)$/i];\nconst HOME_VARIABLE_RM_TARGETS = new Set([\"$home\", \"$\" + \"{home}\", \"$home/\", \"$\" + \"{home}/\"]);\n\nfunction verdict(risk: CommandRisk, rule: string, reason: string): CommandVerdict {\n\treturn { risk, rule, reason };\n}\n\nfunction allowVerdict(): CommandVerdict {\n\treturn verdict(\"allow\", \"command.allow\", \"No destructive or protected command pattern matched.\");\n}\n\nfunction isEnvAssignment(token: string | undefined): boolean {\n\treturn token !== undefined && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token);\n}\n\nfunction tokenizeShellSegment(segment: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (const character of segment.trim()) {\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tif (character === quote) {\n\t\t\t\tquote = undefined;\n\t\t\t} else {\n\t\t\t\tcurrent += character;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(character)) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (escaped) current += \"\\\\\";\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n\nfunction splitShellCommands(command: string): string[] {\n\tconst segments: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (let index = 0; index < command.length; index += 1) {\n\t\tconst character = command[index];\n\t\tconst next = command[index + 1];\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tcurrent += character;\n\t\t\tif (character === quote) quote = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \";\" || character === \"|\" || character === \"\\n\" || character === \"\\r\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"&\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tif (next === \"&\") index += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (current.trim()) segments.push(current.trim());\n\treturn segments;\n}\n\nfunction skipEnvironmentPrefix(tokens: readonly string[], startIndex: number): number {\n\tlet index = startIndex;\n\twhile (isEnvAssignment(tokens[index])) index += 1;\n\tif (tokens[index]?.toLowerCase() !== \"env\") return index;\n\n\tindex += 1;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (isEnvAssignment(token)) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token?.startsWith(\"-\")) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn index;\n}\n\nfunction optionConsumesValue(option: string, optionsWithValue: ReadonlySet<string>): boolean {\n\tif (option.includes(\"=\")) return false;\n\treturn optionsWithValue.has(option);\n}\n\nfunction skipPrivilegeOptions(tokens: readonly string[], startIndex: number, privilege: string): number {\n\tif (privilege !== \"sudo\" && privilege !== \"doas\") return startIndex;\n\n\tlet index = startIndex;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (!token?.startsWith(\"-\")) break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (optionConsumesValue(token, SUDO_OPTIONS_WITH_VALUE)) index += 1;\n\t}\n\treturn index;\n}\n\nfunction stripCommandPrefixes(tokens: readonly string[]): CommandPrefixResult {\n\tlet index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) {\n\t\treturn { tokens: tokens.slice(index), privilege: undefined };\n\t}\n\n\tindex += 1;\n\tindex = skipPrivilegeOptions(tokens, index, privilege ?? \"\");\n\tindex = skipEnvironmentPrefix(tokens, index);\n\treturn { tokens: tokens.slice(index), privilege };\n}\n\nfunction hasForkBomb(command: string): boolean {\n\treturn command.replace(/\\s/g, \"\").includes(\":(){:|:&};:\");\n}\n\nfunction normalizePathTarget(target: string): string {\n\treturn target.replace(/\\/+$/g, (match) => (target === match ? \"/\" : \"\")).replace(/\\/+/g, \"/\");\n}\n\nfunction classifyRmTarget(target: string): CommandVerdict | null {\n\tconst normalized = normalizePathTarget(target);\n\tif (normalized === \"/\" || normalized === \"/*\" || normalized === \"/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_root\", \"Recursive forced rm targets filesystem root.\");\n\t}\n\tif (normalized === \"~\" || normalized === \"~/\" || normalized === \"~/*\" || normalized === \"~/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_home\", \"Recursive forced rm targets the home directory.\");\n\t}\n\t// $HOME / ${HOME} expands to the user home directory at runtime.\n\tconst lower = normalized.toLowerCase();\n\tif (HOME_VARIABLE_RM_TARGETS.has(lower)) {\n\t\treturn verdict(\n\t\t\t\"confirm\",\n\t\t\t\"fs.rm_rf_home_var\",\n\t\t\t\"Recursive forced rm targets $HOME, which expands to the user home directory.\",\n\t\t);\n\t}\n\t// ~username targets another user home directory.\n\tif (/^~[A-Za-z0-9._-]/.test(normalized)) {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_user_home\", \"Recursive forced rm targets another user home directory.\");\n\t}\n\t// \".\" or \"./\" — current working directory (project root deletion risk).\n\tif (normalized === \".\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_cwd\", \"Recursive forced rm targets the current working directory.\");\n\t}\n\t// \"..\" — parent directory.\n\tif (normalized === \"..\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_parent\", \"Recursive forced rm targets the parent directory.\");\n\t}\n\treturn null;\n}\n\nfunction classifyRm(tokens: readonly string[]): CommandVerdict | null {\n\tlet hasRecursive = false;\n\tlet hasForce = false;\n\tlet endOfOptions = false;\n\tconst targets: string[] = [];\n\n\tfor (const token of tokens.slice(1)) {\n\t\tif (!endOfOptions && token === \"--\") {\n\t\t\tendOfOptions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!endOfOptions && token.startsWith(\"-\") && token !== \"-\") {\n\t\t\tif (token === \"--recursive\") hasRecursive = true;\n\t\t\tif (token === \"--force\") hasForce = true;\n\t\t\tif (!token.startsWith(\"--\")) {\n\t\t\t\tconst flagCharacters = token.slice(1).split(\"\");\n\t\t\t\tif (flagCharacters.some((flag) => flag === \"r\" || flag === \"R\")) hasRecursive = true;\n\t\t\t\tif (flagCharacters.includes(\"f\")) hasForce = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\ttargets.push(token);\n\t}\n\n\tif (!hasRecursive || !hasForce) return null;\n\tfor (const target of targets) {\n\t\tconst targetVerdict = classifyRmTarget(target);\n\t\tif (targetVerdict) return targetVerdict;\n\t}\n\treturn null;\n}\n\nfunction isBlockDeviceOutput(token: string): boolean {\n\treturn /^of=\\/dev\\/(?:sd[a-z][a-z0-9]*|nvme[0-9a-z]+|disk(?:[0-9].*|\\/.*|$))/i.test(token);\n}\n\nfunction classifyDestructiveFilesystemCommand(command: string): CommandVerdict | null {\n\tif (hasForkBomb(command)) {\n\t\treturn verdict(\"block\", \"process.fork_bomb\", \"Shell fork bomb pattern would exhaust process resources.\");\n\t}\n\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\tif (executable === \"rm\") return classifyRm(tokens);\n\tif (executable.startsWith(\"mkfs\")) {\n\t\treturn verdict(\"block\", \"fs.mkfs\", \"Filesystem formatting commands are blocked.\");\n\t}\n\tif (executable === \"dd\" && tokens.slice(1).some(isBlockDeviceOutput)) {\n\t\treturn verdict(\"block\", \"fs.dd_block_device\", \"dd writes directly to a block-device output path.\");\n\t}\n\treturn null;\n}\n\nfunction findGitCommand(tokens: readonly string[]): { command: string; args: string[]; originalArgs: string[] } | null {\n\tif (tokens[0]?.toLowerCase() !== \"git\") return null;\n\tlet index = 1;\n\twhile (index < tokens.length && tokens[index]?.startsWith(\"-\")) {\n\t\tconst option = tokens[index];\n\t\tconst normalizedOption = option.toLowerCase();\n\t\tindex += 1;\n\t\tif (option === \"--\") break;\n\t\tif (\n\t\t\toptionConsumesValue(option, GIT_GLOBAL_OPTIONS_WITH_VALUE) ||\n\t\t\toptionConsumesValue(normalizedOption, GIT_GLOBAL_OPTIONS_WITH_VALUE)\n\t\t) {\n\t\t\tindex += 1;\n\t\t}\n\t}\n\tconst command = tokens[index]?.toLowerCase();\n\tif (!command) return null;\n\tconst originalArgs = tokens.slice(index + 1);\n\treturn {\n\t\tcommand,\n\t\targs: originalArgs.map((argument) => argument.toLowerCase()),\n\t\toriginalArgs,\n\t};\n}\n\nfunction hasGitCleanForceDirectory(args: readonly string[]): boolean {\n\tlet hasForce = false;\n\tlet hasDirectory = false;\n\tfor (const arg of args) {\n\t\tif (arg === \"--force\") hasForce = true;\n\t\tif (arg === \"-f\") hasForce = true;\n\t\tif (arg === \"-d\") hasDirectory = true;\n\t\tif (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tconst flags = arg.slice(1).split(\"\");\n\t\t\tif (flags.includes(\"f\")) hasForce = true;\n\t\t\tif (flags.includes(\"d\")) hasDirectory = true;\n\t\t}\n\t}\n\treturn hasForce && hasDirectory;\n}\n\nfunction classifyProtectedGitCommand(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst gitCommand = findGitCommand(tokens);\n\tif (!gitCommand) return null;\n\n\tif (gitCommand.command === \"reset\" && gitCommand.args.includes(\"--hard\")) {\n\t\treturn verdict(\"confirm\", \"git.reset_hard\", \"git reset --hard can discard worktree changes.\");\n\t}\n\tif (gitCommand.command === \"restore\") {\n\t\tconst targetAll = gitCommand.originalArgs.includes(\".\") || gitCommand.originalArgs.includes(\"*\");\n\t\tconst rewritesWorktree = gitCommand.args.includes(\"--worktree\") || gitCommand.args.includes(\"--source\");\n\t\tif (targetAll || rewritesWorktree) {\n\t\t\treturn verdict(\"confirm\", \"git.restore\", \"git restore can discard or overwrite local worktree changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"checkout\" && gitCommand.originalArgs.includes(\".\")) {\n\t\treturn verdict(\"confirm\", \"git.checkout_dot\", \"git checkout . can overwrite local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"clean\" && hasGitCleanForceDirectory(gitCommand.args)) {\n\t\treturn verdict(\"confirm\", \"git.clean_force\", \"git clean -fd can delete untracked files and directories.\");\n\t}\n\tif (gitCommand.command === \"stash\" && gitCommand.originalArgs.length === 0) {\n\t\treturn verdict(\"confirm\", \"git.stash_bare\", \"Bare git stash can hide local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"add\") {\n\t\tif (gitCommand.originalArgs.includes(\".\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_dot\", \"git add . can stage unrelated local changes.\");\n\t\t}\n\t\tif (gitCommand.originalArgs.includes(\"-A\") || gitCommand.args.includes(\"--all\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_all\", \"git add -A can stage unrelated local changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"commit\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.no_verify\", \"git commit --no-verify bypasses repository verification hooks.\");\n\t}\n\tif (gitCommand.command === \"push\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.push_no_verify\", \"git push --no-verify bypasses remote verification hooks.\");\n\t}\n\tif (\n\t\tgitCommand.command === \"push\" &&\n\t\t(gitCommand.originalArgs.includes(\"-f\") ||\n\t\t\tgitCommand.args.includes(\"--force\") ||\n\t\t\tgitCommand.args.includes(\"--force-with-lease\"))\n\t) {\n\t\treturn verdict(\"confirm\", \"git.force_push\", \"Force-pushing can rewrite remote history.\");\n\t}\n\treturn null;\n}\n\nfunction classifyPrivilegeCommand(command: string): CommandVerdict | null {\n\tconst tokens = tokenizeShellSegment(command);\n\tconst index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) return null;\n\treturn verdict(\"confirm\", `priv.${privilege}`, `${privilege} requires explicit per-command confirmation.`);\n}\n\n/**\n * Locate the inline command-string argument for a `shell -c \"<cmd>\"` invocation.\n * Returns the token index of the command string, or -1 when the shell runs a\n * script file (no inline command to classify).\n */\nfunction findShellCommandStringIndex(tokens: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (/^-[a-z]*c$/i.test(token)) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Locate the inline program-string argument for an interpreter invocation\n * (`python -c`, `node -e`, `perl -e`, ...). Returns the token index of the\n * program string, or -1 when the interpreter runs a script file (no inline\n * program to classify).\n */\nfunction findInterpreterInlineIndex(tokens: readonly string[], flags: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (token === \"--\") return -1;\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (flags.includes(token) || flags.includes(token.toLowerCase())) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/** Drop leading xargs options (and their values) to expose the wrapped command. */\nfunction stripXargsOptions(args: readonly string[]): string[] {\n\tlet index = 0;\n\twhile (index < args.length) {\n\t\tconst token = args[index];\n\t\tif (!token.startsWith(\"-\") || token === \"-\") break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (!token.includes(\"=\") && XARGS_OPTIONS_WITH_VALUE.has(token)) index += 1;\n\t}\n\treturn args.slice(index);\n}\n\n/** Extract the command tokens of a `find ... -exec <cmd> {} ;|+` clause, if present. */\nfunction extractFindExec(tokens: readonly string[]): string[] | null {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index].toLowerCase();\n\t\tif (token === \"-exec\" || token === \"-execdir\" || token === \"-ok\" || token === \"-okdir\") {\n\t\t\tconst collected: string[] = [];\n\t\t\tfor (let inner = index + 1; inner < tokens.length; inner += 1) {\n\t\t\t\tconst piece = tokens[inner];\n\t\t\t\tif (piece === \";\" || piece === \"+\") break;\n\t\t\t\tif (piece === \"{}\") continue;\n\t\t\t\tcollected.push(piece);\n\t\t\t}\n\t\t\treturn collected.length > 0 ? collected : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Classify the effective command hidden inside a wrapper executable so a\n * destructive or protected command cannot be smuggled past the classifier via\n * `bash -c`, `eval`, `xargs`, or `find -exec`. Returns null when the segment is\n * not a recognized wrapper.\n */\nfunction classifyWrappedCommand(command: string, depth: number): CommandVerdict | null {\n\tif (depth >= MAX_WRAP_DEPTH) return null;\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\n\tif (SHELL_WRAPPERS.has(executable)) {\n\t\tconst innerIndex = findShellCommandStringIndex(tokens);\n\t\tconst inner = innerIndex === -1 ? undefined : tokens[innerIndex];\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"eval\") {\n\t\tconst inner = tokens.slice(1).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"xargs\") {\n\t\tconst inner = stripXargsOptions(tokens.slice(1)).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"find\") {\n\t\tconst lowered = tokens.map((token) => token.toLowerCase());\n\t\tconst hasDelete = lowered.includes(\"-delete\");\n\t\tconst execTokens = extractFindExec(tokens);\n\t\tconst inner = execTokens?.join(\" \").trim();\n\t\tconst innerVerdict = inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t\tif (hasDelete) {\n\t\t\tconst deleteVerdict = verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t\"fs.find_delete\",\n\t\t\t\t\"find -delete removes matched files and directories.\",\n\t\t\t);\n\t\t\tif (!innerVerdict || RISK_RANK[deleteVerdict.risk] > RISK_RANK[innerVerdict.risk]) {\n\t\t\t\treturn deleteVerdict;\n\t\t\t}\n\t\t}\n\t\treturn innerVerdict ?? null;\n\t}\n\tconst inlineFlags = INTERPRETER_INLINE_FLAGS[executable];\n\tif (inlineFlags) {\n\t\tconst innerIndex = findInterpreterInlineIndex(tokens, inlineFlags);\n\t\tif (innerIndex === -1) return null; // script file path — allow\n\t\tconst program = tokens[innerIndex];\n\t\tif (INLINE_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(program))) {\n\t\t\treturn verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t`interp.${executable}_inline_destructive`,\n\t\t\t\t`${executable} inline program can execute destructive shell commands.`,\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\n/** Flag commands that read, copy, or transmit a credential / secret file path. */\nfunction classifySecretAccess(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tif (tokens.length === 0) return null;\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token || token.startsWith(\"-\")) continue;\n\t\tif (/\\s/.test(token)) continue;\n\t\tif (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token))) continue;\n\t\tif (SECRET_FILE_STRICT_PATTERNS.some((pattern) => pattern.test(token))) {\n\t\t\treturn verdict(\"confirm\", \"secret.read_path\", \"Command references a credential or secret file path.\");\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction classifySingleCommand(command: string, depth: number): CommandVerdict {\n\tconst trimmed = command.trim();\n\tif (!trimmed) return allowVerdict();\n\n\tconst destructiveFilesystem = classifyDestructiveFilesystemCommand(trimmed);\n\tif (destructiveFilesystem) return destructiveFilesystem;\n\n\tconst wrapped = classifyWrappedCommand(trimmed, depth);\n\tif (wrapped && wrapped.risk === \"block\") return wrapped;\n\n\tconst protectedGit = classifyProtectedGitCommand(trimmed);\n\tif (protectedGit) return protectedGit;\n\n\tconst privilege = classifyPrivilegeCommand(trimmed);\n\tif (privilege) return privilege;\n\n\tconst secret = classifySecretAccess(trimmed);\n\tif (secret) return secret;\n\n\tif (wrapped && RISK_RANK[wrapped.risk] > RISK_RANK.allow) return wrapped;\n\n\treturn allowVerdict();\n}\n\nfunction classifyShellCommandInternal(command: string, depth: number): CommandVerdict {\n\tlet selected = classifySingleCommand(command, depth);\n\tfor (const segment of splitShellCommands(command)) {\n\t\tconst candidate = classifySingleCommand(segment, depth);\n\t\tif (RISK_RANK[candidate.risk] > RISK_RANK[selected.risk]) selected = candidate;\n\t\tif (selected.risk === \"block\") break;\n\t}\n\treturn selected;\n}\n\nexport function classifyShellCommand(command: string): CommandVerdict {\n\treturn classifyShellCommandInternal(command, 0);\n}\n\nexport function isDestructiveFilesystem(command: string): boolean {\n\treturn classifyDestructiveFilesystemCommand(command) !== null;\n}\n\nexport function isProtectedGitOperation(command: string): boolean {\n\treturn classifyProtectedGitCommand(command) !== null;\n}\n\nexport function isPrivilegeEscalation(command: string): boolean {\n\treturn classifyPrivilegeCommand(command) !== null;\n}\n"]}
1
+ {"version":3,"file":"command-safety.d.ts","sourceRoot":"","sources":["../../src/core/command-safety.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AAsoBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAEpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9D","sourcesContent":["// allow: SIZE_OK - legacy safety rules table; this change only preserves existing typed behavior.\nexport type CommandRisk = \"block\" | \"confirm\" | \"allow\";\n\nexport interface CommandVerdict {\n\trisk: CommandRisk;\n\trule: string;\n\treason: string;\n}\n\ninterface CommandPrefixResult {\n\ttokens: string[];\n\tprivilege: string | undefined;\n}\n\nconst RISK_RANK: Readonly<Record<CommandRisk, number>> = {\n\tallow: 0,\n\tconfirm: 1,\n\tblock: 2,\n};\n\nconst PRIVILEGE_COMMANDS = new Set([\"sudo\", \"su\", \"doas\", \"pkexec\"]);\nconst SUDO_OPTIONS_WITH_VALUE = new Set([\n\t\"-C\",\n\t\"--close-from\",\n\t\"-g\",\n\t\"--group\",\n\t\"-h\",\n\t\"--host\",\n\t\"-p\",\n\t\"--prompt\",\n\t\"-T\",\n\t\"--command-timeout\",\n\t\"-u\",\n\t\"--user\",\n]);\nconst GIT_GLOBAL_OPTIONS_WITH_VALUE = new Set([\"-C\", \"-c\", \"--git-dir\", \"--work-tree\", \"--namespace\"]);\n\n/** Maximum recursion depth when unwrapping shell wrappers (bash -c, eval, xargs, find -exec). */\nconst MAX_WRAP_DEPTH = 6;\n\n/** Executables that run an inline command string passed via a `-c`-style flag. */\nconst SHELL_WRAPPERS = new Set([\"bash\", \"sh\", \"zsh\", \"dash\", \"ksh\", \"ash\"]);\n\n/**\n * Script interpreters that execute an inline program string (via a -c/-e/-r\n * style flag). Running inline code can execute arbitrary shell commands, so a\n * matched destructive/exec keyword inside the program escalates the call to\n * confirm-tier. A plain script-file path stays allow-tier.\n */\nconst INTERPRETER_INLINE_FLAGS: Readonly<Record<string, readonly string[]>> = {\n\tpython: [\"-c\"],\n\tpython2: [\"-c\"],\n\tpython3: [\"-c\"],\n\tnode: [\"-e\", \"--eval\", \"-p\", \"--print\"],\n\tperl: [\"-e\", \"-E\"],\n\truby: [\"-e\", \"-E\"],\n\tphp: [\"-r\", \"--run\"],\n\tlua: [\"-e\"],\n\texpect: [\"-c\"],\n};\n\n/**\n * Patterns that indicate an inline program invokes a shell command or\n * performs a destructive filesystem operation. Matching any of these inside\n * a `-c`/`-e`/`-r` program string escalates the call to confirm-tier.\n */\nconst INLINE_DESTRUCTIVE_PATTERNS: readonly RegExp[] = [\n\t/\\bos\\.system\\s*\\(/,\n\t/\\bos\\.remove\\s*\\(/,\n\t/\\bos\\.unlink\\s*\\(/,\n\t/\\bshutil\\.rmtree\\s*\\(/,\n\t/\\bsubprocess\\.(?:call|run|check_call|check_output|Popen)\\s*\\(/,\n\t/\\bexec(?:Sync|File)?\\s*\\(/,\n\t/\\bspawn(?:Sync)?\\s*\\(/,\n\t/\\bchild_process\\b/,\n\t/\\brequire\\s*\\(\\s*['\"]child_process['\"]\\s*\\)/,\n\t/\\b(?:system|popen)\\s*\\(/,\n\t/`[^`]*\\brm\\s+-/m,\n\t/\\$\\(\\s*[^)]*\\brm\\s+-/,\n];\n\n/** xargs options that consume a following value token before the wrapped command begins. */\nconst XARGS_OPTIONS_WITH_VALUE = new Set([\n\t\"-I\",\n\t\"-i\",\n\t\"-n\",\n\t\"-L\",\n\t\"-P\",\n\t\"-d\",\n\t\"-E\",\n\t\"-s\",\n\t\"-a\",\n\t\"--max-args\",\n\t\"--max-procs\",\n\t\"--delimiter\",\n\t\"--arg-file\",\n\t\"--max-lines\",\n\t\"--replace\",\n]);\n\n/**\n * Credential / secret file patterns. A bash argv token matching one of these is\n * treated as a `confirm`-tier access: headless callers (LLM tool calls, RPC bash)\n * deny by default, while an interactive user can still approve reading their own\n * files. These complement the §0.1 freedom safety floor, which hard-denies the\n * same paths on the entry points it guards.\n */\nconst SECRET_FILE_STRICT_PATTERNS: readonly RegExp[] = [\n\t/(^|\\/)\\.env(\\.|$)/i,\n\t/(^|\\/)\\.npmrc$/i,\n\t/(^|\\/)\\.netrc$/i,\n\t/(^|\\/)\\.pgpass$/i,\n\t/(^|\\/)\\.aws\\/credentials$/i,\n\t/(^|\\/)credentials$/i,\n\t/(^|\\/)auth\\.json$/i,\n\t/(^|\\/)id_(rsa|dsa|ecdsa|ed25519)$/i,\n\t/\\.(pem|key|p12|pfx|keystore|jks)$/i,\n\t/(^|\\/)secrets?(\\.[^/]+)?$/i,\n];\n\n/** Benign sibling files that look secret-ish but never hold credentials. */\nconst SECRET_FILE_ALLOW_PATTERNS: readonly RegExp[] = [/(^|\\/)\\.env\\.(example|sample|template|dist|defaults?)$/i];\nconst SEARCH_COMMANDS = new Set([\"grep\", \"egrep\", \"fgrep\", \"rg\"]);\nconst HOME_VARIABLE_RM_TARGETS = new Set([\"$home\", \"$\" + \"{home}\", \"$home/\", \"$\" + \"{home}/\"]);\n\nfunction verdict(risk: CommandRisk, rule: string, reason: string): CommandVerdict {\n\treturn { risk, rule, reason };\n}\n\nfunction allowVerdict(): CommandVerdict {\n\treturn verdict(\"allow\", \"command.allow\", \"No destructive or protected command pattern matched.\");\n}\n\nfunction isEnvAssignment(token: string | undefined): boolean {\n\treturn token !== undefined && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token);\n}\n\nfunction tokenizeShellSegment(segment: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (const character of segment.trim()) {\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tif (character === quote) {\n\t\t\t\tquote = undefined;\n\t\t\t} else {\n\t\t\t\tcurrent += character;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(character)) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (escaped) current += \"\\\\\";\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n\nfunction splitShellCommands(command: string): string[] {\n\tconst segments: string[] = [];\n\tlet current = \"\";\n\tlet quote: string | undefined;\n\tlet escaped = false;\n\n\tfor (let index = 0; index < command.length; index += 1) {\n\t\tconst character = command[index];\n\t\tconst next = command[index + 1];\n\t\tif (escaped) {\n\t\t\tcurrent += character;\n\t\t\tescaped = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\" && quote !== \"'\") {\n\t\t\tescaped = true;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote) {\n\t\t\tcurrent += character;\n\t\t\tif (character === quote) quote = undefined;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"'\" || character === '\"') {\n\t\t\tquote = character;\n\t\t\tcurrent += character;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \";\" || character === \"|\" || character === \"\\n\" || character === \"\\r\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"&\") {\n\t\t\tif (current.trim()) segments.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tif (next === \"&\") index += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += character;\n\t}\n\n\tif (current.trim()) segments.push(current.trim());\n\treturn segments;\n}\n\nfunction skipEnvironmentPrefix(tokens: readonly string[], startIndex: number): number {\n\tlet index = startIndex;\n\twhile (isEnvAssignment(tokens[index])) index += 1;\n\tif (tokens[index]?.toLowerCase() !== \"env\") return index;\n\n\tindex += 1;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (isEnvAssignment(token)) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (token?.startsWith(\"-\")) {\n\t\t\tindex += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn index;\n}\n\nfunction optionConsumesValue(option: string, optionsWithValue: ReadonlySet<string>): boolean {\n\tif (option.includes(\"=\")) return false;\n\treturn optionsWithValue.has(option);\n}\n\nfunction skipPrivilegeOptions(tokens: readonly string[], startIndex: number, privilege: string): number {\n\tif (privilege !== \"sudo\" && privilege !== \"doas\") return startIndex;\n\n\tlet index = startIndex;\n\twhile (index < tokens.length) {\n\t\tconst token = tokens[index];\n\t\tif (!token?.startsWith(\"-\")) break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (optionConsumesValue(token, SUDO_OPTIONS_WITH_VALUE)) index += 1;\n\t}\n\treturn index;\n}\n\nfunction stripCommandPrefixes(tokens: readonly string[]): CommandPrefixResult {\n\tlet index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) {\n\t\treturn { tokens: tokens.slice(index), privilege: undefined };\n\t}\n\n\tindex += 1;\n\tindex = skipPrivilegeOptions(tokens, index, privilege ?? \"\");\n\tindex = skipEnvironmentPrefix(tokens, index);\n\treturn { tokens: tokens.slice(index), privilege };\n}\n\nfunction hasForkBomb(command: string): boolean {\n\treturn command.replace(/\\s/g, \"\").includes(\":(){:|:&};:\");\n}\n\nfunction normalizePathTarget(target: string): string {\n\treturn target.replace(/\\/+$/g, (match) => (target === match ? \"/\" : \"\")).replace(/\\/+/g, \"/\");\n}\n\nfunction classifyRmTarget(target: string): CommandVerdict | null {\n\tconst normalized = normalizePathTarget(target);\n\tif (normalized === \"/\" || normalized === \"/*\" || normalized === \"/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_root\", \"Recursive forced rm targets filesystem root.\");\n\t}\n\tif (normalized === \"~\" || normalized === \"~/\" || normalized === \"~/*\" || normalized === \"~/.*\") {\n\t\treturn verdict(\"block\", \"fs.rm_rf_home\", \"Recursive forced rm targets the home directory.\");\n\t}\n\t// $HOME / ${HOME} expands to the user home directory at runtime.\n\tconst lower = normalized.toLowerCase();\n\tif (HOME_VARIABLE_RM_TARGETS.has(lower)) {\n\t\treturn verdict(\n\t\t\t\"confirm\",\n\t\t\t\"fs.rm_rf_home_var\",\n\t\t\t\"Recursive forced rm targets $HOME, which expands to the user home directory.\",\n\t\t);\n\t}\n\t// ~username targets another user home directory.\n\tif (/^~[A-Za-z0-9._-]/.test(normalized)) {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_user_home\", \"Recursive forced rm targets another user home directory.\");\n\t}\n\t// \".\" or \"./\" — current working directory (project root deletion risk).\n\tif (normalized === \".\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_cwd\", \"Recursive forced rm targets the current working directory.\");\n\t}\n\t// \"..\" — parent directory.\n\tif (normalized === \"..\") {\n\t\treturn verdict(\"confirm\", \"fs.rm_rf_parent\", \"Recursive forced rm targets the parent directory.\");\n\t}\n\treturn null;\n}\n\nfunction classifyRm(tokens: readonly string[]): CommandVerdict | null {\n\tlet hasRecursive = false;\n\tlet hasForce = false;\n\tlet endOfOptions = false;\n\tconst targets: string[] = [];\n\n\tfor (const token of tokens.slice(1)) {\n\t\tif (!endOfOptions && token === \"--\") {\n\t\t\tendOfOptions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!endOfOptions && token.startsWith(\"-\") && token !== \"-\") {\n\t\t\tif (token === \"--recursive\") hasRecursive = true;\n\t\t\tif (token === \"--force\") hasForce = true;\n\t\t\tif (!token.startsWith(\"--\")) {\n\t\t\t\tconst flagCharacters = token.slice(1).split(\"\");\n\t\t\t\tif (flagCharacters.some((flag) => flag === \"r\" || flag === \"R\")) hasRecursive = true;\n\t\t\t\tif (flagCharacters.includes(\"f\")) hasForce = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\ttargets.push(token);\n\t}\n\n\tif (!hasRecursive || !hasForce) return null;\n\tfor (const target of targets) {\n\t\tconst targetVerdict = classifyRmTarget(target);\n\t\tif (targetVerdict) return targetVerdict;\n\t}\n\treturn null;\n}\n\nfunction isBlockDeviceOutput(token: string): boolean {\n\treturn /^of=\\/dev\\/(?:sd[a-z][a-z0-9]*|nvme[0-9a-z]+|disk(?:[0-9].*|\\/.*|$))/i.test(token);\n}\n\nfunction classifyDestructiveFilesystemCommand(command: string): CommandVerdict | null {\n\tif (hasForkBomb(command)) {\n\t\treturn verdict(\"block\", \"process.fork_bomb\", \"Shell fork bomb pattern would exhaust process resources.\");\n\t}\n\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\tif (executable === \"rm\") return classifyRm(tokens);\n\tif (executable.startsWith(\"mkfs\")) {\n\t\treturn verdict(\"block\", \"fs.mkfs\", \"Filesystem formatting commands are blocked.\");\n\t}\n\tif (executable === \"dd\" && tokens.slice(1).some(isBlockDeviceOutput)) {\n\t\treturn verdict(\"block\", \"fs.dd_block_device\", \"dd writes directly to a block-device output path.\");\n\t}\n\treturn null;\n}\n\nfunction findGitCommand(tokens: readonly string[]): { command: string; args: string[]; originalArgs: string[] } | null {\n\tif (tokens[0]?.toLowerCase() !== \"git\") return null;\n\tlet index = 1;\n\twhile (index < tokens.length && tokens[index]?.startsWith(\"-\")) {\n\t\tconst option = tokens[index];\n\t\tconst normalizedOption = option.toLowerCase();\n\t\tindex += 1;\n\t\tif (option === \"--\") break;\n\t\tif (\n\t\t\toptionConsumesValue(option, GIT_GLOBAL_OPTIONS_WITH_VALUE) ||\n\t\t\toptionConsumesValue(normalizedOption, GIT_GLOBAL_OPTIONS_WITH_VALUE)\n\t\t) {\n\t\t\tindex += 1;\n\t\t}\n\t}\n\tconst command = tokens[index]?.toLowerCase();\n\tif (!command) return null;\n\tconst originalArgs = tokens.slice(index + 1);\n\treturn {\n\t\tcommand,\n\t\targs: originalArgs.map((argument) => argument.toLowerCase()),\n\t\toriginalArgs,\n\t};\n}\n\nfunction hasGitCleanForceDirectory(args: readonly string[]): boolean {\n\tlet hasForce = false;\n\tlet hasDirectory = false;\n\tfor (const arg of args) {\n\t\tif (arg === \"--force\") hasForce = true;\n\t\tif (arg === \"-f\") hasForce = true;\n\t\tif (arg === \"-d\") hasDirectory = true;\n\t\tif (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tconst flags = arg.slice(1).split(\"\");\n\t\t\tif (flags.includes(\"f\")) hasForce = true;\n\t\t\tif (flags.includes(\"d\")) hasDirectory = true;\n\t\t}\n\t}\n\treturn hasForce && hasDirectory;\n}\n\nfunction classifyProtectedGitCommand(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst gitCommand = findGitCommand(tokens);\n\tif (!gitCommand) return null;\n\n\tif (gitCommand.command === \"reset\" && gitCommand.args.includes(\"--hard\")) {\n\t\treturn verdict(\"confirm\", \"git.reset_hard\", \"git reset --hard can discard worktree changes.\");\n\t}\n\tif (gitCommand.command === \"restore\") {\n\t\tconst targetAll = gitCommand.originalArgs.includes(\".\") || gitCommand.originalArgs.includes(\"*\");\n\t\tconst rewritesWorktree = gitCommand.args.includes(\"--worktree\") || gitCommand.args.includes(\"--source\");\n\t\tif (targetAll || rewritesWorktree) {\n\t\t\treturn verdict(\"confirm\", \"git.restore\", \"git restore can discard or overwrite local worktree changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"checkout\" && gitCommand.originalArgs.includes(\".\")) {\n\t\treturn verdict(\"confirm\", \"git.checkout_dot\", \"git checkout . can overwrite local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"clean\" && hasGitCleanForceDirectory(gitCommand.args)) {\n\t\treturn verdict(\"confirm\", \"git.clean_force\", \"git clean -fd can delete untracked files and directories.\");\n\t}\n\tif (gitCommand.command === \"stash\" && gitCommand.originalArgs.length === 0) {\n\t\treturn verdict(\"confirm\", \"git.stash_bare\", \"Bare git stash can hide local worktree changes.\");\n\t}\n\tif (gitCommand.command === \"add\") {\n\t\tif (gitCommand.originalArgs.includes(\".\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_dot\", \"git add . can stage unrelated local changes.\");\n\t\t}\n\t\tif (gitCommand.originalArgs.includes(\"-A\") || gitCommand.args.includes(\"--all\")) {\n\t\t\treturn verdict(\"confirm\", \"git.add_all\", \"git add -A can stage unrelated local changes.\");\n\t\t}\n\t}\n\tif (gitCommand.command === \"commit\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.no_verify\", \"git commit --no-verify bypasses repository verification hooks.\");\n\t}\n\tif (gitCommand.command === \"push\" && gitCommand.args.includes(\"--no-verify\")) {\n\t\treturn verdict(\"confirm\", \"git.push_no_verify\", \"git push --no-verify bypasses remote verification hooks.\");\n\t}\n\tif (\n\t\tgitCommand.command === \"push\" &&\n\t\t(gitCommand.originalArgs.includes(\"-f\") ||\n\t\t\tgitCommand.args.includes(\"--force\") ||\n\t\t\tgitCommand.args.includes(\"--force-with-lease\"))\n\t) {\n\t\treturn verdict(\"confirm\", \"git.force_push\", \"Force-pushing can rewrite remote history.\");\n\t}\n\treturn null;\n}\n\nfunction classifyPrivilegeCommand(command: string): CommandVerdict | null {\n\tconst tokens = tokenizeShellSegment(command);\n\tconst index = skipEnvironmentPrefix(tokens, 0);\n\tconst privilege = tokens[index]?.toLowerCase();\n\tif (!PRIVILEGE_COMMANDS.has(privilege ?? \"\")) return null;\n\treturn verdict(\"confirm\", `priv.${privilege}`, `${privilege} requires explicit per-command confirmation.`);\n}\n\n/**\n * Locate the inline command-string argument for a `shell -c \"<cmd>\"` invocation.\n * Returns the token index of the command string, or -1 when the shell runs a\n * script file (no inline command to classify).\n */\nfunction findShellCommandStringIndex(tokens: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (/^-[a-z]*c$/i.test(token)) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Locate the inline program-string argument for an interpreter invocation\n * (`python -c`, `node -e`, `perl -e`, ...). Returns the token index of the\n * program string, or -1 when the interpreter runs a script file (no inline\n * program to classify).\n */\nfunction findInterpreterInlineIndex(tokens: readonly string[], flags: readonly string[]): number {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (token === \"--\") return -1;\n\t\tif (!token.startsWith(\"-\")) return -1;\n\t\tif (flags.includes(token) || flags.includes(token.toLowerCase())) {\n\t\t\treturn index + 1 < tokens.length ? index + 1 : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/** Drop leading xargs options (and their values) to expose the wrapped command. */\nfunction stripXargsOptions(args: readonly string[]): string[] {\n\tlet index = 0;\n\twhile (index < args.length) {\n\t\tconst token = args[index];\n\t\tif (!token.startsWith(\"-\") || token === \"-\") break;\n\t\tindex += 1;\n\t\tif (token === \"--\") break;\n\t\tif (!token.includes(\"=\") && XARGS_OPTIONS_WITH_VALUE.has(token)) index += 1;\n\t}\n\treturn args.slice(index);\n}\n\n/** Extract the command tokens of a `find ... -exec <cmd> {} ;|+` clause, if present. */\nfunction extractFindExec(tokens: readonly string[]): string[] | null {\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index].toLowerCase();\n\t\tif (token === \"-exec\" || token === \"-execdir\" || token === \"-ok\" || token === \"-okdir\") {\n\t\t\tconst collected: string[] = [];\n\t\t\tfor (let inner = index + 1; inner < tokens.length; inner += 1) {\n\t\t\t\tconst piece = tokens[inner];\n\t\t\t\tif (piece === \";\" || piece === \"+\") break;\n\t\t\t\tif (piece === \"{}\") continue;\n\t\t\t\tcollected.push(piece);\n\t\t\t}\n\t\t\treturn collected.length > 0 ? collected : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Classify the effective command hidden inside a wrapper executable so a\n * destructive or protected command cannot be smuggled past the classifier via\n * `bash -c`, `eval`, `xargs`, or `find -exec`. Returns null when the segment is\n * not a recognized wrapper.\n */\nfunction classifyWrappedCommand(command: string, depth: number): CommandVerdict | null {\n\tif (depth >= MAX_WRAP_DEPTH) return null;\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tconst executable = tokens[0]?.toLowerCase();\n\tif (!executable) return null;\n\n\tif (SHELL_WRAPPERS.has(executable)) {\n\t\tconst innerIndex = findShellCommandStringIndex(tokens);\n\t\tconst inner = innerIndex === -1 ? undefined : tokens[innerIndex];\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"eval\") {\n\t\tconst inner = tokens.slice(1).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"xargs\") {\n\t\tconst inner = stripXargsOptions(tokens.slice(1)).join(\" \").trim();\n\t\treturn inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t}\n\tif (executable === \"find\") {\n\t\tconst lowered = tokens.map((token) => token.toLowerCase());\n\t\tconst hasDelete = lowered.includes(\"-delete\");\n\t\tconst execTokens = extractFindExec(tokens);\n\t\tconst inner = execTokens?.join(\" \").trim();\n\t\tconst innerVerdict = inner ? classifyShellCommandInternal(inner, depth + 1) : null;\n\t\tif (hasDelete) {\n\t\t\tconst deleteVerdict = verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t\"fs.find_delete\",\n\t\t\t\t\"find -delete removes matched files and directories.\",\n\t\t\t);\n\t\t\tif (!innerVerdict || RISK_RANK[deleteVerdict.risk] > RISK_RANK[innerVerdict.risk]) {\n\t\t\t\treturn deleteVerdict;\n\t\t\t}\n\t\t}\n\t\treturn innerVerdict ?? null;\n\t}\n\tconst inlineFlags = INTERPRETER_INLINE_FLAGS[executable];\n\tif (inlineFlags) {\n\t\tconst innerIndex = findInterpreterInlineIndex(tokens, inlineFlags);\n\t\tif (innerIndex === -1) return null; // script file path — allow\n\t\tconst program = tokens[innerIndex];\n\t\tif (INLINE_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(program))) {\n\t\t\treturn verdict(\n\t\t\t\t\"confirm\",\n\t\t\t\t`interp.${executable}_inline_destructive`,\n\t\t\t\t`${executable} inline program can execute destructive shell commands.`,\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\nfunction isSearchPatternArgument(tokens: readonly string[], index: number): boolean {\n\tconst executable = tokens[0]?.toLowerCase();\n\treturn SEARCH_COMMANDS.has(executable ?? \"\") && tokens[index - 1] === \"--\";\n}\n\n/** Flag commands that read, copy, or transmit a credential / secret file path. */\nfunction classifySecretAccess(command: string): CommandVerdict | null {\n\tconst { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));\n\tif (tokens.length === 0) return null;\n\tfor (let index = 1; index < tokens.length; index += 1) {\n\t\tconst token = tokens[index];\n\t\tif (!token || token.startsWith(\"-\")) continue;\n\t\tif (isSearchPatternArgument(tokens, index)) continue;\n\t\tif (/\\s/.test(token)) continue;\n\t\tif (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token))) continue;\n\t\tif (SECRET_FILE_STRICT_PATTERNS.some((pattern) => pattern.test(token))) {\n\t\t\treturn verdict(\"confirm\", \"secret.read_path\", \"Command references a credential or secret file path.\");\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction classifySingleCommand(command: string, depth: number): CommandVerdict {\n\tconst trimmed = command.trim();\n\tif (!trimmed) return allowVerdict();\n\n\tconst destructiveFilesystem = classifyDestructiveFilesystemCommand(trimmed);\n\tif (destructiveFilesystem) return destructiveFilesystem;\n\n\tconst wrapped = classifyWrappedCommand(trimmed, depth);\n\tif (wrapped && wrapped.risk === \"block\") return wrapped;\n\n\tconst protectedGit = classifyProtectedGitCommand(trimmed);\n\tif (protectedGit) return protectedGit;\n\n\tconst privilege = classifyPrivilegeCommand(trimmed);\n\tif (privilege) return privilege;\n\n\tconst secret = classifySecretAccess(trimmed);\n\tif (secret) return secret;\n\n\tif (wrapped && RISK_RANK[wrapped.risk] > RISK_RANK.allow) return wrapped;\n\n\treturn allowVerdict();\n}\n\nfunction classifyShellCommandInternal(command: string, depth: number): CommandVerdict {\n\tlet selected = classifySingleCommand(command, depth);\n\tfor (const segment of splitShellCommands(command)) {\n\t\tconst candidate = classifySingleCommand(segment, depth);\n\t\tif (RISK_RANK[candidate.risk] > RISK_RANK[selected.risk]) selected = candidate;\n\t\tif (selected.risk === \"block\") break;\n\t}\n\treturn selected;\n}\n\nexport function classifyShellCommand(command: string): CommandVerdict {\n\treturn classifyShellCommandInternal(command, 0);\n}\n\nexport function isDestructiveFilesystem(command: string): boolean {\n\treturn classifyDestructiveFilesystemCommand(command) !== null;\n}\n\nexport function isProtectedGitOperation(command: string): boolean {\n\treturn classifyProtectedGitCommand(command) !== null;\n}\n\nexport function isPrivilegeEscalation(command: string): boolean {\n\treturn classifyPrivilegeCommand(command) !== null;\n}\n"]}
@@ -98,6 +98,7 @@ const SECRET_FILE_STRICT_PATTERNS = [
98
98
  ];
99
99
  /** Benign sibling files that look secret-ish but never hold credentials. */
100
100
  const SECRET_FILE_ALLOW_PATTERNS = [/(^|\/)\.env\.(example|sample|template|dist|defaults?)$/i];
101
+ const SEARCH_COMMANDS = new Set(["grep", "egrep", "fgrep", "rg"]);
101
102
  const HOME_VARIABLE_RM_TARGETS = new Set(["$home", "$" + "{home}", "$home/", "$" + "{home}/"]);
102
103
  function verdict(risk, rule, reason) {
103
104
  return { risk, rule, reason };
@@ -563,6 +564,10 @@ function classifyWrappedCommand(command, depth) {
563
564
  }
564
565
  return null;
565
566
  }
567
+ function isSearchPatternArgument(tokens, index) {
568
+ const executable = tokens[0]?.toLowerCase();
569
+ return SEARCH_COMMANDS.has(executable ?? "") && tokens[index - 1] === "--";
570
+ }
566
571
  /** Flag commands that read, copy, or transmit a credential / secret file path. */
567
572
  function classifySecretAccess(command) {
568
573
  const { tokens } = stripCommandPrefixes(tokenizeShellSegment(command));
@@ -572,6 +577,8 @@ function classifySecretAccess(command) {
572
577
  const token = tokens[index];
573
578
  if (!token || token.startsWith("-"))
574
579
  continue;
580
+ if (isSearchPatternArgument(tokens, index))
581
+ continue;
575
582
  if (/\s/.test(token))
576
583
  continue;
577
584
  if (SECRET_FILE_ALLOW_PATTERNS.some((pattern) => pattern.test(token)))