oh-my-customcode 0.87.3 → 0.88.1
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 +101 -66
- package/dist/index.js +36 -3
- package/package.json +1 -1
- package/templates/.claude/rules/MUST-completion-verification.md +34 -1
- package/templates/.claude/rules/MUST-intent-transparency.md +28 -0
- package/templates/.claude/rules/SHOULD-memory-integration.md +24 -0
- package/templates/manifest.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2148,18 +2148,41 @@ __export(exports_registry, {
|
|
|
2148
2148
|
registerProject: () => registerProject,
|
|
2149
2149
|
readRegistry: () => readRegistry,
|
|
2150
2150
|
migrateFromLockfiles: () => migrateFromLockfiles,
|
|
2151
|
+
isTempPath: () => isTempPath,
|
|
2151
2152
|
cleanRegistry: () => cleanRegistry,
|
|
2152
2153
|
_setRegistryDirForTesting: () => _setRegistryDirForTesting
|
|
2153
2154
|
});
|
|
2154
2155
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2155
|
-
import { homedir } from "node:os";
|
|
2156
|
-
import { basename, join, resolve } from "node:path";
|
|
2156
|
+
import { homedir, tmpdir } from "node:os";
|
|
2157
|
+
import { basename, join, resolve, sep } from "node:path";
|
|
2157
2158
|
function _setRegistryDirForTesting(dir) {
|
|
2158
2159
|
_registryDirOverride = dir;
|
|
2159
2160
|
}
|
|
2161
|
+
function isTempPath(projectPath) {
|
|
2162
|
+
const normalized = resolve(projectPath);
|
|
2163
|
+
const candidates = new Set;
|
|
2164
|
+
candidates.add(resolve(tmpdir()));
|
|
2165
|
+
for (const envKey of ["TMPDIR", "TMP", "TEMP"]) {
|
|
2166
|
+
const value = process.env[envKey];
|
|
2167
|
+
if (value)
|
|
2168
|
+
candidates.add(resolve(value));
|
|
2169
|
+
}
|
|
2170
|
+
candidates.add("/tmp");
|
|
2171
|
+
candidates.add("/var/tmp");
|
|
2172
|
+
candidates.add("/var/folders");
|
|
2173
|
+
for (const candidate of candidates) {
|
|
2174
|
+
if (normalized === candidate || normalized.startsWith(candidate + sep)) {
|
|
2175
|
+
return true;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
return false;
|
|
2179
|
+
}
|
|
2160
2180
|
function registryDir() {
|
|
2161
2181
|
if (_registryDirOverride !== undefined)
|
|
2162
2182
|
return _registryDirOverride;
|
|
2183
|
+
const envOverride = process.env.OMCUSTOM_REGISTRY_DIR;
|
|
2184
|
+
if (envOverride)
|
|
2185
|
+
return envOverride;
|
|
2163
2186
|
const home = process.env.HOME ?? homedir();
|
|
2164
2187
|
return join(home, ".oh-my-customcode");
|
|
2165
2188
|
}
|
|
@@ -2188,6 +2211,10 @@ async function readRegistry() {
|
|
|
2188
2211
|
}
|
|
2189
2212
|
async function registerProject(projectPath, version) {
|
|
2190
2213
|
const normalizedPath = resolve(projectPath);
|
|
2214
|
+
if (!process.env.OMCUSTOM_REGISTRY_DIR && _registryDirOverride === undefined) {
|
|
2215
|
+
if (isTempPath(normalizedPath))
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2191
2218
|
const registry = await readRegistryRaw();
|
|
2192
2219
|
const existing = registry.projects[normalizedPath];
|
|
2193
2220
|
const now = new Date().toISOString();
|
|
@@ -2211,7 +2238,13 @@ async function cleanRegistry() {
|
|
|
2211
2238
|
const registry = await readRegistryRaw();
|
|
2212
2239
|
const paths = Object.keys(registry.projects);
|
|
2213
2240
|
let removed = 0;
|
|
2241
|
+
const purgeTempPaths = !process.env.OMCUSTOM_REGISTRY_DIR && _registryDirOverride === undefined;
|
|
2214
2242
|
for (const projectPath of paths) {
|
|
2243
|
+
if (purgeTempPaths && isTempPath(projectPath)) {
|
|
2244
|
+
delete registry.projects[projectPath];
|
|
2245
|
+
removed++;
|
|
2246
|
+
continue;
|
|
2247
|
+
}
|
|
2215
2248
|
try {
|
|
2216
2249
|
await fsAccess(projectPath);
|
|
2217
2250
|
} catch {
|
|
@@ -2301,7 +2334,7 @@ var init_package = __esm(() => {
|
|
|
2301
2334
|
workspaces: [
|
|
2302
2335
|
"packages/*"
|
|
2303
2336
|
],
|
|
2304
|
-
version: "0.
|
|
2337
|
+
version: "0.88.1",
|
|
2305
2338
|
description: "Batteries-included agent harness for Claude Code",
|
|
2306
2339
|
type: "module",
|
|
2307
2340
|
bin: {
|
|
@@ -6052,10 +6085,10 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
6052
6085
|
let offset = bm.offset;
|
|
6053
6086
|
let commentEnd = null;
|
|
6054
6087
|
for (const collItem of bm.items) {
|
|
6055
|
-
const { start, key, sep, value } = collItem;
|
|
6088
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
6056
6089
|
const keyProps = resolveProps.resolveProps(start, {
|
|
6057
6090
|
indicator: "explicit-key-ind",
|
|
6058
|
-
next: key ??
|
|
6091
|
+
next: key ?? sep2?.[0],
|
|
6059
6092
|
offset,
|
|
6060
6093
|
onError,
|
|
6061
6094
|
parentIndent: bm.indent,
|
|
@@ -6069,7 +6102,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
6069
6102
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
6070
6103
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
6071
6104
|
}
|
|
6072
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
6105
|
+
if (!keyProps.anchor && !keyProps.tag && !sep2) {
|
|
6073
6106
|
commentEnd = keyProps.end;
|
|
6074
6107
|
if (keyProps.comment) {
|
|
6075
6108
|
if (map.comment)
|
|
@@ -6094,7 +6127,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
6094
6127
|
ctx.atKey = false;
|
|
6095
6128
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
6096
6129
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
6097
|
-
const valueProps = resolveProps.resolveProps(
|
|
6130
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
6098
6131
|
indicator: "map-value-ind",
|
|
6099
6132
|
next: value,
|
|
6100
6133
|
offset: keyNode.range[2],
|
|
@@ -6110,7 +6143,7 @@ var require_resolve_block_map = __commonJS((exports) => {
|
|
|
6110
6143
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
6111
6144
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
6112
6145
|
}
|
|
6113
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
6146
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep2, null, valueProps, onError);
|
|
6114
6147
|
if (ctx.schema.compat)
|
|
6115
6148
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
6116
6149
|
offset = valueNode.range[2];
|
|
@@ -6196,7 +6229,7 @@ var require_resolve_end = __commonJS((exports) => {
|
|
|
6196
6229
|
let comment = "";
|
|
6197
6230
|
if (end) {
|
|
6198
6231
|
let hasSpace = false;
|
|
6199
|
-
let
|
|
6232
|
+
let sep2 = "";
|
|
6200
6233
|
for (const token of end) {
|
|
6201
6234
|
const { source, type } = token;
|
|
6202
6235
|
switch (type) {
|
|
@@ -6210,13 +6243,13 @@ var require_resolve_end = __commonJS((exports) => {
|
|
|
6210
6243
|
if (!comment)
|
|
6211
6244
|
comment = cb;
|
|
6212
6245
|
else
|
|
6213
|
-
comment +=
|
|
6214
|
-
|
|
6246
|
+
comment += sep2 + cb;
|
|
6247
|
+
sep2 = "";
|
|
6215
6248
|
break;
|
|
6216
6249
|
}
|
|
6217
6250
|
case "newline":
|
|
6218
6251
|
if (comment)
|
|
6219
|
-
|
|
6252
|
+
sep2 += source;
|
|
6220
6253
|
hasSpace = true;
|
|
6221
6254
|
break;
|
|
6222
6255
|
default:
|
|
@@ -6256,18 +6289,18 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
6256
6289
|
let offset = fc.offset + fc.start.source.length;
|
|
6257
6290
|
for (let i = 0;i < fc.items.length; ++i) {
|
|
6258
6291
|
const collItem = fc.items[i];
|
|
6259
|
-
const { start, key, sep, value } = collItem;
|
|
6292
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
6260
6293
|
const props = resolveProps.resolveProps(start, {
|
|
6261
6294
|
flow: fcName,
|
|
6262
6295
|
indicator: "explicit-key-ind",
|
|
6263
|
-
next: key ??
|
|
6296
|
+
next: key ?? sep2?.[0],
|
|
6264
6297
|
offset,
|
|
6265
6298
|
onError,
|
|
6266
6299
|
parentIndent: fc.indent,
|
|
6267
6300
|
startOnNewline: false
|
|
6268
6301
|
});
|
|
6269
6302
|
if (!props.found) {
|
|
6270
|
-
if (!props.anchor && !props.tag && !
|
|
6303
|
+
if (!props.anchor && !props.tag && !sep2 && !value) {
|
|
6271
6304
|
if (i === 0 && props.comma)
|
|
6272
6305
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
6273
6306
|
else if (i < fc.items.length - 1)
|
|
@@ -6319,8 +6352,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
6319
6352
|
}
|
|
6320
6353
|
}
|
|
6321
6354
|
}
|
|
6322
|
-
if (!isMap && !
|
|
6323
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
6355
|
+
if (!isMap && !sep2 && !props.found) {
|
|
6356
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
|
|
6324
6357
|
coll.items.push(valueNode);
|
|
6325
6358
|
offset = valueNode.range[2];
|
|
6326
6359
|
if (isBlock(value))
|
|
@@ -6332,7 +6365,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
6332
6365
|
if (isBlock(key))
|
|
6333
6366
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
6334
6367
|
ctx.atKey = false;
|
|
6335
|
-
const valueProps = resolveProps.resolveProps(
|
|
6368
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
6336
6369
|
flow: fcName,
|
|
6337
6370
|
indicator: "map-value-ind",
|
|
6338
6371
|
next: value,
|
|
@@ -6343,8 +6376,8 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
6343
6376
|
});
|
|
6344
6377
|
if (valueProps.found) {
|
|
6345
6378
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
6346
|
-
if (
|
|
6347
|
-
for (const st of
|
|
6379
|
+
if (sep2)
|
|
6380
|
+
for (const st of sep2) {
|
|
6348
6381
|
if (st === valueProps.found)
|
|
6349
6382
|
break;
|
|
6350
6383
|
if (st.type === "newline") {
|
|
@@ -6361,7 +6394,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
|
|
|
6361
6394
|
else
|
|
6362
6395
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
6363
6396
|
}
|
|
6364
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
6397
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep2, null, valueProps, onError) : null;
|
|
6365
6398
|
if (valueNode) {
|
|
6366
6399
|
if (isBlock(value))
|
|
6367
6400
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -6538,7 +6571,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
|
|
|
6538
6571
|
chompStart = i + 1;
|
|
6539
6572
|
}
|
|
6540
6573
|
let value = "";
|
|
6541
|
-
let
|
|
6574
|
+
let sep2 = "";
|
|
6542
6575
|
let prevMoreIndented = false;
|
|
6543
6576
|
for (let i = 0;i < contentStart; ++i)
|
|
6544
6577
|
value += lines[i][0].slice(trimIndent) + `
|
|
@@ -6556,33 +6589,33 @@ var require_resolve_block_scalar = __commonJS((exports) => {
|
|
|
6556
6589
|
indent = "";
|
|
6557
6590
|
}
|
|
6558
6591
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
6559
|
-
value +=
|
|
6560
|
-
|
|
6592
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
6593
|
+
sep2 = `
|
|
6561
6594
|
`;
|
|
6562
6595
|
} else if (indent.length > trimIndent || content[0] === "\t") {
|
|
6563
|
-
if (
|
|
6564
|
-
|
|
6596
|
+
if (sep2 === " ")
|
|
6597
|
+
sep2 = `
|
|
6565
6598
|
`;
|
|
6566
|
-
else if (!prevMoreIndented &&
|
|
6599
|
+
else if (!prevMoreIndented && sep2 === `
|
|
6567
6600
|
`)
|
|
6568
|
-
|
|
6601
|
+
sep2 = `
|
|
6569
6602
|
|
|
6570
6603
|
`;
|
|
6571
|
-
value +=
|
|
6572
|
-
|
|
6604
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
6605
|
+
sep2 = `
|
|
6573
6606
|
`;
|
|
6574
6607
|
prevMoreIndented = true;
|
|
6575
6608
|
} else if (content === "") {
|
|
6576
|
-
if (
|
|
6609
|
+
if (sep2 === `
|
|
6577
6610
|
`)
|
|
6578
6611
|
value += `
|
|
6579
6612
|
`;
|
|
6580
6613
|
else
|
|
6581
|
-
|
|
6614
|
+
sep2 = `
|
|
6582
6615
|
`;
|
|
6583
6616
|
} else {
|
|
6584
|
-
value +=
|
|
6585
|
-
|
|
6617
|
+
value += sep2 + content;
|
|
6618
|
+
sep2 = " ";
|
|
6586
6619
|
prevMoreIndented = false;
|
|
6587
6620
|
}
|
|
6588
6621
|
}
|
|
@@ -6763,27 +6796,27 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
|
|
|
6763
6796
|
if (!match)
|
|
6764
6797
|
return source;
|
|
6765
6798
|
let res = match[1];
|
|
6766
|
-
let
|
|
6799
|
+
let sep2 = " ";
|
|
6767
6800
|
let pos = first.lastIndex;
|
|
6768
6801
|
line.lastIndex = pos;
|
|
6769
6802
|
while (match = line.exec(source)) {
|
|
6770
6803
|
if (match[1] === "") {
|
|
6771
|
-
if (
|
|
6804
|
+
if (sep2 === `
|
|
6772
6805
|
`)
|
|
6773
|
-
res +=
|
|
6806
|
+
res += sep2;
|
|
6774
6807
|
else
|
|
6775
|
-
|
|
6808
|
+
sep2 = `
|
|
6776
6809
|
`;
|
|
6777
6810
|
} else {
|
|
6778
|
-
res +=
|
|
6779
|
-
|
|
6811
|
+
res += sep2 + match[1];
|
|
6812
|
+
sep2 = " ";
|
|
6780
6813
|
}
|
|
6781
6814
|
pos = line.lastIndex;
|
|
6782
6815
|
}
|
|
6783
6816
|
const last = /[ \t]*(.*)/sy;
|
|
6784
6817
|
last.lastIndex = pos;
|
|
6785
6818
|
match = last.exec(source);
|
|
6786
|
-
return res +
|
|
6819
|
+
return res + sep2 + (match?.[1] ?? "");
|
|
6787
6820
|
}
|
|
6788
6821
|
function doubleQuotedValue(source, onError) {
|
|
6789
6822
|
let res = "";
|
|
@@ -7556,14 +7589,14 @@ var require_cst_stringify = __commonJS((exports) => {
|
|
|
7556
7589
|
}
|
|
7557
7590
|
}
|
|
7558
7591
|
}
|
|
7559
|
-
function stringifyItem({ start, key, sep, value }) {
|
|
7592
|
+
function stringifyItem({ start, key, sep: sep2, value }) {
|
|
7560
7593
|
let res = "";
|
|
7561
7594
|
for (const st of start)
|
|
7562
7595
|
res += st.source;
|
|
7563
7596
|
if (key)
|
|
7564
7597
|
res += stringifyToken(key);
|
|
7565
|
-
if (
|
|
7566
|
-
for (const st of
|
|
7598
|
+
if (sep2)
|
|
7599
|
+
for (const st of sep2)
|
|
7567
7600
|
res += st.source;
|
|
7568
7601
|
if (value)
|
|
7569
7602
|
res += stringifyToken(value);
|
|
@@ -8693,18 +8726,18 @@ var require_parser = __commonJS((exports) => {
|
|
|
8693
8726
|
if (this.type === "map-value-ind") {
|
|
8694
8727
|
const prev = getPrevProps(this.peek(2));
|
|
8695
8728
|
const start = getFirstKeyStartProps(prev);
|
|
8696
|
-
let
|
|
8729
|
+
let sep2;
|
|
8697
8730
|
if (scalar.end) {
|
|
8698
|
-
|
|
8699
|
-
|
|
8731
|
+
sep2 = scalar.end;
|
|
8732
|
+
sep2.push(this.sourceToken);
|
|
8700
8733
|
delete scalar.end;
|
|
8701
8734
|
} else
|
|
8702
|
-
|
|
8735
|
+
sep2 = [this.sourceToken];
|
|
8703
8736
|
const map = {
|
|
8704
8737
|
type: "block-map",
|
|
8705
8738
|
offset: scalar.offset,
|
|
8706
8739
|
indent: scalar.indent,
|
|
8707
|
-
items: [{ start, key: scalar, sep }]
|
|
8740
|
+
items: [{ start, key: scalar, sep: sep2 }]
|
|
8708
8741
|
};
|
|
8709
8742
|
this.onKeyLine = true;
|
|
8710
8743
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -8858,15 +8891,15 @@ var require_parser = __commonJS((exports) => {
|
|
|
8858
8891
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
8859
8892
|
const start2 = getFirstKeyStartProps(it.start);
|
|
8860
8893
|
const key = it.key;
|
|
8861
|
-
const
|
|
8862
|
-
|
|
8894
|
+
const sep2 = it.sep;
|
|
8895
|
+
sep2.push(this.sourceToken);
|
|
8863
8896
|
delete it.key;
|
|
8864
8897
|
delete it.sep;
|
|
8865
8898
|
this.stack.push({
|
|
8866
8899
|
type: "block-map",
|
|
8867
8900
|
offset: this.offset,
|
|
8868
8901
|
indent: this.indent,
|
|
8869
|
-
items: [{ start: start2, key, sep }]
|
|
8902
|
+
items: [{ start: start2, key, sep: sep2 }]
|
|
8870
8903
|
});
|
|
8871
8904
|
} else if (start.length > 0) {
|
|
8872
8905
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -9060,13 +9093,13 @@ var require_parser = __commonJS((exports) => {
|
|
|
9060
9093
|
const prev = getPrevProps(parent);
|
|
9061
9094
|
const start = getFirstKeyStartProps(prev);
|
|
9062
9095
|
fixFlowSeqItems(fc);
|
|
9063
|
-
const
|
|
9064
|
-
|
|
9096
|
+
const sep2 = fc.end.splice(1, fc.end.length);
|
|
9097
|
+
sep2.push(this.sourceToken);
|
|
9065
9098
|
const map = {
|
|
9066
9099
|
type: "block-map",
|
|
9067
9100
|
offset: fc.offset,
|
|
9068
9101
|
indent: fc.indent,
|
|
9069
|
-
items: [{ start, key: fc, sep }]
|
|
9102
|
+
items: [{ start, key: fc, sep: sep2 }]
|
|
9070
9103
|
};
|
|
9071
9104
|
this.onKeyLine = true;
|
|
9072
9105
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -9315,7 +9348,7 @@ __export(exports_fs, {
|
|
|
9315
9348
|
copyDirectory: () => copyDirectory,
|
|
9316
9349
|
calculateChecksum: () => calculateChecksum
|
|
9317
9350
|
});
|
|
9318
|
-
import { dirname as dirname2, isAbsolute, join as join3, relative, resolve as resolve2, sep } from "node:path";
|
|
9351
|
+
import { dirname as dirname2, isAbsolute, join as join3, relative, resolve as resolve2, sep as sep2 } from "node:path";
|
|
9319
9352
|
import { fileURLToPath } from "node:url";
|
|
9320
9353
|
function validatePreserveFilePath(filePath, projectRoot) {
|
|
9321
9354
|
if (!filePath || filePath.trim() === "") {
|
|
@@ -9411,7 +9444,7 @@ function shouldSkipPath(destPath, destRoot, skipPaths) {
|
|
|
9411
9444
|
for (const skipPath of skipPaths) {
|
|
9412
9445
|
if (skipPath.endsWith("/")) {
|
|
9413
9446
|
const dirPath = skipPath.slice(0, -1);
|
|
9414
|
-
if (relativePath === dirPath || relativePath.startsWith(dirPath +
|
|
9447
|
+
if (relativePath === dirPath || relativePath.startsWith(dirPath + sep2)) {
|
|
9415
9448
|
return true;
|
|
9416
9449
|
}
|
|
9417
9450
|
} else {
|
|
@@ -9574,7 +9607,7 @@ __export(exports_projects, {
|
|
|
9574
9607
|
default: () => projects_default
|
|
9575
9608
|
});
|
|
9576
9609
|
import { homedir as homedir3 } from "node:os";
|
|
9577
|
-
import { basename as basename4, join as join10, sep as
|
|
9610
|
+
import { basename as basename4, join as join10, sep as sep3 } from "node:path";
|
|
9578
9611
|
async function readLockFile(projectDir) {
|
|
9579
9612
|
const lockFilePath = join10(projectDir, ".omcustom.lock.json");
|
|
9580
9613
|
try {
|
|
@@ -9611,10 +9644,10 @@ async function getTemplateVersion() {
|
|
|
9611
9644
|
function matchesSearchPaths(projectPath, searchPaths) {
|
|
9612
9645
|
if (!searchPaths || searchPaths.length === 0)
|
|
9613
9646
|
return true;
|
|
9614
|
-
return searchPaths.some((searchPath) => projectPath === searchPath || projectPath.startsWith(searchPath +
|
|
9647
|
+
return searchPaths.some((searchPath) => projectPath === searchPath || projectPath.startsWith(searchPath + sep3));
|
|
9615
9648
|
}
|
|
9616
9649
|
function isUnderHome(projectPath, home) {
|
|
9617
|
-
return projectPath.startsWith(home +
|
|
9650
|
+
return projectPath.startsWith(home + sep3) || projectPath === home;
|
|
9618
9651
|
}
|
|
9619
9652
|
function sortProjects(projects) {
|
|
9620
9653
|
return projects.sort((a, b) => {
|
|
@@ -23791,10 +23824,10 @@ class Interpolator {
|
|
|
23791
23824
|
let value;
|
|
23792
23825
|
let clonedOptions;
|
|
23793
23826
|
const handleHasOptions = (key, inheritedOptions) => {
|
|
23794
|
-
const
|
|
23795
|
-
if (!key.includes(
|
|
23827
|
+
const sep2 = this.nestingOptionsSeparator;
|
|
23828
|
+
if (!key.includes(sep2))
|
|
23796
23829
|
return key;
|
|
23797
|
-
const c = key.split(new RegExp(`${regexEscape(
|
|
23830
|
+
const c = key.split(new RegExp(`${regexEscape(sep2)}[ ]*{`));
|
|
23798
23831
|
let optionsString = `{${c[1]}`;
|
|
23799
23832
|
key = c[0];
|
|
23800
23833
|
optionsString = this.interpolate(optionsString, clonedOptions);
|
|
@@ -23812,7 +23845,7 @@ class Interpolator {
|
|
|
23812
23845
|
};
|
|
23813
23846
|
} catch (e) {
|
|
23814
23847
|
this.logger.warn(`failed parsing options string in nesting for key ${key}`, e);
|
|
23815
|
-
return `${key}${
|
|
23848
|
+
return `${key}${sep2}${optionsString}`;
|
|
23816
23849
|
}
|
|
23817
23850
|
if (clonedOptions.defaultValue && clonedOptions.defaultValue.includes(this.prefix))
|
|
23818
23851
|
delete clonedOptions.defaultValue;
|
|
@@ -31465,8 +31498,10 @@ async function checkCliVersion(checkFn) {
|
|
|
31465
31498
|
}
|
|
31466
31499
|
function exitWithChildStatus(child) {
|
|
31467
31500
|
if (child.signal) {
|
|
31468
|
-
const signalNumber = osConstants.signals[child.signal]
|
|
31469
|
-
|
|
31501
|
+
const signalNumber = osConstants.signals[child.signal];
|
|
31502
|
+
if (signalNumber !== undefined) {
|
|
31503
|
+
process.exit(128 + signalNumber);
|
|
31504
|
+
}
|
|
31470
31505
|
}
|
|
31471
31506
|
process.exit(child.status ?? 1);
|
|
31472
31507
|
}
|
package/dist/index.js
CHANGED
|
@@ -299,18 +299,41 @@ __export(exports_registry, {
|
|
|
299
299
|
registerProject: () => registerProject,
|
|
300
300
|
readRegistry: () => readRegistry,
|
|
301
301
|
migrateFromLockfiles: () => migrateFromLockfiles,
|
|
302
|
+
isTempPath: () => isTempPath,
|
|
302
303
|
cleanRegistry: () => cleanRegistry,
|
|
303
304
|
_setRegistryDirForTesting: () => _setRegistryDirForTesting
|
|
304
305
|
});
|
|
305
306
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
306
|
-
import { homedir } from "node:os";
|
|
307
|
-
import { basename as basename3, join as join6, resolve as resolve2 } from "node:path";
|
|
307
|
+
import { homedir, tmpdir } from "node:os";
|
|
308
|
+
import { basename as basename3, join as join6, resolve as resolve2, sep as sep2 } from "node:path";
|
|
308
309
|
function _setRegistryDirForTesting(dir2) {
|
|
309
310
|
_registryDirOverride = dir2;
|
|
310
311
|
}
|
|
312
|
+
function isTempPath(projectPath) {
|
|
313
|
+
const normalized = resolve2(projectPath);
|
|
314
|
+
const candidates = new Set;
|
|
315
|
+
candidates.add(resolve2(tmpdir()));
|
|
316
|
+
for (const envKey of ["TMPDIR", "TMP", "TEMP"]) {
|
|
317
|
+
const value = process.env[envKey];
|
|
318
|
+
if (value)
|
|
319
|
+
candidates.add(resolve2(value));
|
|
320
|
+
}
|
|
321
|
+
candidates.add("/tmp");
|
|
322
|
+
candidates.add("/var/tmp");
|
|
323
|
+
candidates.add("/var/folders");
|
|
324
|
+
for (const candidate of candidates) {
|
|
325
|
+
if (normalized === candidate || normalized.startsWith(candidate + sep2)) {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
311
331
|
function registryDir() {
|
|
312
332
|
if (_registryDirOverride !== undefined)
|
|
313
333
|
return _registryDirOverride;
|
|
334
|
+
const envOverride = process.env.OMCUSTOM_REGISTRY_DIR;
|
|
335
|
+
if (envOverride)
|
|
336
|
+
return envOverride;
|
|
314
337
|
const home = process.env.HOME ?? homedir();
|
|
315
338
|
return join6(home, ".oh-my-customcode");
|
|
316
339
|
}
|
|
@@ -339,6 +362,10 @@ async function readRegistry() {
|
|
|
339
362
|
}
|
|
340
363
|
async function registerProject(projectPath, version) {
|
|
341
364
|
const normalizedPath = resolve2(projectPath);
|
|
365
|
+
if (!process.env.OMCUSTOM_REGISTRY_DIR && _registryDirOverride === undefined) {
|
|
366
|
+
if (isTempPath(normalizedPath))
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
342
369
|
const registry = await readRegistryRaw();
|
|
343
370
|
const existing = registry.projects[normalizedPath];
|
|
344
371
|
const now = new Date().toISOString();
|
|
@@ -362,7 +389,13 @@ async function cleanRegistry() {
|
|
|
362
389
|
const registry = await readRegistryRaw();
|
|
363
390
|
const paths = Object.keys(registry.projects);
|
|
364
391
|
let removed = 0;
|
|
392
|
+
const purgeTempPaths = !process.env.OMCUSTOM_REGISTRY_DIR && _registryDirOverride === undefined;
|
|
365
393
|
for (const projectPath of paths) {
|
|
394
|
+
if (purgeTempPaths && isTempPath(projectPath)) {
|
|
395
|
+
delete registry.projects[projectPath];
|
|
396
|
+
removed++;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
366
399
|
try {
|
|
367
400
|
await fsAccess(projectPath);
|
|
368
401
|
} catch {
|
|
@@ -1974,7 +2007,7 @@ var package_default = {
|
|
|
1974
2007
|
workspaces: [
|
|
1975
2008
|
"packages/*"
|
|
1976
2009
|
],
|
|
1977
|
-
version: "0.
|
|
2010
|
+
version: "0.88.1",
|
|
1978
2011
|
description: "Batteries-included agent harness for Claude Code",
|
|
1979
2012
|
type: "module",
|
|
1980
2013
|
bin: {
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ Before declaring any task `[Done]`, verify completion against task-type-specific
|
|
|
10
10
|
|
|
11
11
|
| Task Type | REQUIRED Verification Before [Done] |
|
|
12
12
|
|-----------|-------------------------------------|
|
|
13
|
-
| Release | All issues closed, version bumped, PR merged, GitHub Release created |
|
|
13
|
+
| Release | All issues closed, version bumped, PR merged, GitHub Release created; **External automation verified**: `.github/workflows/` listed AND `gh run list --limit 10` checked for auto-publish workflows |
|
|
14
14
|
| Implementation | Code compiles/passes lint, tests pass (if exist), no TODO markers left |
|
|
15
15
|
| Documentation | Links valid, counts accurate, cross-references updated |
|
|
16
16
|
| Git Operations | Operation succeeded (check exit code), working tree clean |
|
|
@@ -28,6 +28,19 @@ Before [Done]: (1) Verify ACTUAL outcome not just attempt — "ran command" ≠
|
|
|
28
28
|
4. Would I bet $100 this is truly complete? YES: Declare [Done] / NO: Identify uncertain and verify
|
|
29
29
|
-->
|
|
30
30
|
|
|
31
|
+
## Subagent Self-Report Verification
|
|
32
|
+
|
|
33
|
+
Subagents often report failures as "pre-existing", "baseline", or "unchanged". These claims MUST be verified against the base branch before acceptance.
|
|
34
|
+
|
|
35
|
+
| Subagent Claim | Required Verification |
|
|
36
|
+
|----------------|----------------------|
|
|
37
|
+
| "X test already failing on base" | `git stash && git checkout {base} && run test X && compare` |
|
|
38
|
+
| "This warning is pre-existing" | `git log -S "warning-text" {base}` or run on clean checkout |
|
|
39
|
+
| "File was unchanged" | `git diff {base}..HEAD -- {file}` |
|
|
40
|
+
| "Dependency issue not from this PR" | `git show {base}:package.json` compare |
|
|
41
|
+
|
|
42
|
+
Never accept "pre-existing" without direct base-branch evidence. A false "pre-existing" claim can mask a regression introduced by the current change.
|
|
43
|
+
|
|
31
44
|
## Common False Completion Patterns
|
|
32
45
|
|
|
33
46
|
| Pattern | Reality | Fix |
|
|
@@ -37,6 +50,8 @@ Before [Done]: (1) Verify ACTUAL outcome not just attempt — "ran command" ≠
|
|
|
37
50
|
| "PR created" | CI not checked | Wait for CI, verify green |
|
|
38
51
|
| "Issue closed" | Related issues not updated | Check parent epic, cross-refs |
|
|
39
52
|
| "Tests pass" | Only ran subset | Run full test suite |
|
|
53
|
+
| "Waiting for manual publish" | External CI/CD auto-publishes on merge | Check `.github/workflows/` BEFORE assuming manual step |
|
|
54
|
+
| "Subagent said pre-existing" | Claim not verified against base branch | Run test on base branch, compare directly |
|
|
40
55
|
|
|
41
56
|
## Completion Contract Format
|
|
42
57
|
|
|
@@ -58,6 +73,24 @@ Then at completion:
|
|
|
58
73
|
└── ✓ Criterion N: {evidence}
|
|
59
74
|
```
|
|
60
75
|
|
|
76
|
+
## Autonomous Mode Entry Checklist
|
|
77
|
+
|
|
78
|
+
When entering autonomous mode (user grants extended execution without per-step confirmation), perform this inventory BEFORE first action:
|
|
79
|
+
|
|
80
|
+
1. **Workflow inventory**: `ls .github/workflows/` — identify auto-publish, auto-tag, release, docs-sync, CI workflows
|
|
81
|
+
2. **Recent runs**: `gh run list --limit 10` — check success/failure patterns of automated workflows
|
|
82
|
+
3. **External publish targets**: Check if npm/PyPI/Docker Hub/GitHub Releases are auto-triggered on merge
|
|
83
|
+
4. **Manual intervention points**: Identify which steps require human approval vs. fully automated
|
|
84
|
+
5. **Cross-reference with task**: Which workflows will the planned work trigger?
|
|
85
|
+
|
|
86
|
+
Record findings in session context. Failure to inventory automation is a R020 violation (unknown external state = unverifiable completion).
|
|
87
|
+
|
|
88
|
+
### Cross-reference
|
|
89
|
+
|
|
90
|
+
Related memory records:
|
|
91
|
+
- `feedback_github_workflows_inventory.md` — original incident (v0.87.2~v0.88.0 session)
|
|
92
|
+
- `feedback_subagent_pre_existing_claims.md` — subagent false-positive pattern
|
|
93
|
+
|
|
61
94
|
## Integration
|
|
62
95
|
|
|
63
96
|
| Rule | Interaction |
|
|
@@ -37,6 +37,34 @@ Display reasoning when routing to agents. Users must always know which agent was
|
|
|
37
37
|
|
|
38
38
|
Users can specify agent directly with `@{agent-name} {command}`. Override bypasses detection.
|
|
39
39
|
|
|
40
|
+
## User Directive Persistence
|
|
41
|
+
|
|
42
|
+
When a user explicitly names a tool, skill, or workflow (e.g., "use /pipeline auto-dev", "always run tests with bun test"), this preference persists for the entire session — including after autonomous mode transitions.
|
|
43
|
+
|
|
44
|
+
### Persistence Triggers
|
|
45
|
+
|
|
46
|
+
| User Statement Pattern | Persistence Scope |
|
|
47
|
+
|------------------------|-------------------|
|
|
48
|
+
| "use X for development" | Entire session |
|
|
49
|
+
| "always / every time" | Entire session |
|
|
50
|
+
| "from now on" | Entire session + memory save candidate |
|
|
51
|
+
| "for this task" | Current task only |
|
|
52
|
+
| Named slash command | Subsequent similar invocations |
|
|
53
|
+
|
|
54
|
+
### Cycle Start Self-Check
|
|
55
|
+
|
|
56
|
+
At the start of every work cycle (issue, task, release, or autonomous sub-loop):
|
|
57
|
+
1. Review recent user messages in the conversation
|
|
58
|
+
2. Identify any named tool/skill/workflow directives
|
|
59
|
+
3. Apply those directives unless explicitly rescinded
|
|
60
|
+
4. If unsure whether a directive applies, default to the stated preference
|
|
61
|
+
|
|
62
|
+
**Anti-pattern**: Treating autonomous mode as a clean slate that discards earlier user preferences. Autonomous mode means "continue without per-step confirmation" — NOT "reset user directives".
|
|
63
|
+
|
|
64
|
+
### Cross-reference
|
|
65
|
+
|
|
66
|
+
- Related memory: session v0.87.2~v0.88.0 (issue #869) — `/pipeline auto-dev` preference was lost after autonomous mode transition
|
|
67
|
+
|
|
40
68
|
## Agent Triggers
|
|
41
69
|
|
|
42
70
|
Defined in `.claude/skills/intent-detection/patterns/agent-triggers.yaml`. Each agent has keywords, file patterns, actions, and base confidence.
|
|
@@ -277,6 +277,30 @@ User Model data feeds into intent-detection (R015) and routing skill confidence
|
|
|
277
277
|
|
|
278
278
|
-->
|
|
279
279
|
|
|
280
|
+
## Mid-Session Immediate Save
|
|
281
|
+
|
|
282
|
+
Save memory IMMEDIATELY upon surprising discovery — do not defer to session end.
|
|
283
|
+
|
|
284
|
+
| Trigger | Action | Rationale |
|
|
285
|
+
|---------|--------|-----------|
|
|
286
|
+
| Repeated pattern observed (2nd time) | Save `feedback_*.md` now | Pattern will recur within session |
|
|
287
|
+
| Unexpected tool behavior / workaround | Save `feedback_*.md` now | Session state defense |
|
|
288
|
+
| Subagent false-positive detected | Save `feedback_*.md` now | Prevent repeat in same session |
|
|
289
|
+
| User correction / feedback | Save `feedback_*.md` now | Honor correction immediately |
|
|
290
|
+
|
|
291
|
+
### Why Immediate?
|
|
292
|
+
|
|
293
|
+
Session-end saves lose context: by the time the session ends, multiple discoveries have compounded and nuance is lost. Immediate saves preserve the exact trigger context that makes the memory actionable.
|
|
294
|
+
|
|
295
|
+
**Anti-pattern**: "I'll batch all learnings at session end" — by then you'll have forgotten WHY each one mattered, and further violations may have occurred using the un-saved pattern.
|
|
296
|
+
|
|
297
|
+
### Cross-reference
|
|
298
|
+
|
|
299
|
+
Related records from session v0.87.2~v0.88.0 (issue #869):
|
|
300
|
+
- `feedback_subagent_pre_existing_claims.md`
|
|
301
|
+
- `feedback_github_workflows_inventory.md`
|
|
302
|
+
- `feedback_bun_mock_module.md`
|
|
303
|
+
|
|
280
304
|
## Session-End Auto-Save
|
|
281
305
|
|
|
282
306
|
### Trigger
|
package/templates/manifest.json
CHANGED