mixdog 0.9.139 → 0.9.141

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 (104) hide show
  1. package/LICENSES/MIT.txt +12 -14
  2. package/NOTICE.md +32 -34
  3. package/package.json +1 -8
  4. package/scripts/manifest-cache-key.mjs +44 -0
  5. package/src/lib/output-style-meta.cjs +115 -0
  6. package/src/lib/rules-builder.cjs +18 -100
  7. package/src/output-styles/common.md +27 -43
  8. package/src/output-styles/detailed.md +5 -5
  9. package/src/output-styles/extreme-minimal.md +7 -7
  10. package/src/output-styles/minimal.md +7 -6
  11. package/src/output-styles/simple.md +6 -5
  12. package/src/rules/lead/01-general.md +3 -0
  13. package/src/rules/shared/10-tool-workflow.md +2 -0
  14. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -1
  15. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +1 -1
  16. package/src/runtime/agent/orchestrator/providers/gemini.mjs +1 -1
  17. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +1 -1
  18. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +3 -3
  19. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +1 -1
  20. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +2 -2
  21. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1 -1
  22. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +3 -3
  23. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +4 -4
  24. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +16 -0
  25. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +8 -0
  26. package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +6 -5
  27. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +5 -0
  28. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  29. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +21 -0
  30. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +7 -7
  31. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +38 -0
  32. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +18 -3
  33. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +55 -0
  35. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +27 -7
  36. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +21 -2
  37. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +62 -31
  38. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +45 -1
  39. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +29 -2
  40. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +5 -1
  41. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -3
  42. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +3 -4
  43. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +2 -3
  44. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +9 -4
  45. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +46 -1
  46. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
  47. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +29 -2
  48. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +7 -2
  49. package/src/runtime/agent/orchestrator/tools/patch/v4a-anchors.mjs +27 -0
  50. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +6 -0
  51. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +10 -10
  52. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +72 -4
  53. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -2
  54. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
  55. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  56. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  57. package/src/runtime/media/download.mjs +1 -1
  58. package/src/runtime/media/jobs.mjs +9 -0
  59. package/src/runtime/media/store.mjs +4 -9
  60. package/src/runtime/memory/lib/embedding-worker.mjs +9 -4
  61. package/src/runtime/memory/lib/http-router.mjs +31 -0
  62. package/src/runtime/memory/lib/ko-morph.mjs +8 -1
  63. package/src/runtime/memory/lib/memory-cycle1.mjs +28 -2
  64. package/src/runtime/shared/background-tasks.mjs +1 -1
  65. package/src/runtime/shared/webhook-session-run.mjs +6 -0
  66. package/src/runtime/shared/webhook-session-run.test.mjs +14 -0
  67. package/src/session-runtime/context-status.mjs +4 -2
  68. package/src/session-runtime/cwd-tool-routing.test.mjs +56 -0
  69. package/src/session-runtime/lifecycle-api.mjs +47 -0
  70. package/src/session-runtime/output-styles.mjs +28 -83
  71. package/src/session-runtime/runtime-core.mjs +14 -3
  72. package/src/session-runtime/session-turn-api.mjs +26 -0
  73. package/src/session-runtime/tool-catalog.mjs +2 -2
  74. package/src/session-runtime/tool-defs.mjs +1 -1
  75. package/src/standalone/agent-tool/shard-spread.mjs +5 -3
  76. package/src/standalone/agent-tool/shard-spread.test.mjs +16 -1
  77. package/src/standalone/session-client.mjs +1 -1
  78. package/src/standalone/session-protocol.mjs +2 -0
  79. package/src/standalone/session-service.mjs +2 -2
  80. package/src/standalone/session-wire.mjs +1 -1
  81. package/src/tui/App.jsx +3 -3
  82. package/src/tui/app/slash-commands.mjs +1 -0
  83. package/src/tui/app/slash-dispatch.mjs +18 -0
  84. package/src/tui/app/transcript-window.mjs +4 -5
  85. package/src/tui/app/use-mouse-input.mjs +1 -1
  86. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  87. package/src/tui/app/use-transcript-scroll.mjs +37 -29
  88. package/src/tui/app/use-transcript-window.mjs +1 -1
  89. package/src/tui/dist/index.mjs +79 -54
  90. package/src/tui/keyboard-protocol.mjs +1 -1
  91. package/src/tui/markdown/stream-fence.mjs +40 -17
  92. package/src/tui/session/labels.mjs +6 -1
  93. package/src/tui/session/session-api-ext.mjs +19 -0
  94. package/src/tui/session/session-api.mjs +1 -1
  95. package/src/tui/session/turn.mjs +20 -0
  96. package/src/tui/themes/light.mjs +4 -4
  97. package/src/tui/themes/teal.mjs +1 -1
  98. package/src/ui/statusline-segments.mjs +25 -6
  99. package/vendor/ink/build/ink.js +8 -86
  100. package/vendor/ink/build/input-parser.js +1 -1
  101. package/vendor/ink/build/output.js +11 -13
  102. package/vendor/ink/build/selection-rect.js +80 -0
  103. package/vendor/ink/build/termio-keypress.js +110 -79
  104. package/vendor/ink/build/termio-tokenize.js +160 -225
