docks-kit 0.16.7 → 0.16.8
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/AGENTS.md
CHANGED
|
@@ -63,10 +63,10 @@ Codex SoT notes:
|
|
|
63
63
|
omp SoT notes:
|
|
64
64
|
- `SoT/.omp/AGENTS.md`, `config.yml`, `models.yml`, and `mcp.json` deploy to `~/.omp/agent/`.
|
|
65
65
|
- `SoT/.omp/intercom.json` deploys to `$PI_CODING_AGENT_DIR/intercom/config.json`. The default root is `~/.pi/agent`.
|
|
66
|
-
- `ompSync.ts syncMergedYaml` deep-merges `config.yml` through `mergeOmpConfig`.
|
|
66
|
+
- `ompSync.ts syncMergedYaml` deep-merges `config.yml` through `ompYaml.ts mergeOmpConfig` and `models.yml` through `mergeOmpModels`. Both wrap one generic mapping merge; only the config wrapper prunes stale `retry.fallbackChains` wildcards.
|
|
67
67
|
- `cycleOrder` ends with the `fable` role (`modelRoles.fable` = `anthropic/claude-fable-5-1:medium`, `modelTags.fable` visible, `retry.fallbackChains.fable` empty), so the model switcher reaches Fable 5.1 as its fourth stop and never falls back off it. The hidden `switch_fable` role points at the same model and level.
|
|
68
68
|
- `modelRoles.task` is `openai-codex/gpt-6-astra:low`, chosen for 2.60 s TTFT and 4k output tokens per task at cost parity with the previous `gpt-5.6-sol:high`. `task.agentModelOverrides` points four reviewer names at `@task`; only the bundled `reviewer` and `security-reviewer` are discoverable OMP agents, so those two inherit `task`, and the `code-reviewer` and `plan-reviewer` entries are dormant. Artificial Analysis publishes no per-level Astra Coding Agent Index score, so reviewer output is the signal to watch.
|
|
69
|
-
- `SoT/.omp/models.yml` deploys to `~/.omp/agent/models.yml` through
|
|
69
|
+
- `SoT/.omp/models.yml` deploys to `~/.omp/agent/models.yml` through `mergeOmpModels`, which keeps every deployed-only key. A user file may carry provider credentials, so whole-file replacement is wrong. It restricts the `gpt-6-astra` effort ladder to `low`, so every Astra subagent runs `low` while `task.maxEffort: high` lets `scout` and `sonic` reach Luna `high` with `effort: hi`. `cli/docs/omp-models.md` records the verification.
|
|
70
70
|
- `SoT/.omp/AGENTS.md` carries the rule `Please remove all mannered prose.` Anthropic's Fable 5.1 prompting guide documents mannered prose as a Fable 5.1 behavior and gives that sentence as its short-version fix: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5-1
|
|
71
71
|
- `cli/docs/omp-models.md` (topic `omp-models`) records the role map rationale and the Artificial Analysis snapshot behind it. Model choices change with published benchmarks, so update that topic in the same commit as a role change.
|
|
72
72
|
- Sync registers the `docks` marketplace. It installs or upgrades `docks@docks` and `plan-lifecycle@docks` at user scope.
|
|
@@ -14,7 +14,7 @@ import { p, spawnProcess, type AsyncProcessResult } from "./exec"
|
|
|
14
14
|
import type { Ctx } from "./index"
|
|
15
15
|
import { isObject, parseJson } from "./jq"
|
|
16
16
|
import { ompPaths } from "./ompPaths"
|
|
17
|
-
import { mergeOmpConfig } from "./ompYaml"
|
|
17
|
+
import { mergeOmpConfig, mergeOmpModels } from "./ompYaml"
|
|
18
18
|
import { field } from "./toolchain"
|
|
19
19
|
|
|
20
20
|
const MARKETPLACE_NAME = "docks"
|
|
@@ -35,8 +35,8 @@ export async function ompSync(ctx: Ctx): Promise<OmpState> {
|
|
|
35
35
|
|
|
36
36
|
syncWholeFile(ctx, "SoT/.omp/AGENTS.md", p(agentDir, "AGENTS.md"), "omp AGENTS.md already in sync", "omp AGENTS.md synced")
|
|
37
37
|
syncWholeFile(ctx, "SoT/.omp/mcp.json", p(agentDir, "mcp.json"), "omp mcp.json already in sync", "omp mcp.json synced")
|
|
38
|
-
syncMergedYaml(ctx, "SoT/.omp/config.yml", p(agentDir, "config.yml"))
|
|
39
|
-
syncMergedYaml(ctx, "SoT/.omp/models.yml", p(agentDir, "models.yml"))
|
|
38
|
+
syncMergedYaml(ctx, "SoT/.omp/config.yml", p(agentDir, "config.yml"), mergeOmpConfig)
|
|
39
|
+
syncMergedYaml(ctx, "SoT/.omp/models.yml", p(agentDir, "models.yml"), mergeOmpModels)
|
|
40
40
|
|
|
41
41
|
const intercomRootSetting = process.env["PI_CODING_AGENT_DIR"]
|
|
42
42
|
const intercomRoot = intercomRootSetting !== undefined && intercomRootSetting !== ""
|
|
@@ -112,7 +112,12 @@ function syncWholeFile(
|
|
|
112
112
|
ctx.nextStepTriggers.ompRestart = true
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
function syncMergedYaml(
|
|
115
|
+
function syncMergedYaml(
|
|
116
|
+
ctx: Ctx,
|
|
117
|
+
sourcePath: OmpTextPayloadPath,
|
|
118
|
+
target: string,
|
|
119
|
+
merge: (sotText: string, deployedText: string) => string
|
|
120
|
+
): void {
|
|
116
121
|
const { change, echo, verbose } = ctx.services.logger
|
|
117
122
|
const source = payloadDisplayPath(sourcePath)
|
|
118
123
|
const sotText = payloadText(sourcePath)
|
|
@@ -130,7 +135,7 @@ function syncMergedYaml(ctx: Ctx, sourcePath: OmpTextPayloadPath, target: string
|
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
const deployedText = readFileSync(target, "utf8")
|
|
133
|
-
const merged =
|
|
138
|
+
const merged = merge(sotText, deployedText)
|
|
134
139
|
if (merged === deployedText) {
|
|
135
140
|
verbose(`omp ${name} already in sync`)
|
|
136
141
|
return
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* YAML merge for the omp config.yml and models.yml files.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* YAML merge for the omp config.yml and models.yml files. omp serialises both
|
|
3
|
+
* files itself, so the kit uses the yaml package rather than a line-based
|
|
4
|
+
* merge. Deployed-only keys are retained, except where the file-specific
|
|
5
|
+
* wrapper drops them: config.yml drops slash-bearing keys directly under
|
|
6
|
+
* retry.fallbackChains, because omp treats those as model or provider
|
|
7
|
+
* wildcards ahead of role chains, so a stale wildcard would silently override
|
|
8
|
+
* the kit-managed role chains. models.yml drops nothing; a user file may carry
|
|
9
|
+
* provider credentials and custom models the kit never declares.
|
|
7
10
|
*/
|
|
8
11
|
import { isMap, isScalar, parseDocument, type YAMLMap } from "yaml"
|
|
9
12
|
|
|
13
|
+
/** Decides whether a deployed-only key at `path` is dropped instead of kept. */
|
|
14
|
+
type DropDeployedKey = (path: ReadonlyArray<string>, key: string) => boolean
|
|
15
|
+
|
|
10
16
|
const fallbackChainsPath = ["retry", "fallbackChains"] as const
|
|
11
17
|
|
|
12
18
|
function stringKey(key: unknown): string | undefined {
|
|
@@ -15,29 +21,31 @@ function stringKey(key: unknown): string | undefined {
|
|
|
15
21
|
return undefined
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
function
|
|
19
|
-
return (
|
|
20
|
-
path.length === fallbackChainsPath.length &&
|
|
21
|
-
path[0] === fallbackChainsPath[0] &&
|
|
22
|
-
path[1] === fallbackChainsPath[1]
|
|
23
|
-
)
|
|
24
|
+
function keyName(key: unknown): string {
|
|
25
|
+
return isScalar(key) ? String(key.value) : String(key)
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const dropFallbackWildcard: DropDeployedKey = (path, key) =>
|
|
29
|
+
path.length === fallbackChainsPath.length &&
|
|
30
|
+
path[0] === fallbackChainsPath[0] &&
|
|
31
|
+
path[1] === fallbackChainsPath[1] &&
|
|
32
|
+
key.includes("/")
|
|
33
|
+
|
|
34
|
+
const keepEveryKey: DropDeployedKey = () => false
|
|
35
|
+
|
|
36
|
+
function pruneMapping(mapping: YAMLMap, path: ReadonlyArray<string>, drop: DropDeployedKey): void {
|
|
28
37
|
for (let index = mapping.items.length - 1; index >= 0; index--) {
|
|
29
38
|
const pair = mapping.items[index]
|
|
30
39
|
if (pair === undefined) continue
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
if (underFallbackChains && keyName.includes("/")) {
|
|
41
|
+
if (drop(path, keyName(pair.key))) {
|
|
34
42
|
mapping.items.splice(index, 1)
|
|
35
43
|
continue
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
const key = stringKey(pair.key)
|
|
39
47
|
if (key !== undefined && isMap(pair.value)) {
|
|
40
|
-
|
|
48
|
+
pruneMapping(pair.value, [...path, key], drop)
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
}
|
|
@@ -45,7 +53,8 @@ function pruneFallbackWildcards(mapping: YAMLMap, path: ReadonlyArray<string>):
|
|
|
45
53
|
function mergeMappings(
|
|
46
54
|
sotMapping: YAMLMap,
|
|
47
55
|
deployedMapping: YAMLMap,
|
|
48
|
-
path: ReadonlyArray<string
|
|
56
|
+
path: ReadonlyArray<string>,
|
|
57
|
+
drop: DropDeployedKey
|
|
49
58
|
): void {
|
|
50
59
|
for (const sotPair of sotMapping.items) {
|
|
51
60
|
const key = stringKey(sotPair.key)
|
|
@@ -53,7 +62,7 @@ function mergeMappings(
|
|
|
53
62
|
|
|
54
63
|
const deployedPair = deployedMapping.items.find((candidate) => stringKey(candidate.key) === key)
|
|
55
64
|
if (deployedPair !== undefined && isMap(sotPair.value) && isMap(deployedPair.value)) {
|
|
56
|
-
mergeMappings(sotPair.value, deployedPair.value, [...path, key])
|
|
65
|
+
mergeMappings(sotPair.value, deployedPair.value, [...path, key], drop)
|
|
57
66
|
}
|
|
58
67
|
}
|
|
59
68
|
|
|
@@ -63,22 +72,24 @@ function mergeMappings(
|
|
|
63
72
|
key !== undefined && sotMapping.items.some((candidate) => stringKey(candidate.key) === key)
|
|
64
73
|
if (isManaged) continue
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
? String(deployedPair.key.value)
|
|
68
|
-
: String(deployedPair.key)
|
|
69
|
-
if (isFallbackChains(path) && keyName.includes("/")) continue
|
|
75
|
+
if (drop(path, keyName(deployedPair.key))) continue
|
|
70
76
|
if (key !== undefined && isMap(deployedPair.value)) {
|
|
71
|
-
|
|
77
|
+
pruneMapping(deployedPair.value, [...path, key], drop)
|
|
72
78
|
}
|
|
73
79
|
sotMapping.items.push(deployedPair)
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
82
|
|
|
77
|
-
|
|
83
|
+
function mergeYamlDocuments(
|
|
84
|
+
sotText: string,
|
|
85
|
+
deployedText: string,
|
|
86
|
+
name: string,
|
|
87
|
+
drop: DropDeployedKey
|
|
88
|
+
): string {
|
|
78
89
|
const deployedDoc = parseDocument(deployedText)
|
|
79
90
|
const deployedError = deployedDoc.errors[0]
|
|
80
91
|
if (deployedError !== undefined) {
|
|
81
|
-
throw new Error(`Invalid deployed omp YAML: ${deployedError.message}`)
|
|
92
|
+
throw new Error(`Invalid deployed omp ${name} YAML: ${deployedError.message}`)
|
|
82
93
|
}
|
|
83
94
|
|
|
84
95
|
const deployedContents = deployedDoc.contents
|
|
@@ -90,18 +101,28 @@ export function mergeOmpConfig(sotText: string, deployedText: string): string {
|
|
|
90
101
|
return sotText
|
|
91
102
|
}
|
|
92
103
|
if (!isMap(deployedContents)) {
|
|
93
|
-
throw new Error(
|
|
104
|
+
throw new Error(`Deployed omp ${name} YAML root must be a mapping`)
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
const sotDoc = parseDocument(sotText)
|
|
97
108
|
const sotError = sotDoc.errors[0]
|
|
98
109
|
if (sotError !== undefined) {
|
|
99
|
-
throw new Error(`Invalid SoT omp YAML: ${sotError.message}`)
|
|
110
|
+
throw new Error(`Invalid SoT omp ${name} YAML: ${sotError.message}`)
|
|
100
111
|
}
|
|
101
112
|
if (!isMap(sotDoc.contents)) {
|
|
102
|
-
throw new Error(
|
|
113
|
+
throw new Error(`SoT omp ${name} YAML root must be a mapping`)
|
|
103
114
|
}
|
|
104
115
|
|
|
105
|
-
mergeMappings(sotDoc.contents, deployedContents, [])
|
|
116
|
+
mergeMappings(sotDoc.contents, deployedContents, [], drop)
|
|
106
117
|
return sotDoc.toString()
|
|
107
118
|
}
|
|
119
|
+
|
|
120
|
+
/** config.yml merge: keeps user-only keys, drops stale fallback-chain wildcards. */
|
|
121
|
+
export function mergeOmpConfig(sotText: string, deployedText: string): string {
|
|
122
|
+
return mergeYamlDocuments(sotText, deployedText, "config.yml", dropFallbackWildcard)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** models.yml merge: keeps every user-only key, including credentials and custom models. */
|
|
126
|
+
export function mergeOmpModels(sotText: string, deployedText: string): string {
|
|
127
|
+
return mergeYamlDocuments(sotText, deployedText, "models.yml", keepEveryKey)
|
|
128
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Generated by cli/scripts/generate-sot-payload.ts. DO NOT EDIT.
|
|
2
2
|
// Edit SoT/, notification.mp3, or package.json, then run: bun cli/scripts/generate-sot-payload.ts
|
|
3
3
|
|
|
4
|
-
export const GENERATED_PACKAGE_VERSION = "0.16.
|
|
4
|
+
export const GENERATED_PACKAGE_VERSION = "0.16.8"
|
|
5
5
|
|
|
6
6
|
export const GENERATED_PAYLOAD_TEXT = {
|
|
7
7
|
"SoT/.agents/skills.txt": "# Universal AI-agent skill manifest intentionally empty.\n# Global skill discovery is opt-in: add one <owner>/<repo> slug per line.\n# EngineNative ignores comments and blank lines.\n",
|
|
@@ -21,7 +21,7 @@ export const GENERATED_PAYLOAD_TEXT = {
|
|
|
21
21
|
"SoT/.omp/config.yml": "symbolPreset: unicode\n\ntheme:\n dark: titanium\n\nstatusLine:\n preset: default\n compactThinkingLevel: false\n showHookStatus: true\n sessionAccent: true\n transparent: false\n\nterminal:\n showProgress: false\n\ntui:\n textSizing: false\n tight: false\n\ndefaultThinkingLevel: high\n\ncycleOrder:\n - smol\n - default\n - slow\n - fable\n\ntier:\n openai: none\n anthropic: none\n\nadvisor:\n enabled: true\n syncBacklog: \"1\"\n\ngithub:\n enabled: true\n\ntask:\n eager: always\n showResolvedModelBadge: true\n softRequestBudget: 200\n softRequestBudgetNotice: true\n batch: true\n enableLsp: true\n enableEffort: true\n maxEffort: high\n maxConcurrency: 16\n maxRecursionDepth: 2\n maxRuntimeMs: 3600000\n agentModelOverrides:\n reviewer: \"@task\"\n security-reviewer: \"@task\"\n code-reviewer: \"@task\"\n plan-reviewer: \"@task\"\n\nmodelRoles:\n smol: openai-codex/gpt-5.6-luna:medium\n advisor: openai-codex/gpt-5.6-sol:medium\n designer: anthropic/claude-opus-5:high\n plan: anthropic/claude-opus-5:xhigh\n commit: openai-codex/gpt-5.6-luna:medium\n task: openai-codex/gpt-6-astra:low\n vision: anthropic/claude-opus-5:medium\n tiny: openai-codex/gpt-5.6-luna:low\n default: anthropic/claude-opus-5:high\n slow: anthropic/claude-opus-5:xhigh\n fable: anthropic/claude-fable-5-1:medium\n switch_fable: anthropic/claude-fable-5-1:medium\n\nmodelTags:\n fable:\n name: Fable 5.1\n switch_fable:\n name: Fable switch default\n hidden: true\n\ndisplay:\n shimmer: classic\n showTokenUsage: true\n\nproviders:\n anthropic:\n serverSideFallback: false\n fetch: auto\n webSearchTimeoutSeconds: 30\n webSearchOrder:\n - firecrawl\n - exa\n - perplexity\n - gemini\n - codex\n\nretry:\n usageAwareFallback: true\n fallbackChains:\n default:\n - openai-codex/gpt-5.6-sol:high\n advisor:\n - anthropic/claude-opus-5:medium\n task:\n - anthropic/claude-opus-5:high\n vision:\n - openai-codex/gpt-5.6-sol:medium\n smol:\n - anthropic/claude-opus-5:low\n tiny:\n - anthropic/claude-opus-5:low\n commit:\n - anthropic/claude-opus-5:medium\n switch_fable: []\n fable: []\n\nomitThinking: false\nincludeWorkspaceTree: false\nautocompleteMaxVisible: 10\nemojiAutocomplete: true\n\nbranchSummary:\n enabled: true\n\nreadLineNumbers: false\n\ncommands:\n enableOpencodeProject: false\n enableOpencodeUser: false\n\nskills:\n enableClaudeUser: false\n enableCodexUser: false\n enableAgentsUser: true\n\nsteeringMode: all\ninterruptMode: immediate\n\ndev:\n autoqa: false\n autoqaConsent: denied\n\ncompaction:\n thresholdTokens: 231200\n idleEnabled: true\n handoffSaveToDisk: true\n\ndoubleEscapeAction: tree\nhideThinkingBlock: false\nautoResume: false\ntextVerbosity: low\n\nfeatures:\n unexpectedStopDetection: smart\n\ncodexResets:\n autoRedeem: \"no\"\n\nstartup:\n quiet: true\n changelogMode: summary\n\ntools:\n approvalMode: yolo\n",
|
|
22
22
|
"SoT/.omp/intercom.json": "{\n \"brokerCommand\": \"bun\",\n \"brokerArgs\": []\n}\n",
|
|
23
23
|
"SoT/.omp/mcp.json": "{\n \"$schema\": \"https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json\",\n \"disabledServers\": [\n \"chrome-devtools\",\n \"context7:context7\",\n \"openaiDeveloperDocs\"\n ]\n}\n",
|
|
24
|
-
"SoT/.omp/models.yml": "providers:\n openai-codex:\n modelOverrides:\n gpt-6-astra:\n thinking:\n mode: effort\n efforts
|
|
24
|
+
"SoT/.omp/models.yml": "providers:\n openai-codex:\n modelOverrides:\n gpt-6-astra:\n thinking:\n mode: effort\n efforts:\n - low\n defaultLevel: low\n"
|
|
25
25
|
} as const
|
|
26
26
|
|
|
27
27
|
export const GENERATED_PAYLOAD_BASE64 = {
|
|
@@ -50,4 +50,4 @@ export const GENERATED_PAYLOAD_PATHS = [
|
|
|
50
50
|
"notification.mp3"
|
|
51
51
|
] as const
|
|
52
52
|
|
|
53
|
-
export const GENERATED_PAYLOAD_HASH = "
|
|
53
|
+
export const GENERATED_PAYLOAD_HASH = "5f9f8b3d00c15f6611af2bd8d92bbfcd12481442ff6e0e249846ac2e320b4e37"
|
package/package.json
CHANGED