lazycodex-ai 5.1.4 → 5.1.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/dist/cli/index.js +465 -187
- package/dist/cli-node/index.js +465 -187
- package/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +309 -133
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +25 -18
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +25 -18
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +25 -18
- package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +311 -135
- package/packages/prompts-core/prompts/ultrawork/codex.md +25 -18
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// omo-codex-install:
|
|
2
|
+
// omo-codex-install:926babe1233096485f8e4db8e616e5deaa07ce9e732fdb0477b03390f4f15efa:4d5d406ea49f40877a220f86f4699fafaf2529a5afcea4e30abccfe48760df2c
|
|
3
3
|
var __esm = (fn, res, err) => () => {
|
|
4
4
|
if (fn)
|
|
5
5
|
try {
|
|
@@ -9984,7 +9984,7 @@ var package_default;
|
|
|
9984
9984
|
var init_package = __esm(() => {
|
|
9985
9985
|
package_default = {
|
|
9986
9986
|
name: "@oh-my-opencode/omo-codex",
|
|
9987
|
-
version: "5.1.
|
|
9987
|
+
version: "5.1.6",
|
|
9988
9988
|
type: "module",
|
|
9989
9989
|
private: true,
|
|
9990
9990
|
description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
|
|
@@ -19252,7 +19252,6 @@ function normalizeLegacyModelFields(entry) {
|
|
|
19252
19252
|
delete normalized["reasoningEffort"];
|
|
19253
19253
|
delete normalized["thinking"];
|
|
19254
19254
|
delete normalized["textVerbosity"];
|
|
19255
|
-
delete normalized["maxTokens"];
|
|
19256
19255
|
delete normalized["providerOptions"];
|
|
19257
19256
|
if (typeof entry["model"] === "string")
|
|
19258
19257
|
normalized["model"] = canonicalModelString(entry["model"]);
|
|
@@ -19270,10 +19269,15 @@ function normalizeLegacyModelFields(entry) {
|
|
|
19270
19269
|
providerOptions["textVerbosity"] = entry["textVerbosity"];
|
|
19271
19270
|
if (Object.keys(providerOptions).length > 0)
|
|
19272
19271
|
normalized["provider_options"] = providerOptions;
|
|
19273
|
-
if (entry["max_tokens"] !== undefined)
|
|
19272
|
+
if (entry["max_tokens"] !== undefined) {
|
|
19274
19273
|
normalized["max_tokens"] = entry["max_tokens"];
|
|
19275
|
-
|
|
19274
|
+
if (entry["maxTokens"] === undefined || typeof entry["maxTokens"] === "number") {
|
|
19275
|
+
delete normalized["maxTokens"];
|
|
19276
|
+
}
|
|
19277
|
+
} else if (typeof entry["maxTokens"] === "number") {
|
|
19276
19278
|
normalized["max_tokens"] = entry["maxTokens"];
|
|
19279
|
+
delete normalized["maxTokens"];
|
|
19280
|
+
}
|
|
19277
19281
|
return normalized;
|
|
19278
19282
|
}
|
|
19279
19283
|
var OmoLegacyFallbackModelObjectInputSchema = object({
|
|
@@ -19303,6 +19307,19 @@ function isRecord3(value) {
|
|
|
19303
19307
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19304
19308
|
}
|
|
19305
19309
|
var OmoAgentModelEntrySchema = union([string2(), OmoFallbackModelObjectSchema]);
|
|
19310
|
+
var PermissionValueSchema = _enum(["ask", "allow", "deny"]);
|
|
19311
|
+
var BashPermissionSchema = union([
|
|
19312
|
+
PermissionValueSchema,
|
|
19313
|
+
record(string2(), PermissionValueSchema)
|
|
19314
|
+
]);
|
|
19315
|
+
var OmoAgentPermissionSchema = object({
|
|
19316
|
+
edit: PermissionValueSchema.optional(),
|
|
19317
|
+
bash: BashPermissionSchema.optional(),
|
|
19318
|
+
webfetch: PermissionValueSchema.optional(),
|
|
19319
|
+
task: PermissionValueSchema.optional(),
|
|
19320
|
+
doom_loop: PermissionValueSchema.optional(),
|
|
19321
|
+
external_directory: PermissionValueSchema.optional()
|
|
19322
|
+
}).catchall(PermissionValueSchema.optional());
|
|
19306
19323
|
var OmoAgentDefInputSchema = object({
|
|
19307
19324
|
description: string2().optional(),
|
|
19308
19325
|
prompt: string2().optional(),
|
|
@@ -19319,7 +19336,9 @@ var OmoAgentDefInputSchema = object({
|
|
|
19319
19336
|
disallowed_tools: array(string2()).optional(),
|
|
19320
19337
|
max_turns: number2().int().nonnegative().optional(),
|
|
19321
19338
|
temperature: number2().min(0).max(2).optional(),
|
|
19322
|
-
disable: boolean2().optional()
|
|
19339
|
+
disable: boolean2().optional(),
|
|
19340
|
+
permission: OmoAgentPermissionSchema.optional(),
|
|
19341
|
+
prompt_append: string2().optional()
|
|
19323
19342
|
}).strict();
|
|
19324
19343
|
var OmoAgentDefSchema = preprocess((value) => isRecord3(value) ? normalizeLegacyModelFields(value) : value, OmoAgentDefInputSchema);
|
|
19325
19344
|
var OmoAgentsConfigSchema = record(string2(), OmoAgentDefSchema);
|
|
@@ -20008,6 +20027,16 @@ function canonicalizeLegacyHarnessBlocks(document) {
|
|
|
20008
20027
|
return { document: canonicalized, renames };
|
|
20009
20028
|
}
|
|
20010
20029
|
|
|
20030
|
+
// packages/omo-config-core/src/loader/types.ts
|
|
20031
|
+
import { existsSync as existsSync3, lstatSync, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
20032
|
+
var MERGED_OMO_CONFIG_PATH = "(merged omo config)";
|
|
20033
|
+
var DEFAULT_READ_FILE_SYSTEM = {
|
|
20034
|
+
existsSync: existsSync3,
|
|
20035
|
+
lstatSync,
|
|
20036
|
+
readFileSync: readFileSync2,
|
|
20037
|
+
realpathSync: realpathSync2
|
|
20038
|
+
};
|
|
20039
|
+
|
|
20011
20040
|
// node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
20012
20041
|
function createScanner(text, ignoreTrivia = false) {
|
|
20013
20042
|
const len = text.length;
|
|
@@ -20882,6 +20911,255 @@ function mergeOmoConfigRecords(base, override) {
|
|
|
20882
20911
|
return result;
|
|
20883
20912
|
}
|
|
20884
20913
|
|
|
20914
|
+
// packages/omo-config-core/src/loader/prune-invalid-leaves.ts
|
|
20915
|
+
var MAX_PRUNE_PASSES = 32;
|
|
20916
|
+
function isContainer(value) {
|
|
20917
|
+
return typeof value === "object" && value !== null;
|
|
20918
|
+
}
|
|
20919
|
+
function childOf(container, segment) {
|
|
20920
|
+
return Array.isArray(container) ? container[Number(segment)] : container[String(segment)];
|
|
20921
|
+
}
|
|
20922
|
+
function hasChild(container, segment) {
|
|
20923
|
+
if (Array.isArray(container)) {
|
|
20924
|
+
const index = Number(segment);
|
|
20925
|
+
return Number.isInteger(index) && index >= 0 && index < container.length;
|
|
20926
|
+
}
|
|
20927
|
+
return Object.hasOwn(container, String(segment));
|
|
20928
|
+
}
|
|
20929
|
+
function segmentsOf(path) {
|
|
20930
|
+
return path.map((segment) => typeof segment === "number" ? segment : String(segment));
|
|
20931
|
+
}
|
|
20932
|
+
function targetPaths(root, issue) {
|
|
20933
|
+
const base = segmentsOf(issue.path);
|
|
20934
|
+
if (issue.code === "unrecognized_keys")
|
|
20935
|
+
return issue.keys.map((key) => [...base, key]);
|
|
20936
|
+
let node = root;
|
|
20937
|
+
let depth = 0;
|
|
20938
|
+
for (const segment of base) {
|
|
20939
|
+
if (!isContainer(node) || !hasChild(node, segment))
|
|
20940
|
+
break;
|
|
20941
|
+
node = childOf(node, segment);
|
|
20942
|
+
depth += 1;
|
|
20943
|
+
}
|
|
20944
|
+
return [base.slice(0, depth)];
|
|
20945
|
+
}
|
|
20946
|
+
function isAncestor(ancestor, path) {
|
|
20947
|
+
return ancestor.length < path.length && ancestor.every((segment, index) => segment === path[index]);
|
|
20948
|
+
}
|
|
20949
|
+
function compareDescending(left, right) {
|
|
20950
|
+
const length = Math.min(left.length, right.length);
|
|
20951
|
+
for (let index = 0;index < length; index += 1) {
|
|
20952
|
+
const a = left[index];
|
|
20953
|
+
const b = right[index];
|
|
20954
|
+
if (a === b)
|
|
20955
|
+
continue;
|
|
20956
|
+
if (typeof a === "number" && typeof b === "number")
|
|
20957
|
+
return b - a;
|
|
20958
|
+
return String(b).localeCompare(String(a));
|
|
20959
|
+
}
|
|
20960
|
+
return right.length - left.length;
|
|
20961
|
+
}
|
|
20962
|
+
function removeChild(container, segment) {
|
|
20963
|
+
if (Array.isArray(container))
|
|
20964
|
+
container.splice(Number(segment), 1);
|
|
20965
|
+
else
|
|
20966
|
+
delete container[String(segment)];
|
|
20967
|
+
}
|
|
20968
|
+
function isEmpty(container) {
|
|
20969
|
+
return Array.isArray(container) ? container.length === 0 : Object.keys(container).length === 0;
|
|
20970
|
+
}
|
|
20971
|
+
function removePathAndEmptiedAncestors(root, path) {
|
|
20972
|
+
const chain = [root];
|
|
20973
|
+
for (const segment of path.slice(0, -1)) {
|
|
20974
|
+
const current = chain[chain.length - 1];
|
|
20975
|
+
if (current === undefined)
|
|
20976
|
+
return;
|
|
20977
|
+
const next = childOf(current, segment);
|
|
20978
|
+
if (!isContainer(next))
|
|
20979
|
+
return;
|
|
20980
|
+
chain.push(next);
|
|
20981
|
+
}
|
|
20982
|
+
for (let depth = path.length - 1;depth >= 0; depth -= 1) {
|
|
20983
|
+
const container = chain[depth];
|
|
20984
|
+
const segment = path[depth];
|
|
20985
|
+
if (container === undefined || segment === undefined)
|
|
20986
|
+
return;
|
|
20987
|
+
removeChild(container, segment);
|
|
20988
|
+
if (depth === 0 || !isEmpty(container))
|
|
20989
|
+
return;
|
|
20990
|
+
}
|
|
20991
|
+
}
|
|
20992
|
+
function prunePass(root, issues) {
|
|
20993
|
+
const byKey = new Map;
|
|
20994
|
+
for (const issue of issues) {
|
|
20995
|
+
for (const path of targetPaths(root, issue)) {
|
|
20996
|
+
if (path.length === 0)
|
|
20997
|
+
return null;
|
|
20998
|
+
const key = path.map((segment) => String(segment)).join(".");
|
|
20999
|
+
if (!byKey.has(key))
|
|
21000
|
+
byKey.set(key, { key, message: issue.message, path });
|
|
21001
|
+
}
|
|
21002
|
+
}
|
|
21003
|
+
const targets = [...byKey.values()];
|
|
21004
|
+
const outermost = targets.filter((target) => !targets.some((other) => isAncestor(other.path, target.path))).sort((left, right) => compareDescending(left.path, right.path));
|
|
21005
|
+
const next = structuredClone(root);
|
|
21006
|
+
for (const target of outermost)
|
|
21007
|
+
removePathAndEmptiedAncestors(next, target.path);
|
|
21008
|
+
return { dropped: outermost, next };
|
|
21009
|
+
}
|
|
21010
|
+
function pruneInvalidConfigPaths(config, issues, validate, maxPasses = MAX_PRUNE_PASSES) {
|
|
21011
|
+
const dropped = [];
|
|
21012
|
+
let current = config;
|
|
21013
|
+
let pending = issues;
|
|
21014
|
+
for (let pass = 0;pass < maxPasses; pass += 1) {
|
|
21015
|
+
const step = prunePass(current, pending);
|
|
21016
|
+
if (step === null || step.dropped.length === 0)
|
|
21017
|
+
return { ok: false, dropped };
|
|
21018
|
+
dropped.push(...step.dropped);
|
|
21019
|
+
current = step.next;
|
|
21020
|
+
if (Object.keys(current).length === 0)
|
|
21021
|
+
return { ok: false, dropped };
|
|
21022
|
+
const validation = validate(current);
|
|
21023
|
+
if (validation.success)
|
|
21024
|
+
return { ok: true, config: current, dropped };
|
|
21025
|
+
pending = validation.issues;
|
|
21026
|
+
}
|
|
21027
|
+
return { ok: false, dropped };
|
|
21028
|
+
}
|
|
21029
|
+
|
|
21030
|
+
// packages/omo-config-core/src/loader/layer-validation.ts
|
|
21031
|
+
function validationDiagnostic(path, issues) {
|
|
21032
|
+
const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
|
|
21033
|
+
return {
|
|
21034
|
+
kind: "validation",
|
|
21035
|
+
message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
|
|
21036
|
+
path,
|
|
21037
|
+
issuePaths
|
|
21038
|
+
};
|
|
21039
|
+
}
|
|
21040
|
+
function invalidValueDiagnostics(path, dropped) {
|
|
21041
|
+
return dropped.map((entry) => ({
|
|
21042
|
+
kind: "invalid-value",
|
|
21043
|
+
message: `Ignored invalid value in ${path}: ${entry.key}: ${entry.message}`,
|
|
21044
|
+
path,
|
|
21045
|
+
issuePaths: [entry.key]
|
|
21046
|
+
}));
|
|
21047
|
+
}
|
|
21048
|
+
function unrecognizedKeyIssues(issues) {
|
|
21049
|
+
return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
|
|
21050
|
+
}
|
|
21051
|
+
function isRecord9(value) {
|
|
21052
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21053
|
+
}
|
|
21054
|
+
function sanitizeUnsafeKeys(value, path = []) {
|
|
21055
|
+
if (Array.isArray(value)) {
|
|
21056
|
+
const issues = [];
|
|
21057
|
+
const sanitized = value.map((entry, index) => {
|
|
21058
|
+
const nested = sanitizeUnsafeKeys(entry, [...path, String(index)]);
|
|
21059
|
+
issues.push(...nested.issues);
|
|
21060
|
+
return nested.value;
|
|
21061
|
+
});
|
|
21062
|
+
return { issues, value: sanitized };
|
|
21063
|
+
}
|
|
21064
|
+
if (!isRecord9(value))
|
|
21065
|
+
return { issues: [], value };
|
|
21066
|
+
const issues = [];
|
|
21067
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
21068
|
+
issues.push({ keys: ["__proto__"], path });
|
|
21069
|
+
}
|
|
21070
|
+
const sanitized = {};
|
|
21071
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
21072
|
+
if (isUnsafeObjectKey(key)) {
|
|
21073
|
+
issues.push({ keys: [key], path });
|
|
21074
|
+
continue;
|
|
21075
|
+
}
|
|
21076
|
+
const nested = sanitizeUnsafeKeys(entry, [...path, key]);
|
|
21077
|
+
issues.push(...nested.issues);
|
|
21078
|
+
sanitized[key] = nested.value;
|
|
21079
|
+
}
|
|
21080
|
+
return { issues, value: sanitized };
|
|
21081
|
+
}
|
|
21082
|
+
function containerAt(record, path) {
|
|
21083
|
+
let node = record;
|
|
21084
|
+
for (const segment of path) {
|
|
21085
|
+
if (Array.isArray(node)) {
|
|
21086
|
+
const index = Number(segment);
|
|
21087
|
+
if (!Number.isInteger(index) || index < 0 || index >= node.length)
|
|
21088
|
+
return null;
|
|
21089
|
+
node = node[index];
|
|
21090
|
+
} else if (isRecord9(node) && Object.hasOwn(node, segment)) {
|
|
21091
|
+
node = node[segment];
|
|
21092
|
+
} else {
|
|
21093
|
+
return null;
|
|
21094
|
+
}
|
|
21095
|
+
}
|
|
21096
|
+
return isRecord9(node) ? node : null;
|
|
21097
|
+
}
|
|
21098
|
+
function stripUnrecognizedKeys(record, issues) {
|
|
21099
|
+
const stripped = structuredClone(record);
|
|
21100
|
+
const issuePaths = [];
|
|
21101
|
+
for (const issue of issues) {
|
|
21102
|
+
const container = containerAt(stripped, issue.path);
|
|
21103
|
+
if (container === null)
|
|
21104
|
+
continue;
|
|
21105
|
+
for (const key of issue.keys) {
|
|
21106
|
+
delete container[key];
|
|
21107
|
+
issuePaths.push([...issue.path, key].join("."));
|
|
21108
|
+
}
|
|
21109
|
+
}
|
|
21110
|
+
return { issuePaths, stripped };
|
|
21111
|
+
}
|
|
21112
|
+
function toRecord(value) {
|
|
21113
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21114
|
+
return null;
|
|
21115
|
+
const record = {};
|
|
21116
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
21117
|
+
record[key] = entry;
|
|
21118
|
+
}
|
|
21119
|
+
return record;
|
|
21120
|
+
}
|
|
21121
|
+
var validateLayerRecord = (record) => {
|
|
21122
|
+
const parsed = OmoConfigLayerSchema.safeParse(record);
|
|
21123
|
+
return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
|
|
21124
|
+
};
|
|
21125
|
+
function validateConfigLayer(path, data) {
|
|
21126
|
+
const sanitized = sanitizeUnsafeKeys(data);
|
|
21127
|
+
const record = toRecord(sanitized.value);
|
|
21128
|
+
const unsafeIssuePaths = sanitized.issues.flatMap((issue) => issue.keys.map((key) => [...issue.path, key].join(".")));
|
|
21129
|
+
const unsafeDiagnostics = unsafeIssuePaths.length === 0 ? [] : [{ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${unsafeIssuePaths.join(", ")}`, path, issuePaths: unsafeIssuePaths }];
|
|
21130
|
+
const validation = OmoConfigLayerSchema.safeParse(sanitized.value);
|
|
21131
|
+
if (validation.success) {
|
|
21132
|
+
if (record !== null)
|
|
21133
|
+
return { loaded: true, diagnostics: unsafeDiagnostics, value: record };
|
|
21134
|
+
return {
|
|
21135
|
+
loaded: false,
|
|
21136
|
+
diagnostics: [{ kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path }]
|
|
21137
|
+
};
|
|
21138
|
+
}
|
|
21139
|
+
const rejected = { loaded: false, diagnostics: [validationDiagnostic(path, validation.error.issues)] };
|
|
21140
|
+
const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
|
|
21141
|
+
if (record === null)
|
|
21142
|
+
return rejected;
|
|
21143
|
+
let candidate = record;
|
|
21144
|
+
let issues = validation.error.issues;
|
|
21145
|
+
const diagnostics = [...unsafeDiagnostics];
|
|
21146
|
+
if (unknownIssues.length > 0) {
|
|
21147
|
+
const { issuePaths, stripped } = stripUnrecognizedKeys(record, unknownIssues);
|
|
21148
|
+
if (issuePaths.length > 0) {
|
|
21149
|
+
diagnostics.push({ kind: "unknown-keys", message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`, path, issuePaths });
|
|
21150
|
+
}
|
|
21151
|
+
const strippedValidation = validateLayerRecord(stripped);
|
|
21152
|
+
if (strippedValidation.success)
|
|
21153
|
+
return { loaded: true, diagnostics, value: stripped };
|
|
21154
|
+
candidate = stripped;
|
|
21155
|
+
issues = strippedValidation.issues;
|
|
21156
|
+
}
|
|
21157
|
+
const pruned = pruneInvalidConfigPaths(candidate, issues, validateLayerRecord);
|
|
21158
|
+
if (!pruned.ok)
|
|
21159
|
+
return rejected;
|
|
21160
|
+
return { loaded: true, diagnostics: [...diagnostics, ...invalidValueDiagnostics(path, pruned.dropped)], value: pruned.config };
|
|
21161
|
+
}
|
|
21162
|
+
|
|
20885
21163
|
// packages/omo-config-core/src/loader/paths.ts
|
|
20886
21164
|
import { userInfo } from "node:os";
|
|
20887
21165
|
import { dirname as dirname8, join as join18, posix, resolve as resolve7 } from "node:path";
|
|
@@ -20891,15 +21169,6 @@ function toPosixPath2(path) {
|
|
|
20891
21169
|
return path.split("\\").join("/");
|
|
20892
21170
|
}
|
|
20893
21171
|
|
|
20894
|
-
// packages/omo-config-core/src/loader/types.ts
|
|
20895
|
-
import { existsSync as existsSync3, lstatSync, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
20896
|
-
var DEFAULT_READ_FILE_SYSTEM = {
|
|
20897
|
-
existsSync: existsSync3,
|
|
20898
|
-
lstatSync,
|
|
20899
|
-
readFileSync: readFileSync2,
|
|
20900
|
-
realpathSync: realpathSync2
|
|
20901
|
-
};
|
|
20902
|
-
|
|
20903
21172
|
// packages/omo-config-core/src/loader/paths.ts
|
|
20904
21173
|
var MAX_PROJECT_CONFIG_DIRECTORY_DEPTH = 256;
|
|
20905
21174
|
var ACCOUNT_HOME_DIR = userInfo().homedir;
|
|
@@ -20995,7 +21264,7 @@ function resolveOmoProfileName(options = {}) {
|
|
|
20995
21264
|
const env = options.env ?? process.env;
|
|
20996
21265
|
return profileName(options.profile) ?? profileName(env["OMO_PROFILE"]) ?? profileName(env["OCX_PROFILE"]) ?? profileNameFromOpenCodeConfigDir(env["OPENCODE_CONFIG_DIR"]);
|
|
20997
21266
|
}
|
|
20998
|
-
function
|
|
21267
|
+
function toRecord2(value) {
|
|
20999
21268
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21000
21269
|
return;
|
|
21001
21270
|
return Object.fromEntries(Object.entries(value));
|
|
@@ -21016,13 +21285,13 @@ function harnessLayer(config, harness) {
|
|
|
21016
21285
|
const legacyKeys = Object.entries(OMO_CONFIG_LEGACY_HARNESS_ALIASES).filter(([, target]) => target === canonical).map(([legacy]) => harnessBlockKey(legacy));
|
|
21017
21286
|
let layer = {};
|
|
21018
21287
|
for (const key of [...legacyKeys, harnessBlockKey(canonical)]) {
|
|
21019
|
-
layer = mergeOmoConfigRecords(layer,
|
|
21288
|
+
layer = mergeOmoConfigRecords(layer, toRecord2(config[key]) ?? {});
|
|
21020
21289
|
}
|
|
21021
21290
|
return layer;
|
|
21022
21291
|
}
|
|
21023
21292
|
function resolveOmoConfigView(options) {
|
|
21024
|
-
const profiles =
|
|
21025
|
-
const profile = options.profile === undefined ? undefined :
|
|
21293
|
+
const profiles = toRecord2(options.config["profiles"]);
|
|
21294
|
+
const profile = options.profile === undefined ? undefined : toRecord2(profiles?.[options.profile]);
|
|
21026
21295
|
const diagnostics = profile === undefined && options.profile !== undefined ? [{
|
|
21027
21296
|
kind: "profile",
|
|
21028
21297
|
message: `Activated omo profile "${options.profile}" does not exist; using the base configuration`,
|
|
@@ -21077,70 +21346,9 @@ function stripResolutionControlKeys(config) {
|
|
|
21077
21346
|
} = config;
|
|
21078
21347
|
return resolved;
|
|
21079
21348
|
}
|
|
21080
|
-
function validationDiagnostic(path, issues) {
|
|
21081
|
-
const issuePaths = issues.map((issue) => issue.path.map((segment) => String(segment)).join("."));
|
|
21082
|
-
return {
|
|
21083
|
-
kind: "validation",
|
|
21084
|
-
message: `Invalid omo config at ${path}: ${issuePaths.join(", ")}`,
|
|
21085
|
-
path,
|
|
21086
|
-
issuePaths
|
|
21087
|
-
};
|
|
21088
|
-
}
|
|
21089
|
-
function unrecognizedKeyIssues(issues) {
|
|
21090
|
-
return issues.flatMap((issue) => issue.code === "unrecognized_keys" ? [{ keys: issue.keys, path: issue.path.map((segment) => String(segment)) }] : []);
|
|
21091
|
-
}
|
|
21092
|
-
function hasUnsafeUnrecognizedKey(issues) {
|
|
21093
|
-
return issues.some((issue) => issue.keys.some((key) => isUnsafeObjectKey(key)));
|
|
21094
|
-
}
|
|
21095
|
-
function hasTamperedPrototype(value) {
|
|
21096
|
-
if (Array.isArray(value))
|
|
21097
|
-
return value.some((entry) => hasTamperedPrototype(entry));
|
|
21098
|
-
if (!isRecord9(value))
|
|
21099
|
-
return false;
|
|
21100
|
-
const prototype = Object.getPrototypeOf(value);
|
|
21101
|
-
if (prototype !== Object.prototype && prototype !== null)
|
|
21102
|
-
return true;
|
|
21103
|
-
return Object.values(value).some((entry) => hasTamperedPrototype(entry));
|
|
21104
|
-
}
|
|
21105
|
-
function isRecord9(value) {
|
|
21106
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21107
|
-
}
|
|
21108
|
-
function containerAt(record, path) {
|
|
21109
|
-
let container = record;
|
|
21110
|
-
for (const segment of path) {
|
|
21111
|
-
const next = container[segment];
|
|
21112
|
-
if (!isRecord9(next))
|
|
21113
|
-
return null;
|
|
21114
|
-
container = next;
|
|
21115
|
-
}
|
|
21116
|
-
return container;
|
|
21117
|
-
}
|
|
21118
|
-
function stripUnrecognizedKeys(record, issues) {
|
|
21119
|
-
const stripped = structuredClone(record);
|
|
21120
|
-
const issuePaths = [];
|
|
21121
|
-
for (const issue of issues) {
|
|
21122
|
-
const container = containerAt(stripped, issue.path);
|
|
21123
|
-
if (container === null)
|
|
21124
|
-
continue;
|
|
21125
|
-
for (const key of issue.keys) {
|
|
21126
|
-
delete container[key];
|
|
21127
|
-
issuePaths.push([...issue.path, key].join("."));
|
|
21128
|
-
}
|
|
21129
|
-
}
|
|
21130
|
-
return { issuePaths, stripped };
|
|
21131
|
-
}
|
|
21132
|
-
function toRecord2(value) {
|
|
21133
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
21134
|
-
return null;
|
|
21135
|
-
const record = {};
|
|
21136
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
21137
|
-
record[key] = entry;
|
|
21138
|
-
}
|
|
21139
|
-
return record;
|
|
21140
|
-
}
|
|
21141
21349
|
function readConfigSource(path, scope, fileSystem) {
|
|
21142
21350
|
if (!fileSystem.existsSync(path)) {
|
|
21143
|
-
return { source: { exists: false, loaded: false, path, scope } };
|
|
21351
|
+
return { diagnostics: [], source: { exists: false, loaded: false, path, scope } };
|
|
21144
21352
|
}
|
|
21145
21353
|
let content;
|
|
21146
21354
|
try {
|
|
@@ -21148,68 +21356,23 @@ function readConfigSource(path, scope, fileSystem) {
|
|
|
21148
21356
|
} catch (error) {
|
|
21149
21357
|
const message = error instanceof Error ? error.message : String(error);
|
|
21150
21358
|
return {
|
|
21151
|
-
|
|
21359
|
+
diagnostics: [{ kind: "read", message: `Failed to read ${path}: ${message}`, path }],
|
|
21152
21360
|
source: { exists: true, loaded: false, path, scope }
|
|
21153
21361
|
};
|
|
21154
21362
|
}
|
|
21155
21363
|
const parsed = parseJsoncSafe(content);
|
|
21156
21364
|
if (parsed.errors.length > 0) {
|
|
21157
21365
|
return {
|
|
21158
|
-
|
|
21366
|
+
diagnostics: [{
|
|
21159
21367
|
kind: "parse",
|
|
21160
21368
|
message: `JSONC parse error in ${path}: ${parsed.errors.map((error) => error.message).join(", ")}`,
|
|
21161
21369
|
path
|
|
21162
|
-
},
|
|
21370
|
+
}],
|
|
21163
21371
|
source: { exists: true, loaded: false, path, scope }
|
|
21164
21372
|
};
|
|
21165
21373
|
}
|
|
21166
|
-
|
|
21167
|
-
|
|
21168
|
-
diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: "__proto__" member is not allowed`, path },
|
|
21169
|
-
source: { exists: true, loaded: false, path, scope }
|
|
21170
|
-
};
|
|
21171
|
-
}
|
|
21172
|
-
const parsedRecord = toRecord2(parsed.data);
|
|
21173
|
-
const validation = OmoConfigLayerSchema.safeParse(parsed.data);
|
|
21174
|
-
if (!validation.success) {
|
|
21175
|
-
const unrecognized = unrecognizedKeyIssues(validation.error.issues);
|
|
21176
|
-
if (hasUnsafeUnrecognizedKey(unrecognized)) {
|
|
21177
|
-
return {
|
|
21178
|
-
diagnostic: validationDiagnostic(path, validation.error.issues),
|
|
21179
|
-
source: { exists: true, loaded: false, path, scope }
|
|
21180
|
-
};
|
|
21181
|
-
}
|
|
21182
|
-
const rejected = {
|
|
21183
|
-
diagnostic: validationDiagnostic(path, validation.error.issues),
|
|
21184
|
-
source: { exists: true, loaded: false, path, scope }
|
|
21185
|
-
};
|
|
21186
|
-
const unknownIssues = unrecognizedKeyIssues(validation.error.issues);
|
|
21187
|
-
if (parsedRecord === null || unknownIssues.length === 0)
|
|
21188
|
-
return rejected;
|
|
21189
|
-
const { issuePaths, stripped } = stripUnrecognizedKeys(parsedRecord, unknownIssues);
|
|
21190
|
-
if (!OmoConfigLayerSchema.safeParse(stripped).success)
|
|
21191
|
-
return rejected;
|
|
21192
|
-
return {
|
|
21193
|
-
diagnostic: {
|
|
21194
|
-
kind: "unknown-keys",
|
|
21195
|
-
message: `Ignored unknown keys in ${path}: ${issuePaths.join(", ")}`,
|
|
21196
|
-
path,
|
|
21197
|
-
issuePaths
|
|
21198
|
-
},
|
|
21199
|
-
source: { exists: true, loaded: true, path, scope },
|
|
21200
|
-
value: stripped
|
|
21201
|
-
};
|
|
21202
|
-
}
|
|
21203
|
-
if (parsedRecord === null) {
|
|
21204
|
-
return {
|
|
21205
|
-
diagnostic: { kind: "validation", message: `Invalid omo config at ${path}: root must be an object`, path },
|
|
21206
|
-
source: { exists: true, loaded: false, path, scope }
|
|
21207
|
-
};
|
|
21208
|
-
}
|
|
21209
|
-
return {
|
|
21210
|
-
source: { exists: true, loaded: true, path, scope },
|
|
21211
|
-
value: parsedRecord
|
|
21212
|
-
};
|
|
21374
|
+
const layer = validateConfigLayer(path, parsed.data);
|
|
21375
|
+
return layer.loaded ? { diagnostics: layer.diagnostics, source: { exists: true, loaded: true, path, scope }, value: layer.value } : { diagnostics: layer.diagnostics, source: { exists: true, loaded: false, path, scope } };
|
|
21213
21376
|
}
|
|
21214
21377
|
function legacyCategoryDiagnostic(path, renames) {
|
|
21215
21378
|
const detail = renames.map((rename) => rename.dropped ? `${rename.path} ignored because ${rename.canonical} is also configured` : `${rename.path} renamed to ${rename.canonical}`).join(", ");
|
|
@@ -21244,8 +21407,7 @@ function loadOmoConfig(options = {}) {
|
|
|
21244
21407
|
})) {
|
|
21245
21408
|
const loaded = readConfigSource(candidate.path, candidate.scope, fileSystem);
|
|
21246
21409
|
sources.push(loaded.source);
|
|
21247
|
-
|
|
21248
|
-
diagnostics.push(loaded.diagnostic);
|
|
21410
|
+
diagnostics.push(...loaded.diagnostics);
|
|
21249
21411
|
if (loaded.value !== undefined) {
|
|
21250
21412
|
const canonicalized = canonicalizeLegacyCategoryNames(loaded.value);
|
|
21251
21413
|
if (canonicalized.renames.length > 0) {
|
|
@@ -21268,7 +21430,8 @@ function loadOmoConfig(options = {}) {
|
|
|
21268
21430
|
...options.harness === undefined ? {} : { harness: options.harness },
|
|
21269
21431
|
...requestedProfile === undefined ? {} : { profile: requestedProfile }
|
|
21270
21432
|
});
|
|
21271
|
-
const
|
|
21433
|
+
const finalInput = mergeOmoConfigRecords(DEFAULT_RAW_CONFIG, resolved.config);
|
|
21434
|
+
const finalConfig = OmoConfigSchema.safeParse(finalInput);
|
|
21272
21435
|
if (finalConfig.success) {
|
|
21273
21436
|
return {
|
|
21274
21437
|
config: stripResolutionControlKeys(finalConfig.data),
|
|
@@ -21278,9 +21441,22 @@ function loadOmoConfig(options = {}) {
|
|
|
21278
21441
|
sources
|
|
21279
21442
|
};
|
|
21280
21443
|
}
|
|
21444
|
+
const pruned = pruneInvalidConfigPaths(finalInput, finalConfig.error.issues, (record) => {
|
|
21445
|
+
const parsed = OmoConfigSchema.safeParse(record);
|
|
21446
|
+
return parsed.success ? { success: true } : { success: false, issues: parsed.error.issues };
|
|
21447
|
+
});
|
|
21448
|
+
if (pruned.ok) {
|
|
21449
|
+
return {
|
|
21450
|
+
config: stripResolutionControlKeys(OmoConfigSchema.parse(pruned.config)),
|
|
21451
|
+
diagnostics: [...diagnostics, ...resolved.diagnostics, ...invalidValueDiagnostics(MERGED_OMO_CONFIG_PATH, pruned.dropped)],
|
|
21452
|
+
layers,
|
|
21453
|
+
...resolved.profile === undefined ? {} : { profile: resolved.profile },
|
|
21454
|
+
sources
|
|
21455
|
+
};
|
|
21456
|
+
}
|
|
21281
21457
|
return {
|
|
21282
21458
|
config: stripResolutionControlKeys(OmoConfigSchema.parse(DEFAULT_RAW_CONFIG)),
|
|
21283
|
-
diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic(
|
|
21459
|
+
diagnostics: [...diagnostics, ...resolved.diagnostics, validationDiagnostic(MERGED_OMO_CONFIG_PATH, finalConfig.error.issues)],
|
|
21284
21460
|
layers,
|
|
21285
21461
|
...resolved.profile === undefined ? {} : { profile: resolved.profile },
|
|
21286
21462
|
sources
|
|
@@ -286,17 +286,17 @@ Until every success criterion PASSES with its evidence captured:
|
|
|
286
286
|
vars. Append a one-line cleanup receipt to the notepad next to the
|
|
287
287
|
artifact, e.g. `cleanup: killed 12345; tmux kill-session ulw-qa-foo;
|
|
288
288
|
rm -rf /tmp/ulw.aB12cD`. No receipt → criterion stays in_progress.
|
|
289
|
-
6. Verify: LSP diagnostics clean on changed files
|
|
290
|
-
|
|
291
|
-
turn). Re-run a validation command (suite, typecheck, build) only
|
|
292
|
-
when its inputs changed since its last green run; ONE full-suite
|
|
293
|
-
pass belongs immediately before the final message, not after
|
|
294
|
-
every increment.
|
|
289
|
+
6. Verify: LSP diagnostics clean on changed files; no test skipped or
|
|
290
|
+
xfail-ed this turn.
|
|
295
291
|
7. Mark completed. Append non-obvious findings / learnings.
|
|
296
|
-
8.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
292
|
+
8. Evidence stays valid per target until an input changes; record with
|
|
293
|
+
each artifact the commit and what it exercised. After each increment
|
|
294
|
+
rerun what moved — the tests of every touched file and of the files
|
|
295
|
+
that import it, the scenarios that exercise them, anything whose
|
|
296
|
+
dependencies or environment changed — and cite the capture for the
|
|
297
|
+
rest. The full set (scenarios, suite, typecheck, build) runs once
|
|
298
|
+
more right before the final message. Record PASS/FAIL beside each
|
|
299
|
+
artifact. Loop until all PASS.
|
|
300
300
|
|
|
301
301
|
Within a step, follow Finding things; READ before CHANGE, never in
|
|
302
302
|
parallel with it.
|
|
@@ -399,13 +399,12 @@ When triggered, follow this procedure (NON-NEGOTIABLE):
|
|
|
399
399
|
it names a success criterion the evidence fails; record concerns
|
|
400
400
|
that cite no criterion as notes with a one-line reason — fixed or
|
|
401
401
|
declined at your judgment.
|
|
402
|
-
3. Fix every criterion-cited blocker
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
notes counts as approval.
|
|
402
|
+
3. Fix every criterion-cited blocker; rerun per Execution loop step 8
|
|
403
|
+
and update the notepad.
|
|
404
|
+
4. Spawn a NEW reviewer for each re-review, at most twice, passing only
|
|
405
|
+
the delta diff, the blockers the last one cited, and the
|
|
406
|
+
already-approved criteria marked out-of-scope. An approval whose only
|
|
407
|
+
remaining items are notes counts as approval.
|
|
409
408
|
5. On approval, declare done. If criterion-cited blockers remain after
|
|
410
409
|
two re-reviews, stop and surface them to the user (mirroring the
|
|
411
410
|
2-attempt stop rule below) — do not loop further.
|
|
@@ -430,7 +429,15 @@ commits this session — then stage + draft the message instead.
|
|
|
430
429
|
for the regression it names is NOT evidence: mock-call assertions,
|
|
431
430
|
pinned constants, a fixture equal to the default it must override,
|
|
432
431
|
an expected value re-derived from the output under test.
|
|
433
|
-
-
|
|
432
|
+
- Make the smallest correct change per unit, and fix in THIS run every
|
|
433
|
+
defect inside the change's blast radius — the request not delivered,
|
|
434
|
+
a regression this change introduces, an invalid proof, a failing test
|
|
435
|
+
or stale doc of code you touched — as registered work (todo plus
|
|
436
|
+
success criterion) to the ideal state. A defect outside it gets a
|
|
437
|
+
tracked issue with reproduction and evidence and a line in the final
|
|
438
|
+
message; a deferral never turns a criterion into PASS. Keep delegated
|
|
439
|
+
unit scope hard: the worker reports, the orchestrator registers or
|
|
440
|
+
files.
|
|
434
441
|
- Never suppress lints / errors / test failures. Never delete, skip,
|
|
435
442
|
`.only`, `.skip`, `xfail`, or comment out tests to green the suite.
|
|
436
443
|
- Never claim done from inference — only from captured evidence.
|
|
@@ -53,7 +53,7 @@ Do ALL of this immediately after the plan is selected, BEFORE the first implemen
|
|
|
53
53
|
|
|
54
54
|
1. **Set the goal, in detail.** When a goal tool is available (`create_goal`), call it with a DETAILED objective: the plan name and path, the concrete end state, the phase and task counts, the delivery mode (direct, `--make-pr`, or `--ship`), and how completion will be verified. One work session = one registered goal (the goal tool holds one active goal); each phase then carries its own concrete goal — the ledger entry Phase 2 records before the wave's first dispatch, defined from the previous phase's landed and verified evidence. No goal tool -> record the same objective as the first ledger entry.
|
|
55
55
|
2. **Register every phase and task as todos.** Mirror the plan into the todo/plan tool of your harness: one phase per plan wave, one todo per column-zero checkbox (including the final verification wave). Register ALL of them up front - never keep tasks in memory only.
|
|
56
|
-
3. **Keep them current at every moment.** Mark a todo in_progress when its work dispatches and done immediately after its verification passes. Never batch-complete at the end, never execute work that is not a registered todo; discovered work — a
|
|
56
|
+
3. **Keep them current at every moment.** Mark a todo in_progress when its work dispatches and done immediately after its verification passes. Never batch-complete at the end, never execute work that is not a registered todo; discovered work inside the change's blast radius — the request not delivered, a regression this change introduces, an invalid proof, a failing test or stale doc of touched code — is appended to the plan as a checkbox, mirrored as a todo before it runs, and fixed to the ideal state; a defect outside it gets a tracked issue with reproduction and evidence and a line in the final report. A worker that meets a defect outside its assigned files reports it instead of fixing it; only the orchestrator appends the checkbox and dispatches it to a correctly scoped unit. The todo list, Boulder state, and plan checkboxes must always tell the same story.
|
|
57
57
|
|
|
58
58
|
## Phase 1: Select the plan
|
|
59
59
|
|