package/LICENSES/MIT.txt CHANGED
@@ -8,24 +8,22 @@ component listed here. Each component keeps its own copyright notice.
8
8
  https://github.com/microsoft/vscode
9
9
  Copyright (c) 2015 - present Microsoft Corporation
10
10
 
11
+ Visual Studio Code Codicons (icon font; the icons are CC BY 4.0)
12
+ https://github.com/microsoft/vscode-codicons
13
+ Copyright (c) Microsoft Corporation
14
+
15
+ Octicons
16
+ https://github.com/primer/octicons
17
+ Copyright (c) GitHub, Inc.
18
+
11
19
  GitHub Pull Requests and Issues (VS Code extension)
12
20
  https://github.com/microsoft/vscode-pull-request-github
13
21
  Copyright (c) Microsoft Corporation. All rights reserved.
14
22
 
15
- Files
16
- https://github.com/files-community/Files
17
- Copyright (c) 2018 - present Files Community
18
-
19
- OpenCode
20
- https://github.com/anomalyco/opencode
21
- Copyright (c) 2025 opencode
22
-
23
- pi
24
- https://github.com/earendil-works/pi
25
- Copyright (c) 2025 Mario Zechner
26
-
27
- Orca
28
- Copyright (c) 2026 Lovecast Inc.
23
+ ripgrep (grep/ignore crates plus the packaged rg executable; offered as
24
+ MIT OR Unlicense — the MIT terms are taken here)
25
+ https://github.com/BurntSushi/ripgrep
26
+ Copyright (c) 2015 Andrew Gallant
29
27
 
30
28
  Ink (vendored under vendor/ink, which keeps its own license file)
31
29
  https://github.com/vadimdemedes/ink
package/NOTICE.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # NOTICE
2
2
 
3
- Mixdog itself is MIT-licensed (see `LICENSE`). Parts of it were written
4
- against the public source of the projects listed below. Their terms are
5
- preserved here in one place so the individual source files can stay free of
6
- scattered attribution comments.
3
+ Mixdog itself is MIT-licensed (see `LICENSE`). The sections below cover the
4
+ third-party code and data that Mixdog actually carries, kept here in one place
5
+ so the individual source files stay free of scattered attribution comments.
7
6
 
8
7
  Full license texts live in `LICENSES/`:
9
8
 
@@ -12,6 +11,10 @@ Full license texts live in `LICENSES/`:
12
11
  - `LICENSES/Apache-2.0.txt` — the Apache License 2.0.
13
12
  - `LICENSES/codex-NOTICE.txt` — the upstream NOTICE file of the Codex CLI,
14
13
  carried forward as Apache-2.0 section 4(d) requires.
14
+ - `LICENSES/ripgrep-NOTICE.txt` — the ripgrep notice for the search crates
15
+ compiled into the native binaries.
16
+ - `LICENSES/editor-assets-NOTICE.txt` — the editor icon and language-data
17
+ notice referenced by the generated files.
15
18
 
16
19
  ## MIT
17
20
 
@@ -34,6 +37,23 @@ language contributions used to generate the Monaco pane languages.
34
37
  `editor-ansi.ts` the terminal ANSI palette values, and `seti-icons.ts` /
35
38
  `editor-languages.ts` are generated from the shipped extension data.
36
39
 
40
+ ### Visual Studio Code Codicons — Copyright (c) Microsoft Corporation
41
+
42
+ <https://github.com/microsoft/vscode-codicons>
43
+
44
+ The chrome-level UI glyph font used by the desktop and web renderer. The code
45
+ is MIT; the icons themselves are licensed under Creative Commons Attribution
46
+ 4.0 International (CC BY 4.0).
47
+
48
+ ### Octicons — Copyright (c) GitHub, Inc.
49
+
50
+ <https://github.com/primer/octicons>
51
+
52
+ The five changed-file status glyphs in
53
+ `apps/desktop/src/renderer/ScmStatusIcon.tsx` carry Octicons 16px path data
54
+ unmodified: `diffAdded`, `diffModified`, `diffRemoved`, `diffRenamed` and
55
+ `alert`.
56
+
37
57
  ### GitHub Pull Requests extension — Copyright (c) Microsoft Corporation
38
58
 
39
59
  <https://github.com/microsoft/vscode-pull-request-github>
@@ -46,39 +66,17 @@ The default pull-request query set and its category labels in
46
66
  Bundled glyphs and colour tables. Full license text in
47
67
  `apps/desktop/THIRD-PARTY-NOTICES.txt`.
48
68
 
