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.
- package/LICENSES/MIT.txt +12 -14
- package/NOTICE.md +32 -34
- package/package.json +1 -8
- package/scripts/manifest-cache-key.mjs +44 -0
- package/src/lib/output-style-meta.cjs +115 -0
- package/src/lib/rules-builder.cjs +18 -100
- package/src/output-styles/common.md +27 -43
- package/src/output-styles/detailed.md +5 -5
- package/src/output-styles/extreme-minimal.md +7 -7
- package/src/output-styles/minimal.md +7 -6
- package/src/output-styles/simple.md +6 -5
- package/src/rules/lead/01-general.md +3 -0
- package/src/rules/shared/10-tool-workflow.md +2 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +6 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +18 -3
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +55 -0
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +27 -7
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +21 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +62 -31
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +45 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +29 -2
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +3 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +9 -4
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +46 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +29 -2
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/patch/v4a-anchors.mjs +27 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +10 -10
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +72 -4
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/media/download.mjs +1 -1
- package/src/runtime/media/jobs.mjs +9 -0
- package/src/runtime/media/store.mjs +4 -9
- package/src/runtime/memory/lib/embedding-worker.mjs +9 -4
- package/src/runtime/memory/lib/http-router.mjs +31 -0
- package/src/runtime/memory/lib/ko-morph.mjs +8 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +28 -2
- package/src/runtime/shared/background-tasks.mjs +1 -1
- package/src/runtime/shared/webhook-session-run.mjs +6 -0
- package/src/runtime/shared/webhook-session-run.test.mjs +14 -0
- package/src/session-runtime/context-status.mjs +4 -2
- package/src/session-runtime/cwd-tool-routing.test.mjs +56 -0
- package/src/session-runtime/lifecycle-api.mjs +47 -0
- package/src/session-runtime/output-styles.mjs +28 -83
- package/src/session-runtime/runtime-core.mjs +14 -3
- package/src/session-runtime/session-turn-api.mjs +26 -0
- package/src/session-runtime/tool-catalog.mjs +2 -2
- package/src/session-runtime/tool-defs.mjs +1 -1
- package/src/standalone/agent-tool/shard-spread.mjs +5 -3
- package/src/standalone/agent-tool/shard-spread.test.mjs +16 -1
- package/src/standalone/session-client.mjs +1 -1
- package/src/standalone/session-protocol.mjs +2 -0
- package/src/standalone/session-service.mjs +2 -2
- package/src/standalone/session-wire.mjs +1 -1
- package/src/tui/App.jsx +3 -3
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch.mjs +18 -0
- package/src/tui/app/transcript-window.mjs +4 -5
- package/src/tui/app/use-mouse-input.mjs +1 -1
- package/src/tui/app/use-prompt-handlers.mjs +1 -1
- package/src/tui/app/use-transcript-scroll.mjs +37 -29
- package/src/tui/app/use-transcript-window.mjs +1 -1
- package/src/tui/dist/index.mjs +79 -54
- package/src/tui/keyboard-protocol.mjs +1 -1
- package/src/tui/markdown/stream-fence.mjs +40 -17
- package/src/tui/session/labels.mjs +6 -1
- package/src/tui/session/session-api-ext.mjs +19 -0
- package/src/tui/session/session-api.mjs +1 -1
- package/src/tui/session/turn.mjs +20 -0
- package/src/tui/themes/light.mjs +4 -4
- package/src/tui/themes/teal.mjs +1 -1
- package/src/ui/statusline-segments.mjs +25 -6
- package/vendor/ink/build/ink.js +8 -86
- package/vendor/ink/build/input-parser.js +1 -1
- package/vendor/ink/build/output.js +11 -13
- package/vendor/ink/build/selection-rect.js +80 -0
- package/vendor/ink/build/termio-keypress.js +110 -79
- 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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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`).
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
71
|
+
<https://github.com/BurntSushi/ripgrep>
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
- `
|
|
80
|
-
|
|
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.
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
313
|
-
|
|
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()]
|
|
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 =
|
|
347
|
-
||
|
|
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
|
|
11
|
-
|
|
12
|
-
sentences
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
from question type
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
lessons
|
|
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,
|
|
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
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
when asked to explain or report.
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
explanation over compression that makes the reader infer a missing
|
|
16
|
-
|
|
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.
|