docks-kit 0.16.4 → 0.16.6
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 +2 -1
- package/cli/docs/omp-models.md +34 -1
- package/cli/src/argv.ts +2 -1
- package/cli/src/commands/model.ts +2 -1
- package/cli/src/commands/models.ts +2 -1
- package/cli/src/commands/status.ts +2 -11
- package/cli/src/efforts.ts +0 -4
- package/cli/src/engine-native/bun.ts +1 -1
- package/cli/src/engine-native/claudePlugins.ts +390 -0
- package/cli/src/engine-native/claudeSync.ts +2 -387
- package/cli/src/engine-native/codexSync.ts +6 -146
- package/cli/src/engine-native/codexToml.ts +144 -3
- package/cli/src/engine-native/exec.ts +1 -5
- package/cli/src/engine-native/jq.ts +7 -0
- package/cli/src/engine-native/models.ts +31 -9
- package/cli/src/engine-native/ompSync.ts +3 -3
- package/cli/src/engine-native/os/darwin.ts +8 -56
- package/cli/src/engine-native/os/linux.ts +8 -56
- package/cli/src/engine-native/os/posix.ts +67 -0
- package/cli/src/engine-native/os/targets.ts +1 -1
- package/cli/src/engine-native/skillsSync.ts +3 -3
- package/cli/src/engine-native/toolchain.ts +7 -7
- package/cli/src/generated/sotPayload.ts +3 -3
- package/cli/src/manifests.ts +1 -18
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Line-based Codex TOML passes: top-level setting replacement, the SoT
|
|
3
|
+
* top-level and table merges used by `sync codex`, and `--codex-model` /
|
|
4
|
+
* `--codex-effort`. A TOML library would reformat user configs and break the
|
|
5
|
+
* golden contract. The key is used as a raw regex fragment, preserving the
|
|
5
6
|
* historical record-oriented behavior.
|
|
6
7
|
*/
|
|
7
8
|
import { p } from "./exec"
|
|
@@ -110,3 +111,143 @@ export function syncCodexEffort(ctx: Ctx, effort: string): void {
|
|
|
110
111
|
unchanged: `Effort: deployed Codex model_reasoning_effort already ${resolved}`
|
|
111
112
|
})
|
|
112
113
|
}
|
|
114
|
+
|
|
115
|
+
export function mergeTopLevelSettings(sotConfigText: string, userConfig: string): void {
|
|
116
|
+
for (const line of sotConfigText.split("\n")) {
|
|
117
|
+
if (line.startsWith("[")) break
|
|
118
|
+
if (/^[ \t]*($|#)/.test(line)) continue
|
|
119
|
+
if (!/^[A-Za-z0-9_.-]+[ \t]*=/.test(line)) continue
|
|
120
|
+
const key = line.slice(0, line.indexOf("=")).replace(/[ \t]+$/, "")
|
|
121
|
+
replaceTopLevelSettingInFile(userConfig, key, line)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface TomlTableHeader {
|
|
126
|
+
readonly path: string
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const TOML_BASIC_ESCAPES: Readonly<Record<string, string>> = {
|
|
130
|
+
b: "\b",
|
|
131
|
+
t: "\t",
|
|
132
|
+
n: "\n",
|
|
133
|
+
f: "\f",
|
|
134
|
+
r: "\r",
|
|
135
|
+
'"': '"',
|
|
136
|
+
"\\": "\\"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function tomlBasicEscape(line: string, offset: number): { readonly next: number; readonly value: string } | undefined {
|
|
140
|
+
const escaped = line[offset]
|
|
141
|
+
const simple = escaped === undefined ? undefined : TOML_BASIC_ESCAPES[escaped]
|
|
142
|
+
if (simple !== undefined) return { next: offset + 1, value: simple }
|
|
143
|
+
const digits = escaped === "u" ? 4 : escaped === "U" ? 8 : 0
|
|
144
|
+
if (digits === 0) return undefined
|
|
145
|
+
const hex = line.slice(offset + 1, offset + 1 + digits)
|
|
146
|
+
if (hex.length !== digits || !/^[0-9A-Fa-f]+$/.test(hex)) return undefined
|
|
147
|
+
const codePoint = Number.parseInt(hex, 16)
|
|
148
|
+
if (codePoint > 0x10ffff || (codePoint >= 0xd800 && codePoint <= 0xdfff)) return undefined
|
|
149
|
+
return { next: offset + 1 + digits, value: String.fromCodePoint(codePoint) }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Decode a table header to the TOML path that determines managed ownership. */
|
|
153
|
+
function tomlTableHeader(line: string): TomlTableHeader | undefined {
|
|
154
|
+
let offset = 0
|
|
155
|
+
const skipWhitespace = (): void => {
|
|
156
|
+
while (line[offset] === " " || line[offset] === "\t") offset++
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
skipWhitespace()
|
|
160
|
+
if (line[offset] !== "[") return undefined
|
|
161
|
+
offset++
|
|
162
|
+
const array = line[offset] === "["
|
|
163
|
+
if (array) offset++
|
|
164
|
+
|
|
165
|
+
const keys: Array<string> = []
|
|
166
|
+
while (true) {
|
|
167
|
+
skipWhitespace()
|
|
168
|
+
const quote = line[offset]
|
|
169
|
+
let key = ""
|
|
170
|
+
if (quote === '"' || quote === "'") {
|
|
171
|
+
offset++
|
|
172
|
+
let closed = false
|
|
173
|
+
while (offset < line.length) {
|
|
174
|
+
const char = line[offset]!
|
|
175
|
+
if (char === quote) {
|
|
176
|
+
offset++
|
|
177
|
+
closed = true
|
|
178
|
+
break
|
|
179
|
+
}
|
|
180
|
+
if (quote === '"' && char === "\\") {
|
|
181
|
+
const escape = tomlBasicEscape(line, offset + 1)
|
|
182
|
+
if (escape === undefined) return undefined
|
|
183
|
+
key += escape.value
|
|
184
|
+
offset = escape.next
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
if (char === "\n" || char === "\r") return undefined
|
|
188
|
+
key += char
|
|
189
|
+
offset++
|
|
190
|
+
}
|
|
191
|
+
if (!closed) return undefined
|
|
192
|
+
} else {
|
|
193
|
+
const start = offset
|
|
194
|
+
while (offset < line.length && /[A-Za-z0-9_-]/.test(line[offset]!)) offset++
|
|
195
|
+
if (offset === start) return undefined
|
|
196
|
+
key = line.slice(start, offset)
|
|
197
|
+
}
|
|
198
|
+
keys.push(key)
|
|
199
|
+
|
|
200
|
+
skipWhitespace()
|
|
201
|
+
if (line[offset] === ".") {
|
|
202
|
+
offset++
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
205
|
+
if (line[offset] !== "]") return undefined
|
|
206
|
+
offset++
|
|
207
|
+
if (array) {
|
|
208
|
+
if (line[offset] !== "]") return undefined
|
|
209
|
+
offset++
|
|
210
|
+
}
|
|
211
|
+
skipWhitespace()
|
|
212
|
+
if (offset < line.length && line[offset] !== "#") return undefined
|
|
213
|
+
return { path: JSON.stringify(keys) }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function mergeTableSettingsText(sotConfigText: string, userConfigText: string): string {
|
|
218
|
+
const sotLines = sotConfigText.split("\n")
|
|
219
|
+
let merged = userConfigText
|
|
220
|
+
for (let tableOffset = 0; tableOffset < sotLines.length; tableOffset++) {
|
|
221
|
+
const managedHeader = tomlTableHeader(sotLines[tableOffset]!)
|
|
222
|
+
if (managedHeader === undefined) continue
|
|
223
|
+
|
|
224
|
+
const block: Array<string> = []
|
|
225
|
+
for (let blockOffset = tableOffset; blockOffset < sotLines.length; blockOffset++) {
|
|
226
|
+
const line = sotLines[blockOffset]!
|
|
227
|
+
if (blockOffset !== tableOffset && tomlTableHeader(line) !== undefined) break
|
|
228
|
+
block.push(line)
|
|
229
|
+
}
|
|
230
|
+
const tableBlock = block.join("\n").replace(/\n+$/, "")
|
|
231
|
+
|
|
232
|
+
const userLines = merged.split("\n")
|
|
233
|
+
if (userLines[userLines.length - 1] === "") userLines.pop()
|
|
234
|
+
let skip = false
|
|
235
|
+
const kept: Array<string> = []
|
|
236
|
+
for (const line of userLines) {
|
|
237
|
+
const header = tomlTableHeader(line)
|
|
238
|
+
if (header !== undefined) {
|
|
239
|
+
skip = header.path === managedHeader.path
|
|
240
|
+
if (skip) continue
|
|
241
|
+
}
|
|
242
|
+
if (!skip) kept.push(line)
|
|
243
|
+
}
|
|
244
|
+
merged = `${kept.join("\n")}\n\n${tableBlock}\n`
|
|
245
|
+
}
|
|
246
|
+
return merged
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function mergeTableSettings(sotConfigText: string, userConfig: string): void {
|
|
250
|
+
const next = mergeTableSettingsText(sotConfigText, readFileSync(userConfig, "utf8"))
|
|
251
|
+
writeFileSync(`${userConfig}.tmp`, next)
|
|
252
|
+
renameSync(`${userConfig}.tmp`, userConfig)
|
|
253
|
+
}
|
|
@@ -116,7 +116,7 @@ export function commandExists(name: string): boolean {
|
|
|
116
116
|
return which(name) !== ""
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
function isExecutable(path: string, suffixes: ReadonlyArray<string> = hostOs().executableSuffixes): boolean {
|
|
120
120
|
try {
|
|
121
121
|
if (!statSync(path).isFile()) return false
|
|
122
122
|
if (suffixes.some((suffix) => suffix !== "")) {
|
|
@@ -148,7 +148,3 @@ export function writeBytesIfChanged(path: string, content: Uint8Array): boolean
|
|
|
148
148
|
writeFileSync(path, bytes)
|
|
149
149
|
return true
|
|
150
150
|
}
|
|
151
|
-
|
|
152
|
-
export function writeFileIfChanged(path: string, content: string): boolean {
|
|
153
|
-
return writeTextIfChanged(path, content)
|
|
154
|
-
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* - output formatting: 2-space indent, UTF-8 raw, trailing newline.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
15
|
+
|
|
14
16
|
export type Json = null | boolean | number | string | Array<Json> | { [key: string]: Json }
|
|
15
17
|
|
|
16
18
|
export function isObject(v: Json): v is { [key: string]: Json } {
|
|
@@ -61,3 +63,8 @@ export function parseJson(text: string): Json | undefined {
|
|
|
61
63
|
return undefined
|
|
62
64
|
}
|
|
63
65
|
}
|
|
66
|
+
|
|
67
|
+
/** Parse a JSON file with `parseJson` semantics; a missing file is `undefined`. */
|
|
68
|
+
export function readJsonFile(file: string): Json | undefined {
|
|
69
|
+
return existsSync(file) ? parseJson(readFileSync(file, "utf8")) : undefined
|
|
70
|
+
}
|
|
@@ -6,6 +6,16 @@ import type { Ctx } from "./index"
|
|
|
6
6
|
import { isObject, parseJson, type Json } from "./jq"
|
|
7
7
|
import { payloadDisplayPath, payloadText } from "../payload"
|
|
8
8
|
|
|
9
|
+
export interface ModelEntry {
|
|
10
|
+
readonly id: string
|
|
11
|
+
readonly kind: "alias" | "id"
|
|
12
|
+
readonly note?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ModelCatalog {
|
|
16
|
+
readonly verified: string
|
|
17
|
+
readonly models: ReadonlyArray<ModelEntry>
|
|
18
|
+
}
|
|
9
19
|
|
|
10
20
|
function toolEntry(tool: string): { [k: string]: Json } | undefined {
|
|
11
21
|
const doc = parseJson(payloadText("SoT/models.json"))
|
|
@@ -14,16 +24,28 @@ function toolEntry(tool: string): { [k: string]: Json } | undefined {
|
|
|
14
24
|
return entry !== undefined && isObject(entry) ? entry : undefined
|
|
15
25
|
}
|
|
16
26
|
|
|
17
|
-
function modelEntries(
|
|
18
|
-
const entry = toolEntry(tool)
|
|
27
|
+
function modelEntries(entry: { [k: string]: Json } | undefined): Array<{ [k: string]: Json }> {
|
|
19
28
|
const models = entry?.["models"]
|
|
20
29
|
return Array.isArray(models) ? models.filter(isObject) : []
|
|
21
30
|
}
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Typed view of one tool's `SoT/models.json` section. An entry whose `id` is not a
|
|
34
|
+
* string or whose `kind` is neither `alias` nor `id` is skipped; a missing section
|
|
35
|
+
* yields `verified: "?"` and no models.
|
|
36
|
+
*/
|
|
37
|
+
export function modelCatalog(tool: string): ModelCatalog {
|
|
38
|
+
const entry = toolEntry(tool)
|
|
39
|
+
const verified = entry?.["verified"]
|
|
40
|
+
const models: Array<ModelEntry> = []
|
|
41
|
+
for (const m of modelEntries(entry)) {
|
|
42
|
+
const id = m["id"]
|
|
43
|
+
const kind = m["kind"]
|
|
44
|
+
if (typeof id !== "string" || (kind !== "alias" && kind !== "id")) continue
|
|
45
|
+
const note = m["note"]
|
|
46
|
+
models.push(typeof note === "string" ? { id, kind, note } : { id, kind })
|
|
47
|
+
}
|
|
48
|
+
return { verified: typeof verified === "string" ? verified : "?", models }
|
|
27
49
|
}
|
|
28
50
|
|
|
29
51
|
export function printModels(ctx: Ctx, tool: string): void {
|
|
@@ -35,7 +57,7 @@ export function printModels(ctx: Ctx, tool: string): void {
|
|
|
35
57
|
}
|
|
36
58
|
const verified = typeof entry["verified"] === "string" ? entry["verified"] : "?"
|
|
37
59
|
const lines = [`Available ${tool} models (kit-verified ${verified} — SoT/models.json):`]
|
|
38
|
-
for (const m of modelEntries(
|
|
60
|
+
for (const m of modelEntries(entry)) {
|
|
39
61
|
const note = typeof m["note"] === "string" ? ` — ${m["note"]}` : ""
|
|
40
62
|
lines.push(` ${String(m["id"] ?? "")}${note}`)
|
|
41
63
|
}
|
|
@@ -46,7 +68,7 @@ export function printModels(ctx: Ctx, tool: string): void {
|
|
|
46
68
|
|
|
47
69
|
export function validateClaudeModel(ctx: Ctx, m: string): boolean {
|
|
48
70
|
if (m === "") return false
|
|
49
|
-
if (
|
|
71
|
+
if (modelCatalog("claude").models.some((entry) => entry.id === m)) return true
|
|
50
72
|
if (m.startsWith("claude-")) {
|
|
51
73
|
ctx.services.logger.warn(`Claude model '${m}' is not in the kit-verified catalog (SoT/models.json) — applying anyway`)
|
|
52
74
|
return true
|
|
@@ -56,7 +78,7 @@ export function validateClaudeModel(ctx: Ctx, m: string): boolean {
|
|
|
56
78
|
|
|
57
79
|
export function validateCodexModel(ctx: Ctx, m: string): boolean {
|
|
58
80
|
if (!/^[A-Za-z0-9._-]+$/.test(m)) return false
|
|
59
|
-
if (!
|
|
81
|
+
if (!modelCatalog("codex").models.some((entry) => entry.id === m)) {
|
|
60
82
|
ctx.services.logger.warn(
|
|
61
83
|
`Codex model '${m}' is not in the kit-verified catalog (SoT/models.json) — applying anyway (check ~/.codex/config.toml if Codex rejects it)`
|
|
62
84
|
)
|
|
@@ -10,7 +10,7 @@ import { isAbsolute, resolve } from "node:path"
|
|
|
10
10
|
|
|
11
11
|
import { payloadDisplayPath, payloadText, type PayloadPath } from "../payload"
|
|
12
12
|
import { bunBootstrap } from "./bun"
|
|
13
|
-
import {
|
|
13
|
+
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"
|
|
@@ -317,7 +317,7 @@ async function syncPlugins(ctx: Ctx): Promise<number> {
|
|
|
317
317
|
const { change, clearProgress, echo, progress, verbose, warn } = ctx.services.logger
|
|
318
318
|
|
|
319
319
|
if (ctx.dryRun) {
|
|
320
|
-
const piIntercomPin = field(
|
|
320
|
+
const piIntercomPin = field("pi-intercom", "verified")
|
|
321
321
|
for (const pluginId of MARKETPLACE_PLUGIN_IDS) {
|
|
322
322
|
echo(`[dry-run] omp plugin install --scope user ${pluginId}`)
|
|
323
323
|
}
|
|
@@ -345,7 +345,7 @@ async function syncPlugins(ctx: Ctx): Promise<number> {
|
|
|
345
345
|
)
|
|
346
346
|
return 0
|
|
347
347
|
}
|
|
348
|
-
const piIntercomPin = field(
|
|
348
|
+
const piIntercomPin = field("pi-intercom", "verified")
|
|
349
349
|
|
|
350
350
|
progress("Checking installed omp plugins...")
|
|
351
351
|
const installed = await installedPluginIdsFromCli()
|
|
@@ -1,62 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { posixHost } from "./posix"
|
|
2
2
|
import type { HostOs } from "./types"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
return `'${value.replaceAll("'", `'"'"'`)}'`
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const darwin: HostOs = {
|
|
4
|
+
export const darwin: HostOs = posixHost({
|
|
9
5
|
id: "darwin",
|
|
10
6
|
toolchainOs: "darwin",
|
|
11
7
|
supportsBubblewrap: false,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const scriptPath = p(directory, "install.sh")
|
|
18
|
-
return {
|
|
19
|
-
scriptPath,
|
|
20
|
-
download: {
|
|
21
|
-
command: "curl",
|
|
22
|
-
args: ["-fsSL", "https://bun.sh/install", "-o", scriptPath]
|
|
23
|
-
},
|
|
24
|
-
run: {
|
|
25
|
-
command: "bash",
|
|
26
|
-
args: [scriptPath, `bun-v${pin}`]
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
environmentSetting: (name, value) => ({
|
|
31
|
-
kind: "profile",
|
|
32
|
-
candidates: [".zshrc", ".bashrc", ".bash_profile", ".profile", ".zshenv"],
|
|
33
|
-
target: (shell) => {
|
|
34
|
-
const shellPath = shell ?? "bash"
|
|
35
|
-
const shellName = shellPath.slice(shellPath.lastIndexOf("/") + 1)
|
|
36
|
-
return shellName === "zsh" ? ".zshrc" : shellName === "bash" ? ".bashrc" : ".profile"
|
|
37
|
-
},
|
|
38
|
-
line: `export ${name}=${value}`
|
|
39
|
-
}),
|
|
40
|
-
statusLineCommand: (bun, script) => {
|
|
41
|
-
const bunLiteral = posixLiteral(bun)
|
|
42
|
-
const scriptLiteral = posixLiteral(script)
|
|
43
|
-
return `test -x ${bunLiteral} && test -f ${scriptLiteral} && exec ${bunLiteral} ${scriptLiteral} || true`
|
|
44
|
-
},
|
|
45
|
-
failureHookCommand: (command) => command,
|
|
46
|
-
installHint: (tool) => {
|
|
47
|
-
switch (tool) {
|
|
48
|
-
case "git":
|
|
49
|
-
return "brew install git"
|
|
50
|
-
case "jq":
|
|
51
|
-
return "brew install jq"
|
|
52
|
-
case "curl":
|
|
53
|
-
return "brew install curl"
|
|
54
|
-
case "ffplay":
|
|
55
|
-
return "brew install ffmpeg"
|
|
56
|
-
case "claude":
|
|
57
|
-
return "curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install.sh && bash /tmp/claude-install.sh"
|
|
58
|
-
case "codex":
|
|
59
|
-
return 'tmp=$(mktemp) && curl -fsSL https://chatgpt.com/codex/install.sh -o "$tmp" && CODEX_NON_INTERACTIVE=1 sh "$tmp"'
|
|
60
|
-
}
|
|
8
|
+
packageManagerHints: {
|
|
9
|
+
git: "brew install git",
|
|
10
|
+
jq: "brew install jq",
|
|
11
|
+
curl: "brew install curl",
|
|
12
|
+
ffplay: "brew install ffmpeg"
|
|
61
13
|
}
|
|
62
|
-
}
|
|
14
|
+
})
|
|
@@ -1,62 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { posixHost } from "./posix"
|
|
2
2
|
import type { HostOs } from "./types"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
return `'${value.replaceAll("'", `'"'"'`)}'`
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const linux: HostOs = {
|
|
4
|
+
export const linux: HostOs = posixHost({
|
|
9
5
|
id: "linux",
|
|
10
6
|
toolchainOs: "linux",
|
|
11
7
|
supportsBubblewrap: true,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const scriptPath = p(directory, "install.sh")
|
|
18
|
-
return {
|
|
19
|
-
scriptPath,
|
|
20
|
-
download: {
|
|
21
|
-
command: "curl",
|
|
22
|
-
args: ["-fsSL", "https://bun.sh/install", "-o", scriptPath]
|
|
23
|
-
},
|
|
24
|
-
run: {
|
|
25
|
-
command: "bash",
|
|
26
|
-
args: [scriptPath, `bun-v${pin}`]
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
environmentSetting: (name, value) => ({
|
|
31
|
-
kind: "profile",
|
|
32
|
-
candidates: [".zshrc", ".bashrc", ".bash_profile", ".profile", ".zshenv"],
|
|
33
|
-
target: (shell) => {
|
|
34
|
-
const shellPath = shell ?? "bash"
|
|
35
|
-
const shellName = shellPath.slice(shellPath.lastIndexOf("/") + 1)
|
|
36
|
-
return shellName === "zsh" ? ".zshrc" : shellName === "bash" ? ".bashrc" : ".profile"
|
|
37
|
-
},
|
|
38
|
-
line: `export ${name}=${value}`
|
|
39
|
-
}),
|
|
40
|
-
statusLineCommand: (bun, script) => {
|
|
41
|
-
const bunLiteral = posixLiteral(bun)
|
|
42
|
-
const scriptLiteral = posixLiteral(script)
|
|
43
|
-
return `test -x ${bunLiteral} && test -f ${scriptLiteral} && exec ${bunLiteral} ${scriptLiteral} || true`
|
|
44
|
-
},
|
|
45
|
-
failureHookCommand: (command) => command,
|
|
46
|
-
installHint: (tool) => {
|
|
47
|
-
switch (tool) {
|
|
48
|
-
case "git":
|
|
49
|
-
return "sudo apt install -y git (or your distro's package manager)"
|
|
50
|
-
case "jq":
|
|
51
|
-
return "sudo apt install -y jq"
|
|
52
|
-
case "curl":
|
|
53
|
-
return "sudo apt install -y curl"
|
|
54
|
-
case "ffplay":
|
|
55
|
-
return "sudo apt install -y ffmpeg"
|
|
56
|
-
case "claude":
|
|
57
|
-
return "curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install.sh && bash /tmp/claude-install.sh"
|
|
58
|
-
case "codex":
|
|
59
|
-
return 'tmp=$(mktemp) && curl -fsSL https://chatgpt.com/codex/install.sh -o "$tmp" && CODEX_NON_INTERACTIVE=1 sh "$tmp"'
|
|
60
|
-
}
|
|
8
|
+
packageManagerHints: {
|
|
9
|
+
git: "sudo apt install -y git (or your distro's package manager)",
|
|
10
|
+
jq: "sudo apt install -y jq",
|
|
11
|
+
curl: "sudo apt install -y curl",
|
|
12
|
+
ffplay: "sudo apt install -y ffmpeg"
|
|
61
13
|
}
|
|
62
|
-
}
|
|
14
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// No import from "../exec": exec.ts imports "./os", which evaluates linux and darwin
|
|
2
|
+
// through this module. A slash join keeps the same host-stable path p() would produce.
|
|
3
|
+
import type { HintedTool, HostOs } from "./types"
|
|
4
|
+
|
|
5
|
+
function posixLiteral(value: string): string {
|
|
6
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Package-manager hints for the tools a POSIX host installs through its own package manager. */
|
|
10
|
+
export type PackageManagerHints = Readonly<Record<Exclude<HintedTool, "claude" | "codex">, string>>
|
|
11
|
+
|
|
12
|
+
const UPSTREAM_INSTALLER_HINTS: Readonly<Record<"claude" | "codex", string>> = {
|
|
13
|
+
claude: "curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install.sh && bash /tmp/claude-install.sh",
|
|
14
|
+
codex: 'tmp=$(mktemp) && curl -fsSL https://chatgpt.com/codex/install.sh -o "$tmp" && CODEX_NON_INTERACTIVE=1 sh "$tmp"'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface PosixHostInputs {
|
|
18
|
+
readonly id: HostOs["id"]
|
|
19
|
+
readonly toolchainOs: HostOs["toolchainOs"]
|
|
20
|
+
readonly supportsBubblewrap: boolean
|
|
21
|
+
readonly packageManagerHints: PackageManagerHints
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Members shared by every POSIX host; linux and darwin differ only in the inputs. */
|
|
25
|
+
export function posixHost(inputs: PosixHostInputs): HostOs {
|
|
26
|
+
return {
|
|
27
|
+
id: inputs.id,
|
|
28
|
+
toolchainOs: inputs.toolchainOs,
|
|
29
|
+
supportsBubblewrap: inputs.supportsBubblewrap,
|
|
30
|
+
directoryLinkKinds: ["symlink"],
|
|
31
|
+
executableSuffixes: [""],
|
|
32
|
+
invoke: (executablePath, args) => ({ command: executablePath, args }),
|
|
33
|
+
bunExecutableName: "bun",
|
|
34
|
+
bunInstaller: (pin, directory) => {
|
|
35
|
+
const scriptPath = `${directory}/install.sh`
|
|
36
|
+
return {
|
|
37
|
+
scriptPath,
|
|
38
|
+
download: {
|
|
39
|
+
command: "curl",
|
|
40
|
+
args: ["-fsSL", "https://bun.sh/install", "-o", scriptPath]
|
|
41
|
+
},
|
|
42
|
+
run: {
|
|
43
|
+
command: "bash",
|
|
44
|
+
args: [scriptPath, `bun-v${pin}`]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
environmentSetting: (name, value) => ({
|
|
49
|
+
kind: "profile",
|
|
50
|
+
candidates: [".zshrc", ".bashrc", ".bash_profile", ".profile", ".zshenv"],
|
|
51
|
+
target: (shell) => {
|
|
52
|
+
const shellPath = shell ?? "bash"
|
|
53
|
+
const shellName = shellPath.slice(shellPath.lastIndexOf("/") + 1)
|
|
54
|
+
return shellName === "zsh" ? ".zshrc" : shellName === "bash" ? ".bashrc" : ".profile"
|
|
55
|
+
},
|
|
56
|
+
line: `export ${name}=${value}`
|
|
57
|
+
}),
|
|
58
|
+
statusLineCommand: (bun, script) => {
|
|
59
|
+
const bunLiteral = posixLiteral(bun)
|
|
60
|
+
const scriptLiteral = posixLiteral(script)
|
|
61
|
+
return `test -x ${bunLiteral} && test -f ${scriptLiteral} && exec ${bunLiteral} ${scriptLiteral} || true`
|
|
62
|
+
},
|
|
63
|
+
failureHookCommand: (command) => command,
|
|
64
|
+
installHint: (tool) =>
|
|
65
|
+
tool === "claude" || tool === "codex" ? UPSTREAM_INSTALLER_HINTS[tool] : inputs.packageManagerHints[tool]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -63,7 +63,7 @@ export const HOST_TARGETS: ReadonlyArray<HostTarget> = [
|
|
|
63
63
|
target("windows-arm64", "windows", "arm64", [], ["ARM64"])
|
|
64
64
|
]
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
function targetFor(platform: PlatformName, arch: string): HostTarget | undefined {
|
|
67
67
|
return HOST_TARGETS.find((t) => t.platform === platform && t.arch === arch)
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "node:fs"
|
|
18
18
|
import { dirname, relative, resolve } from "node:path"
|
|
19
19
|
import { payloadText } from "../payload"
|
|
20
|
-
import { p, spawnProcess,
|
|
20
|
+
import { p, spawnProcess, writeTextIfChanged } from "./exec"
|
|
21
21
|
import type { Ctx } from "./index"
|
|
22
22
|
import { compareCodepoints } from "./jq"
|
|
23
23
|
import { hostOs, type DirectoryLinkKind } from "./os"
|
|
@@ -50,7 +50,7 @@ export async function skillsSync(ctx: Ctx): Promise<SkillsState> {
|
|
|
50
50
|
|
|
51
51
|
/** skills::_skills_cli — the pinned npx package spec. */
|
|
52
52
|
function skillsCli(ctx: Ctx): string {
|
|
53
|
-
const version = field(
|
|
53
|
+
const version = field("skills-cli", "verified")
|
|
54
54
|
if (version !== "") return `skills@${version}`
|
|
55
55
|
ctx.services.logger.err("Universal skills sync aborted because SoT/toolchain.json has no verified skills-cli pin")
|
|
56
56
|
throw new ExitError(1)
|
|
@@ -365,7 +365,7 @@ function updateSnapshot(ctx: Ctx, manifest: string, snapshot: string, failedRemo
|
|
|
365
365
|
|
|
366
366
|
mkdirSync(ctx.agentsDir, { recursive: true })
|
|
367
367
|
const sorted = [...new Set([...normalizeManifest(manifest), ...failedRemovals])].sort(compareCodepoints)
|
|
368
|
-
|
|
368
|
+
writeTextIfChanged(snapshot, sorted.length > 0 ? `${sorted.join("\n")}\n` : "")
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
// -------------------------------------------------------------- summary ----
|
|
@@ -14,7 +14,7 @@ function manifest(): { [k: string]: Json } {
|
|
|
14
14
|
return tools !== undefined && isObject(tools) ? tools : {}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function field(
|
|
17
|
+
export function field(tool: string, name: string): string {
|
|
18
18
|
const entry = manifest()[tool]
|
|
19
19
|
if (entry === undefined || !isObject(entry)) return ""
|
|
20
20
|
const v = entry[name]
|
|
@@ -22,7 +22,7 @@ export function field(ctx: Ctx, tool: string, name: string): string {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** toolchain::_is_newer — numeric per dotted field, GNU-sort last-resort tie-break. */
|
|
25
|
-
|
|
25
|
+
function isNewer(a: string, b: string): boolean {
|
|
26
26
|
if (a === "" || b === "" || a === b) return false
|
|
27
27
|
const fa = a.split(".")
|
|
28
28
|
const fb = b.split(".")
|
|
@@ -86,14 +86,14 @@ export async function report(ctx: Ctx): Promise<void> {
|
|
|
86
86
|
const pn = ctx.services.platform.name()
|
|
87
87
|
const platformOs = hostOs(pn).toolchainOs
|
|
88
88
|
for (const tool of Object.keys(manifest()).sort(compareCodepoints)) {
|
|
89
|
-
const os = field(
|
|
89
|
+
const os = field(tool, "os")
|
|
90
90
|
if (os !== "" && platformOs !== "" && os !== platformOs) continue
|
|
91
|
-
const kind = field(
|
|
92
|
-
const floor = field(
|
|
93
|
-
const verified = field(
|
|
91
|
+
const kind = field(tool, "kind")
|
|
92
|
+
const floor = field(tool, "floor")
|
|
93
|
+
const verified = field(tool, "verified")
|
|
94
94
|
const dash = (v: string): string => (v !== "" ? v : "-")
|
|
95
95
|
if (kind === "pin") {
|
|
96
|
-
const via = field(
|
|
96
|
+
const via = field(tool, "via")
|
|
97
97
|
echo(row([tool, kind, `(${via !== "" ? via : "npx"})`, dash(floor), dash(verified), "pinned"]))
|
|
98
98
|
continue
|
|
99
99
|
}
|
|
@@ -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.6"
|
|
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",
|
|
@@ -18,7 +18,7 @@ export const GENERATED_PAYLOAD_TEXT = {
|
|
|
18
18
|
"SoT/.codex/plugins/marketplace.json": "{\n \"name\": \"docks\",\n \"interface\": {\n \"displayName\": \"DocksDocks\"\n },\n \"plugins\": [\n {\n \"name\": \"docks\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/docks\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"plan-lifecycle\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/plan-lifecycle\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n }\n ]\n}\n",
|
|
19
19
|
"SoT/.codex/rules/docks.rules": "prefix_rule(pattern=[\"pwd\"], decision=\"allow\")\nprefix_rule(pattern=[\"ls\"], decision=\"allow\")\nprefix_rule(pattern=[\"cat\"], decision=\"allow\")\nprefix_rule(pattern=[\"head\"], decision=\"allow\")\nprefix_rule(pattern=[\"tail\"], decision=\"allow\")\nprefix_rule(pattern=[\"wc\"], decision=\"allow\")\nprefix_rule(pattern=[\"nl\"], decision=\"allow\")\nprefix_rule(pattern=[\"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"sort\"], decision=\"allow\")\nprefix_rule(pattern=[\"uniq\"], decision=\"allow\")\nprefix_rule(pattern=[\"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"which\"], decision=\"allow\")\nprefix_rule(pattern=[\"date\"], decision=\"allow\")\nprefix_rule(pattern=[\"basename\"], decision=\"allow\")\nprefix_rule(pattern=[\"dirname\"], decision=\"allow\")\nprefix_rule(pattern=[\"realpath\"], decision=\"allow\")\nprefix_rule(pattern=[\"readlink\"], decision=\"allow\")\nprefix_rule(pattern=[\"jq\"], decision=\"allow\")\nprefix_rule(pattern=[\"tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"cut\"], decision=\"allow\")\nprefix_rule(pattern=[\"tr\"], decision=\"allow\")\nprefix_rule(pattern=[\"echo\"], decision=\"allow\")\nprefix_rule(pattern=[\"printf\"], decision=\"allow\")\nprefix_rule(pattern=[\"printenv\"], decision=\"allow\")\nprefix_rule(pattern=[\"uname\"], decision=\"allow\")\nprefix_rule(pattern=[\"file\"], decision=\"allow\")\nprefix_rule(pattern=[\"stat\"], decision=\"allow\")\nprefix_rule(pattern=[\"du\"], decision=\"allow\")\nprefix_rule(pattern=[\"id\"], decision=\"allow\")\nprefix_rule(pattern=[\"whoami\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"log\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"show\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"blame\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"rev-parse\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-files\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"--show-current\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"-vv\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"mv\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"gh\", \"pr\", \"view\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"list\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"checks\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"docker\", \"ps\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"push\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"reset\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"clean\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"merge\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"rebase\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"checkout\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"mv\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chmod\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chown\"], decision=\"prompt\")\nprefix_rule(pattern=[\"kill\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pkill\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"npm\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"npm\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"uninstall\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"docker\", \"run\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"volume\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"system\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"stop\"], decision=\"prompt\")\n\n# `tail -f`/`--follow` never returns and hangs the agent (bare `tail` stays allowed above).\nprefix_rule(pattern=[\"tail\", \"-f\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tail\", \"--follow\"], decision=\"prompt\")\n# rg and `sed -n` are prompt, NOT allow: argv-prefix matching cannot gate their\n# code-exec forms (rg --pre=CMD or a reordered --pre; sed -n 'e CMD' / -ni) while a\n# shorter allow prefix would auto-approve the whole command.\nprefix_rule(pattern=[\"rg\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-n\"], decision=\"prompt\")\nprefix_rule(pattern=[\"find\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-i\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"--in-place\"], decision=\"prompt\")\nprefix_rule(pattern=[\"awk\"], decision=\"prompt\")\nprefix_rule(pattern=[\"xargs\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tee\"], decision=\"prompt\")\nprefix_rule(pattern=[\"curl\"], decision=\"prompt\")\nprefix_rule(pattern=[\"env\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"sudo\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"eval\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"mkfs\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"dd\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"master\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"master\"], decision=\"forbidden\")\n",
|
|
20
20
|
"SoT/.omp/AGENTS.md": "# Global OMP guidance\n\n- Verify unfamiliar or version-sensitive APIs and configuration against current official documentation before implementation.\n- When the user asks for an assessment rather than a change, report findings without editing.\n- For Docks plan reviews, cross-company review is standing-authorized; host security policy still applies.\n- Please remove all mannered prose.\n\n## Asking me things\n\nA question typed in prose is just text I may or may not act on. The `ask` tool renders a\nblocking picker, waits indefinitely (`ask.timeout = 0`), and records my answer in the\ntranscript. If you actually need an answer, it MUST go through `ask`.\n\nMUST use `ask` before:\n- Anything irreversible or destructive: deleting/overwriting files or data you did not create,\n force-push, history rewrite, dropping tables, running migrations, mass rename, touching\n secrets/credentials, or publishing outward (release, upstream PR, issue, comment).\n- Two or more viable approaches whose tradeoffs are mine to own: schema/API/protocol shape,\n adding a dependency, or establishing a convention this repo does not already have.\n- A fact only I hold: intended semantics of an ambiguous requirement, which of several\n conflicting existing patterns is canonical, or which environment/account/target to use.\n- A request that contradicts the repo: surface the conflict and let me resolve it; never\n silently pick one side.\n\nIf `ask` is not registered — subagent, headless, or `-p` print runs, where `hasUI` is false —\nthe MUST above cannot be satisfied: do not fabricate the call and do not stall on it. Take the\nconservative reversible option and put the question, plus the assumption you made, in your\nfinal report so whoever spawned you can decide.\n\nNEVER use `ask` for:\n- Permission to begin, or to confirm scope already stated in the request.\n- Anything a tool, grep, or doc can answer — go read it.\n- A cheap reversible choice — take the conservative option and say which you took.\n- Something already answered earlier in the conversation.\n\nBatch every open question into one `ask` call with multiple questions; do not serialize\nround trips. Being overruled ends the discussion — execute my call without relitigating.\n\n## Output Standard\n\nApply Simplified Technical English to all agent text. This includes responses, messages,\ndocumentation, comments, and interface text.\n\nReply in the language I use, and apply every rule below to that language.\n\nA rule that names English grammar applies only to English. The contraction ban is one\nsuch rule. In another language, follow the normal grammar of that language. Portuguese,\nSpanish, French, Italian, and German merge a preposition with an article, and that merge\nis required, not optional.\n\nTreat the word limits as approximate outside English. Some languages need more words to\ncarry the same content.\n\n- Use the simplest precise technical term.\n- Use each term consistently.\n- Expand an abbreviation at its first occurrence.\n- Explain a technical term when I ask for an explanation.\n- Write complete and grammatically correct sentences.\n- Use active voice and identify the actor.\n- Use the imperative form for instructions.\n- Put only one action in each instruction sentence.\n- Put a necessary condition before its instruction.\n- Use simple verb tenses.\n- Do not use contractions, idioms, or slang. Avoid humor and rhetorical questions.\n- Keep procedural sentences to 20 words or fewer.\n- Keep descriptive sentences to 25 words or fewer.\n- Keep each paragraph to one topic and six sentences or fewer.\n- Do not use more than three nouns together.\n- Use vertical lists for complex information.\n- Put a warning or caution before a related hazardous instruction.\n\n### Naming\n\n- Say what the thing does before you name it. Put the technical term after the plain\n description, once, in parentheses.\n- Do not use a technical term as the only name for something you just introduced.\n- Prefer the short common word. Use \"use\", not \"utilize\". Use \"set up\", not \"provision\".\n- Do not explain by metaphor alone. A metaphor may follow a literal statement.\n",
|
|
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:
|
|
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: low\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
24
|
} as const
|
|
@@ -48,4 +48,4 @@ export const GENERATED_PAYLOAD_PATHS = [
|
|
|
48
48
|
"notification.mp3"
|
|
49
49
|
] as const
|
|
50
50
|
|
|
51
|
-
export const GENERATED_PAYLOAD_HASH = "
|
|
51
|
+
export const GENERATED_PAYLOAD_HASH = "df37d563d668ab4f09d4b0ab50f9e7ff81612c2032b57870b0f919aacef43855"
|