49
- ### Files — Copyright (c) Files Community
50
-
51
- <https://github.com/files-community/Files>
52
-
53
- Folder-pane grouping keys, date-span labels, size buckets and the discrete
54
- layout size ladder in `apps/desktop/src/renderer/FolderPane.lazy.tsx`.
55
-
56
- ### OpenCode — Copyright (c) Anomaly / SST
57
-
58
- <https://github.com/anomalyco/opencode>
59
-
60
- Transcript auto-scroll gesture grammar, virtual-timeline anchoring and the
61
- streaming-markdown projection model.
62
-
63
- - `apps/desktop/src/renderer/use-transcript-follow.ts`, `TranscriptList.tsx`,
64
- `transcript-measure.ts`, `transcript-rows.ts`, `transcript-virtual-cache.ts`,
65
- `StreamingMarkdownBody.tsx`, `streaming-markdown.ts`, `Conversation.tsx`
66
-
67
- ### pi — Copyright (c) 2025 Mario Zechner
68
-
69
- <https://github.com/earendil-works/pi>
70
-
71
- `trimPartialClosingFences()` in `src/tui/markdown/stream-fence.mjs`, ported
72
- from the TUI markdown component.
69
+ ### ripgrep — Copyright (c) 2015 Andrew Gallant
73
70
 
74
- ### Orca — Copyright (c) 2026 Lovecast Inc.
71
+ <https://github.com/BurntSushi/ripgrep>
75
72
 
76
- Editor-surface and tab-hierarchy structure, the terminal host portal, the
77
- keep-awake power-save blocker and the hosted-review link derivation.
73
+ The `grep` and `ignore` library crates are compiled into the native search
74
+ binary, and the packaged `rg` executable ships as the grep tool's fast path.
75
+ ripgrep is offered under the MIT License OR the Unlicense; Mixdog takes the
76
+ MIT terms. The upstream notice is preserved as `LICENSES/ripgrep-NOTICE.txt`.
78
77
 
79
- - `apps/desktop/src/renderer/EditorPane.lazy.tsx`, `PaneSurfaceGate.tsx`,
80
- `PullRequestsPane.tsx`, `SourceControlDock.tsx`
81
- - `apps/desktop/src/main/agent-awake.ts`
78
+ - `native/mixdog-graph/` (the `grep` and `ignore` crates)
79
+ - `src/runtime/agent/orchestrator/tools/builtin/` (the `rg` fast path)
82
80
 
83
81
  ### Ink — Copyright (c) Vadim Demedes
84
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixdog",
3
- "version": "0.9.139",
3
+ "version": "0.9.141",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone mixdog coding-agent CLI/TUI workspace.",
@@ -108,7 +108,6 @@
108
108
  "chalk": "^5.6.2",
109
109
  "cli-highlight": "^2.1.11",
110
110
  "diff": "^9.0.0",
111
- "discord.js": "^14.26.4",
112
111
  "ink": "^7.1.0",
113
112
  "jsdom": "^27.0.0",
114
113
  "kiwi-nlp": "^0.21.0",
@@ -129,12 +128,6 @@
129
128
  "zod": "^3.25.76"
130
129
  },
131
130
  "overrides": {
132
- "discord.js": {
133
- "undici": "6.28.0"
134
- },
135
- "@discordjs/rest": {
136
- "undici": "6.28.0"
137
- },
138
131
  "onnxruntime-node": {
139
132
  "global-agent": "^4.1.3"
140
133
  },
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Version-neutral digest for release cache keys.
5
+ *
6
+ * Deploy rewrites the version field of package.json / package-lock.json on
7
+ * every release, so a cache key built from hashFiles() over those manifests is
8
+ * guaranteed to miss on the one run that needs it: the desktop bundle and the
9
+ * five prepared platform runtimes were rebuilt from scratch each release even
10
+ * though their inputs were byte-identical apart from the version string.
11
+ * Hashing the same manifests with the package identity stripped keeps the key
12
+ * pinned to the dependency tree that actually decides the output.
13
+ */
14
+ import { createHash } from 'node:crypto'
15
+ import { readFile } from 'node:fs/promises'
16
+ import { resolve } from 'node:path'
17
+ import { pathToFileURL } from 'node:url'
18
+
19
+ import { normalizeRuntimeLockfile } from './runtime-dependency-cache-key.mjs'
20
+
21
+ export const MANIFEST_CACHE_SCHEMA = 1
22
+
23
+ export function manifestCacheKey(manifests) {
24
+ const digest = createHash('sha256')
25
+ digest.update(`manifest-v${MANIFEST_CACHE_SCHEMA}`)
26
+ for (const manifest of manifests) {
27
+ digest.update(JSON.stringify(normalizeRuntimeLockfile(manifest)))
28
+ }
29
+ return digest.digest('hex')
30
+ }
31
+
32
+ const invokedPath = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : ''
33
+ if (invokedPath === import.meta.url) {
34
+ const manifestPaths = process.argv.slice(2)
35
+ if (!manifestPaths.length) {
36
+ throw new Error('Usage: manifest-cache-key.mjs <package.json|package-lock.json>...')
37
+ }
38
+ Promise.all(manifestPaths.map((path) => readFile(resolve(path))))
39
+ .then((manifests) => process.stdout.write(`${manifestCacheKey(manifests)}\n`))
40
+ .catch((error) => {
41
+ process.stderr.write(`Manifest cache key failed: ${error?.message || error}\n`)
42
+ process.exitCode = 1
43
+ })
44
+ }
@@ -0,0 +1,115 @@
1
+ // Output-style metadata contract: id normalization, frontmatter parsing,
2
+ // catalog ordering, and lookup. Required by the CJS rules builder (prompt
3
+ // injection) and imported by the ESM session runtime (pickers, config), so one
4
+ // schema governs both instead of two parallel copies drifting apart.
5
+ //
6
+ // Frontmatter schema for `output-styles/<id>.md`:
7
+ // name — canonical id (defaults to the file name)
8
+ // title | label — display label (defaults to a title-cased id)
9
+ // description — one-line picker text
10
+ // aliases — comma-separated alternate ids; the only alias source
11
+ // partial — true hides the file from the selectable catalog
12
+ // keep-shared-format — false replaces common.md instead of extending it
13
+ 'use strict';
14
+
15
+ const DEFAULT_OUTPUT_STYLE_ID = 'simple';
16
+ // Catalog order: deepest first, then unknown/custom styles by label.
17
+ const OUTPUT_STYLE_ORDER = ['detailed', 'simple', 'minimal', 'extreme-minimal'];
18
+
19
+ /** Slug form of a style id. Aliases live in frontmatter, never in code. */
20
+ function normalizeOutputStyleId(value) {
21
+ const slug = String(value ?? '').trim().toLowerCase()
22
+ .replace(/[_\s]+/g, '-')
23
+ .replace(/^-+|-+$/g, '');
24
+ return /^[a-z0-9.-]+$/.test(slug) ? slug : '';
25
+ }
26
+
27
+ /** Separator-insensitive key so `one-line`, `one_line`, and `oneline` match. */
28
+ function outputStyleCompactKey(value) {
29
+ return normalizeOutputStyleId(value).replace(/[_.-]+/g, '');
30
+ }
31
+
32
+ function titleCaseOutputStyle(id) {
33
+ return String(id || '')
34
+ .split(/[_.-]+/)
35
+ .filter(Boolean)
36
+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
37
+ .join(' ') || 'Default';
38
+ }
39
+
40
+ function parseOutputStyleFrontmatter(markdown) {
41
+ const match = String(markdown || '').match(/^---\r?\n([\s\S]*?)\r?\n---/);
42
+ const meta = {};
43
+ if (!match) return meta;
44
+ for (const line of match[1].split(/\r?\n/)) {
45
+ const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*?)\s*$/);
46
+ if (!kv) continue;
47
+ meta[kv[1]] = kv[2].replace(/^['"]|['"]$/g, '').trim();
48
+ }
49
+ return meta;
50
+ }
51
+
52
+ function outputStyleFlag(value, fallback) {
53
+ const raw = String(value ?? '').trim().toLowerCase();
54
+ if (!raw) return fallback;
55
+ if (/^(?:true|1|yes|on)$/.test(raw)) return true;
56
+ if (/^(?:false|0|no|off)$/.test(raw)) return false;
57
+ return fallback;
58
+ }
59
+
60
+ /**
61
+ * Frontmatter → catalog entry. Returns null for `partial: true` files (the
62
+ * shared format partial) and for files with no usable id.
63
+ */
64
+ function outputStyleMetaFromMarkdown(markdown, fileName) {
65
+ const meta = parseOutputStyleFrontmatter(markdown);
66
+ if (outputStyleFlag(meta.partial, false)) return null;
67
+ const fileId = normalizeOutputStyleId(String(fileName || '').replace(/\.md$/i, ''));
68
+ const id = normalizeOutputStyleId(meta.name) || fileId;
69
+ if (!id) return null;
70
+ return {
71
+ id,
72
+ label: String(meta.title || meta.label || '').trim() || titleCaseOutputStyle(id),
73
+ description: String(meta.description || '').trim(),
74
+ aliases: String(meta.aliases || '').split(',')
75
+ .map((alias) => normalizeOutputStyleId(alias))
76
+ .filter(Boolean),
77
+ // Built-in and custom styles both inherit the shared format partial; a
78
+ // style opts out only by declaring `keep-shared-format: false`.
79
+ keepSharedFormat: outputStyleFlag(meta['keep-shared-format'], true),
80
+ };
81
+ }
82
+
83
+ function sortOutputStyles(styles) {
84
+ return [...(styles || [])].sort((a, b) => {
85
+ const ai = OUTPUT_STYLE_ORDER.indexOf(a.id);
86
+ const bi = OUTPUT_STYLE_ORDER.indexOf(b.id);
87
+ if (ai !== bi) return (ai < 0 ? 999 : ai) - (bi < 0 ? 999 : bi);
88
+ return a.label.localeCompare(b.label, 'en', { sensitivity: 'base' });
89
+ });
90
+ }
91
+
92
+ /** Resolve an id, label, or frontmatter alias, ignoring separators and case. */
93
+ function matchOutputStyle(value, styles) {
94
+ const id = normalizeOutputStyleId(value);
95
+ const compact = outputStyleCompactKey(value);
96
+ if (!id && !compact) return null;
97
+ return (styles || []).find((style) => {
98
+ if (style.id === id || outputStyleCompactKey(style.id) === compact) return true;
99
+ if (outputStyleCompactKey(style.label) === compact) return true;
100
+ return (style.aliases || []).some((alias) => alias === id || outputStyleCompactKey(alias) === compact);
101
+ }) || null;
102
+ }
103
+
104
+ module.exports = {
105
+ DEFAULT_OUTPUT_STYLE_ID,
106
+ OUTPUT_STYLE_ORDER,
107
+ normalizeOutputStyleId,
108
+ outputStyleCompactKey,
109
+ titleCaseOutputStyle,
110
+ parseOutputStyleFrontmatter,
111
+ outputStyleFlag,
112
+ outputStyleMetaFromMarkdown,
113
+ sortOutputStyles,
114
+ matchOutputStyle,
115
+ };
@@ -35,6 +35,12 @@
35
35
 
36
36
  const fs = require('fs');
37
37
  const path = require('path');
38
+ const {
39
+ DEFAULT_OUTPUT_STYLE_ID,
40
+ matchOutputStyle,
41
+ outputStyleMetaFromMarkdown,
42
+ sortOutputStyles,
43
+ } = require('./output-style-meta.cjs');
38
44
 
39
45
  /**
40
46
  * Read a single section from mixdog-config.json (unified config).
@@ -187,6 +193,7 @@ function buildLanguageSection(dataDir) {
187
193
  const lines = [
188
194
  `- Default user-facing language${source}: ${language.prompt}. Use it for all user-facing text (preambles, progress, questions, reports, notices), overriding output style; switch only when the user does or asks.`,
189
195
  `- Keep code identifiers, paths, commands, symbols, API names, and exact errors in original form.`,
196
+ `- Never coin a word-for-word translation of source jargon; use the established original term or a plain functional description.`,
190
197
  ];
191
198
  return `# Language\n\n${lines.join('\n')}`;
192
199
  }
@@ -221,84 +228,10 @@ function omitToolRoutes(text, omitTools = []) {
221
228
  return out.replace(/[ \t]+\n/g, '\n').replace(/\n{3,}/g, '\n\n');
222
229
  }
223
230
 
224
- const DEFAULT_OUTPUT_STYLE_ID = 'simple';
225
- const OUTPUT_STYLE_ORDER = ['detailed', 'simple', 'minimal', 'extreme-minimal'];
226
- const OUTPUT_STYLE_ALIASES = new Map([
227
- ['extreme', 'extreme-minimal'],
228
- ['extremesimple', 'extreme-minimal'],
229
- ['extreme-simple', 'extreme-minimal'],
230
- ['extreme_simple', 'extreme-minimal'],
231
- ['extrememinimal', 'extreme-minimal'],
232
- ['extreme_minimal', 'extreme-minimal'],
233
- ['mono', 'extreme-minimal'],
234
- ['oneline', 'extreme-minimal'],
235
- ['one-line', 'extreme-minimal'],
236
- ['one_line', 'extreme-minimal'],
237
- ]);
238
-
239
- function normalizeOutputStyleId(value) {
240
- const raw = String(value || '').trim().toLowerCase();
241
- if (!raw) return '';
242
- const slug = raw.replace(/[_\s]+/g, '-').replace(/^-+|-+$/g, '');
243
- const compact = slug.replace(/[_.-]+/g, '');
244
- if (OUTPUT_STYLE_ALIASES.has(slug)) return OUTPUT_STYLE_ALIASES.get(slug);
245
- if (OUTPUT_STYLE_ALIASES.has(compact)) return OUTPUT_STYLE_ALIASES.get(compact);
246
- return /^[a-z0-9.-]+$/.test(slug) ? slug : '';
247
- }
248
-
249
- function outputStyleCompactKey(value) {
250
- return normalizeOutputStyleId(value).replace(/[_.-]+/g, '');
251
- }
252
-
253
- function titleCaseOutputStyle(id) {
254
- return String(id || '')
255
- .split(/[_.-]+/)
256
- .filter(Boolean)
257
- .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
258
- .join(' ') || 'Default';
259
- }
260
-
261
- function parseOutputStyleFrontmatter(markdown) {
262
- const match = String(markdown || '').match(/^---\r?\n([\s\S]*?)\r?\n---/);
263
- const meta = {};
264
- if (!match) return meta;
265
- for (const line of match[1].split(/\r?\n/)) {
266
- const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*?)\s*$/);
267
- if (!kv) continue;
268
- meta[kv[1]] = kv[2].replace(/^['"]|['"]$/g, '').trim();
269
- }
270
- return meta;
271
- }
272
-
273
- function outputStyleFlag(value, fallback) {
274
- const raw = String(value ?? '').trim().toLowerCase();
275
- if (!raw) return fallback;
276
- if (/^(?:true|1|yes|on)$/.test(raw)) return true;
277
- if (/^(?:false|0|no|off)$/.test(raw)) return false;
278
- return fallback;
279
- }
280
-
281
- function readOutputStyleEntry(filePath, source) {
282
- const raw = readOptional(filePath);
283
- if (!raw) return null;
284
- const meta = parseOutputStyleFrontmatter(raw);
285
- if (/^(?:true|1|yes)$/i.test(String(meta.partial || '').trim())) return null;
286
- const fileId = normalizeOutputStyleId(path.basename(filePath).replace(/\.md$/i, ''));
287
- const id = normalizeOutputStyleId(meta.name) || fileId;
288
- if (!id) return null;
289
- return {
290
- id,
291
- label: String(meta.title || meta.label || '').trim() || titleCaseOutputStyle(id),
292
- aliases: String(meta.aliases || '').split(',')
293
- .map((alias) => normalizeOutputStyleId(alias))
294
- .filter(Boolean),
295
- // Built-in and custom styles both inherit the shared format partial; a
296
- // style opts out only by declaring `keep-shared-format: false`.
297
- keepSharedFormat: outputStyleFlag(meta['keep-shared-format'], true),
298
- source,
299
- raw,
300
- };
301
- }
231
+ // Framing line under the style header: the block owns user-facing prose only,
232
+ // so a style (including a `keep-shared-format: false` replacement) never reads
233
+ // as guidance for code or tool payloads.
234
+ const OUTPUT_STYLE_ANCHOR = 'The section below governs how you write user-facing text; it outranks any other formatting habit and does not apply to code or tool calls.';
302
235
 
303
236
  function listOutputStyleEntries({ PLUGIN_ROOT, DATA_DIR }) {
304
237
  const byId = new Map();
@@ -309,27 +242,12 @@ function listOutputStyleEntries({ PLUGIN_ROOT, DATA_DIR }) {
309
242
  let files = [];
310
243
  try { files = fs.readdirSync(dir).filter((name) => name.toLowerCase().endsWith('.md')).sort(); } catch {}
311
244
  for (const name of files) {
312
- const style = readOutputStyleEntry(path.join(dir, name), source);
313
- if (style) byId.set(style.id, style);
245
+ const raw = readOptional(path.join(dir, name));
246
+ const meta = raw ? outputStyleMetaFromMarkdown(raw, name) : null;
247
+ if (meta) byId.set(meta.id, { ...meta, source, raw });
314
248
  }
315
249
  }
316
- return [...byId.values()].sort((a, b) => {
317
- const ai = OUTPUT_STYLE_ORDER.indexOf(a.id);
318
- const bi = OUTPUT_STYLE_ORDER.indexOf(b.id);
319
- if (ai !== bi) return (ai < 0 ? 999 : ai) - (bi < 0 ? 999 : bi);
320
- return a.label.localeCompare(b.label, 'en', { sensitivity: 'base' });
321
- });
322
- }
323
-
324
- function findOutputStyleEntry(value, styles) {
325
- const id = normalizeOutputStyleId(value);
326
- const compact = outputStyleCompactKey(value);
327
- if (!id && !compact) return null;
328
- return styles.find((style) => {
329
- if (style.id === id || outputStyleCompactKey(style.id) === compact) return true;
330
- if (outputStyleCompactKey(style.label) === compact) return true;
331
- return style.aliases.some((alias) => alias === id || outputStyleCompactKey(alias) === compact);
332
- }) || null;
250
+ return sortOutputStyles([...byId.values()]);
333
251
  }
334
252
 
335
253
  /** Shared format partial: a user copy in DATA_DIR overrides the built-in one. */
@@ -343,14 +261,14 @@ function loadOutputStyle({ PLUGIN_ROOT, DATA_DIR }) {
343
261
  // `agent.outputStyle` key is dropped by config canonicalization.
344
262
  const configured = String(readUnifiedConfig(DATA_DIR).outputStyle || '').trim() || DEFAULT_OUTPUT_STYLE_ID;
345
263
  const styles = listOutputStyleEntries({ PLUGIN_ROOT, DATA_DIR });
346
- const style = findOutputStyleEntry(configured, styles)
347
- || findOutputStyleEntry(DEFAULT_OUTPUT_STYLE_ID, styles)
264
+ const style = matchOutputStyle(configured, styles)
265
+ || matchOutputStyle(DEFAULT_OUTPUT_STYLE_ID, styles)
348
266
  || styles[0];
349
267
  if (!style) return '';
350
268
  const selected = stripFrontmatter(style.raw);
351
269
  if (!selected) return '';
352
270
  const common = style.keepSharedFormat ? readSharedFormatPartial({ PLUGIN_ROOT, DATA_DIR }) : '';
353
- return [`# Output Style: ${style.label}`, common, selected].filter(Boolean).join('\n\n');
271
+ return [`# Output Style: ${style.label}`, OUTPUT_STYLE_ANCHOR, common, selected].filter(Boolean).join('\n\n');
354
272
  }
355
273
 
356
274
  function buildSharedToolContent({ PLUGIN_ROOT, omitTools = [] } = {}) {
@@ -7,48 +7,32 @@ partial: true
7
7
 
8
8
  ## Shared Output Format
9
9
 
10
- - Lead with the answer or action. Write for a person, not as a console log. Match
11
- the request's language, tone, and apparent expertise; use complete grammatical
12
- sentences and explain unfamiliar terms without talking down to the reader.
13
- - Select content according to the active depth variation before formatting it.
14
- Formatting must never restore information that variation discarded.
15
- - Choose the presentation from the content, the reader, and the task. Markdown is
16
- available, not mandatory. Use paragraphs, line breaks, bullets, numbered steps,
17
- headings, tables, or code in any combination that makes the particular answer
18
- easier to understand; no response shape is the default or a required mapping
19
- from question type to format.
10
+ - Lead with the answer or action, then the context needed to understand it, then
11
+ lower-priority detail. Write for a person, not a console log: complete
12
+ sentences, explained terms, and a register matched to the reader's language,
13
+ tone, and apparent expertise.
14
+ - Select content by the active depth variation before formatting it. Formatting
15
+ never restores information that variation discarded.
16
+ - Choose the shape from the content and the reader. Markdown is available, not
17
+ mandatory; prose, bullets, numbered steps, headings, tables, and code may
18
+ combine in any way that makes this particular answer easier to read, and no
19
+ response shape is a default or a required mapping from question type.
20
20
  - Output renders as GitHub-flavored Markdown — tables, task lists, footnotes,
21
21
  fenced code with a language tag, and `$$…$$` math — in a proportional-width
22
- surface. Space-aligned columns and ASCII art do not line up there; use a real
23
- table or a fenced block instead.
24
- - Keep simple answers visually simple. For longer answers, add only the structure
25
- that creates useful grouping, order, contrast, or emphasis. Do not rotate
26
- formats for variety, force a recurring template, label the opening answer or
27
- closing paragraph, create headings for tiny blocks, or nest lists without need.
28
- - Never produce an essay-shaped wall of text. When a block contains several
29
- separable ideas or becomes hard to scan, split it or choose a clearer structure.
30
- Do not overcorrect into fragments or excessive sections when a compact answer
31
- already reads cleanly.
32
- - Use tables only for short enumerable or quantitative facts when they genuinely
33
- improve comparison. Keep explanatory reasoning outside table cells.
34
- - Keep the flow linear so each sentence builds on what came before without
35
- semantic backtracking. Keep related ideas together and give each paragraph or
36
- list item one main idea. Split or list a block as soon as it serves multiple
37
- roles. Avoid essay-shaped walls of text, fragments, excessive dashes,
38
- symbol-heavy shorthand, dense paragraphs, and decorative emphasis.
39
- - Use an inverted pyramid when useful: answer first, then the context needed to
40
- understand it, then lower-priority detail. State each material point once; do
41
- not repeat the opening conclusion in a `Summary`, `Conclusion`, or `In short`
42
- ending, and never force a recurring response template.
43
- - Keep established facts separate from plausible inference. Do not invent a
44
- mechanism, value, consequence, or certainty to make an explanation feel
45
- complete.
46
- - Use user-facing terms without unexplained internal shorthand, routine process
47
- narration, raw output dumps, or emojis unless requested. Show code and file
48
- references only when their exact form helps the reader.
49
- - Reader understanding outranks terseness, but every retained sentence must earn
50
- its place. Do not restate the request or announce compliance with constraints
51
- such as language or tool use. Remove preamble, filler, obvious statements, and
52
- repetition. Once every requested point is covered, stop; do not append a recap,
53
- solution, recommendation, next step, or offer of more help unless requested or
54
- necessary to prevent a misleading answer. Preserve exact technical literals.
22
+ surface, so space-aligned columns and ASCII art never line up; use a real table
23
+ or a fenced block instead. Use tables only for short enumerable or quantitative
24
+ facts, and keep explanatory reasoning outside the cells.
25
+ - Keep simple answers visually simple and add structure only where it creates
26
+ useful grouping, order, or contrast. Never produce an essay-shaped wall of
27
+ text, and never overcorrect into fragments, decorative emphasis, tiny headings,
28
+ needless nesting, or a recurring template.
29
+ - Give each paragraph or list item one idea and keep the flow linear, so the
30
+ reader builds meaning forward without re-parsing what came before.
31
+ - Keep established facts separate from plausible inference. Never invent a
32
+ mechanism, value, or certainty to make an explanation feel complete, preserve
33
+ exact technical literals, and show code or file references only where their
34
+ exact form helps.
35
+ - State each material point once and stop when the request is covered. Do not
36
+ restate the request, close with a recap, or append an unrequested
37
+ recommendation, next step, or offer of help; no emojis, raw output dumps, or
38
+ narration of your own process unless asked.
@@ -9,8 +9,8 @@ aliases: verbose, full
9
9
 
10
10
  Detailed — concrete explanation.
11
11
 
12
- - Information depth: retain the answer plus every established explanatory layer
13
- that materially improves understanding—mechanism, evidence, trade-offs,
14
- implications, and useful examples. Deepen the requested subject without
15
- filling evidentiary gaps or broadening into fixes, recommendations, or adjacent
16
- lessons unless asked.
12
+ - Retain: the answer plus every established explanatory layer that materially
13
+ improves understanding mechanism, evidence, trade-offs, implications, and
14
+ useful examples. Deepen the requested subject rather than widening it.
15
+ - Omit: speculation that fills an evidentiary gap, and any expansion into fixes,
16
+ recommendations, or adjacent lessons that were not requested.
@@ -2,16 +2,16 @@
2
2
  name: extreme-minimal
3
3
  title: Extreme Minimal
4
4
  description: Final decision or direct answer in one or two sentences
5
- aliases: extreme, oneline, one-line, one line, mono
5
+ aliases: extreme, extreme-simple, one-line, mono
6
6
  ---
7
7
 
8
8
  ## Depth Variation
9
9
 
10
10
  Extreme minimal — final decision or answer.
11
11
 
12
- - Information depth: the entire response is one or two short sentences containing
13
- only the final decision, result, or direct answer. Include no heading, label,
14
- list, explanation, supporting fact, scope, caveat, recap, or follow-up, even
15
- when asked to explain or report. For a cause question, the direct answer is the
16
- highest-level cause, not its concrete causal chain. Never pack discarded detail
17
- into a longer sentence.
12
+ - Retain: only the final decision, result, or direct answer. For a cause
13
+ question, that is the highest-level cause, not its concrete causal chain.
14
+ - Omit: every heading, label, list, explanation, supporting fact, scope, caveat,
15
+ recap, and follow-up, even when asked to explain or report.
16
+ - Shape: one or two short sentences. Never pack discarded detail into a longer
17
+ sentence.
@@ -2,15 +2,16 @@
2
2
  name: minimal
3
3
  title: Minimal
4
4
  description: Conclusion, core cause, and one decisive fact
5
+ aliases: brief, short
5
6
  ---
6
7
 
7
8
  ## Depth Variation
8
9
 
9
10
  Minimal — conclusion and core cause.
10
11
 
11
- - Information depth: retain the conclusion, the single core cause that determines
12
- it, and one or two decisive concrete facts that make the cause clear. Include
13
- the immediate consequence when it completes the answer, then stop before a
14
- full explanation. Do not add secondary evidence, deployment state, unaffected
15
- scope, broader impact, exceptions, remediation, or process unless that category
16
- is the requested answer.
12
+ - Retain: the conclusion, the single core cause that determines it, one or two
13
+ decisive concrete facts that make the cause clear, and the immediate
14
+ consequence when it completes the answer. Stop before a full explanation.
15
+ - Omit: secondary evidence, deployment state, unaffected scope, broader impact,
16
+ exceptions, remediation, and process, unless that category is the requested
17
+ answer.
@@ -9,9 +9,10 @@ aliases: default, concise, handoff
9
9
 
10
10
  Simple — concise summary.
11
11
 
12
- - Information depth: preserve enough context for the answer to stand on its own.
13
- Retain the main answer, the essential causal sequence, strongest supporting
14
- evidence, direct consequences, and material scope or caveat. Prefer a complete
15
- explanation over compression that makes the reader infer a missing step. Omit
16
- only secondary evidence, exhaustive implementation detail, examples, edge
12
+ - Retain: the main answer, the essential causal sequence, the strongest
13
+ supporting evidence, direct consequences, and any material scope or caveat.
14
+ Preserve enough context for the answer to stand on its own, and prefer a
15
+ complete explanation over compression that makes the reader infer a missing
16
+ step.
17
+ - Omit: secondary evidence, exhaustive implementation detail, examples, edge
17
18
  cases, adjacent implications, alternatives, and unrequested remediation.