@open-agent-toolkit/cli 0.3.17 → 0.3.18

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.
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "oatVersion": "0.3.17"
3
+ "oatVersion": "0.3.18"
4
4
  }
@@ -71,6 +71,28 @@ oat project log append \
71
71
  Structural entries require `--structural`, `--producer`, `--ref`, and `--body`.
72
72
  Do not combine structural flags with judgment flags.
73
73
 
74
+ ### Preview an append
75
+
76
+ Validate an entry against the current project state before appending it:
77
+
78
+ ```bash
79
+ oat project log append --dry-run \
80
+ --project .oat/projects/shared/example \
81
+ --type friction --scope project --area "gate review handoff" \
82
+ --body "The gate passed but the handoff was missing, so review receipt stopped."
83
+ ```
84
+
85
+ The preview reports whether it would append, create the log, recognize a replay,
86
+ or skip a disabled missing log. It writes no files, acquires no locks, and makes
87
+ no Git changes. `--dry-run --commit` also validates an existing recovery receipt
88
+ without committing or removing it. A would-create preview checks compatible
89
+ bundled assets and validates the template in memory; an existing log does not
90
+ need assets. Sealed logs and invalid entries retain normal append diagnostics.
91
+
92
+ This is advisory validation of current state. It reserves nothing and does not
93
+ guarantee later permissions, disk space, hook success, or the absence of other
94
+ writers. A normal append validates again under its required lock.
95
+
74
96
  ### Idempotent append and commit
75
97
 
76
98
  Two optional flags let an interrupted append be replayed safely:
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli": "0.3.17",
3
- "docs-config": "0.3.17",
4
- "docs-theme": "0.3.17",
5
- "docs-transforms": "0.3.17"
2
+ "cli": "0.3.18",
3
+ "docs-config": "0.3.18",
4
+ "docs-theme": "0.3.18",
5
+ "docs-transforms": "0.3.18"
6
6
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skill-script-references.d.ts","sourceRoot":"","sources":["../../../../../src/commands/init/tools/shared/skill-script-references.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAEV,cAAc,EACd,QAAQ,EACT,MAAM,8BAA8B,CAAC;AAEtC,iFAAiF;AACjF,eAAO,MAAM,yBAAyB,kBAAkB,CAAC;AAiKzD,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qEAAqE;AACrE,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,qBAAqB,CAAC;AAEvE,mEAAmE;AACnE,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CA4BvE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,MAAM,EAAE,CAUV;AAED,+CAA+C;AAC/C,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,QAAQ,EACd,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,MAAM,EAAE,CAQV;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,sBAAsB,CAIxB;AAED,sDAAsD;AACtD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,+BAA+B,CAOjC;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,MAAM,EAAE,CAGV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,QAAQ,EAAE,CAgBZ;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,wBAAwB,EAAE,CAkB5B;AAED,gFAAgF;AAChF,wBAAgB,8BAA8B,CAAC,EAC7C,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,KAAK,EACL,OAAO,GACR,EAAE,wBAAwB,GAAG,MAAM,CAInC"}
1
+ {"version":3,"file":"skill-script-references.d.ts","sourceRoot":"","sources":["../../../../../src/commands/init/tools/shared/skill-script-references.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAEV,cAAc,EACd,QAAQ,EACT,MAAM,8BAA8B,CAAC;AAEtC,iFAAiF;AACjF,eAAO,MAAM,yBAAyB,kBAAkB,CAAC;AAwKzD,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qEAAqE;AACrE,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,qBAAqB,CAAC;AAEvE,mEAAmE;AACnE,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,EAAE,CA4BvE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,MAAM,EAAE,CAUV;AAED,+CAA+C;AAC/C,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,QAAQ,EACd,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,MAAM,EAAE,CAQV;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,sBAAsB,CAIxB;AAED,sDAAsD;AACtD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,+BAA+B,CAOjC;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,MAAM,EAAE,CAGV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,QAAQ,EAAE,CAgBZ;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,wBAAwB,EAAE,CAkB5B;AAED,gFAAgF;AAChF,wBAAgB,8BAA8B,CAAC,EAC7C,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,KAAK,EACL,OAAO,GACR,EAAE,wBAAwB,GAAG,MAAM,CAInC"}
@@ -112,7 +112,8 @@ function readReferenceToken(line, from) {
112
112
  *
113
113
  * `null` rejects the match outright for `myrepo_.oat/scripts/x.sh`, where the
114
114
  * underscore is glued to an identifier. The identifier test is Unicode-aware so
115
- * `café_.oat/scripts/x.sh` is rejected too.
115
+ * `café_.oat/scripts/x.sh` is rejected too. An odd backslash run before
116
+ * the opening emphasis escapes it; an even run preserves the delimiter.
116
117
  */
117
118
  function readOpeningEmphasis(line, at) {
118
119
  let start = at;
@@ -125,6 +126,12 @@ function readOpeningEmphasis(line, at) {
125
126
  const preceding = start > 0 ? line[start - 1] : '';
126
127
  if (IDENTIFIER_CHARACTER.test(preceding))
127
128
  return null;
129
+ let backslashStart = start;
130
+ while (backslashStart > 0 && line[backslashStart - 1] === '\\') {
131
+ backslashStart -= 1;
132
+ }
133
+ if ((start - backslashStart) % 2 === 1)
134
+ return null;
128
135
  return {
129
136
  asterisks: run.split('').filter((char) => char === '*').length,
130
137
  underscores: run.split('').filter((char) => char === '_').length,
@@ -1 +1 @@
1
- {"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/append.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/append.ts"],"names":[],"mappings":"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"}
@@ -1013,7 +1013,57 @@ export async function appendProjectLog(input, overrides = {}) {
1013
1013
  const logPath = join(projectPath, PROJECT_LOG_FILENAME);
1014
1014
  return withProjectLogLock(logPath, dependencies.lock, 'required', async () => appendLockedProjectLog(input, dependencies, logPath));
1015
1015
  }
1016
+ async function previewProjectLogAppend(input, dependencies) {
1017
+ const projectPath = await resolveTargetProject(input, dependencies);
1018
+ const prepared = await prepareProjectLogAppend(input, dependencies, join(projectPath, PROJECT_LOG_FILENAME));
1019
+ if (prepared.status === 'skipped') {
1020
+ return {
1021
+ status: 'dry-run',
1022
+ outcome: 'skipped',
1023
+ reason: prepared.reason,
1024
+ wouldAppend: false,
1025
+ wouldCreate: false,
1026
+ };
1027
+ }
1028
+ if (prepared.status === 'already-appended') {
1029
+ return {
1030
+ status: 'dry-run',
1031
+ outcome: 'already-appended',
1032
+ logPath: prepared.logPath,
1033
+ heading: prepared.heading,
1034
+ wouldAppend: false,
1035
+ wouldCreate: false,
1036
+ };
1037
+ }
1038
+ return {
1039
+ status: 'dry-run',
1040
+ outcome: 'append',
1041
+ logPath: prepared.logPath,
1042
+ heading: prepared.heading,
1043
+ wouldAppend: true,
1044
+ wouldCreate: prepared.instantiated !== undefined,
1045
+ ...(prepared.normalized ? { normalizedLineEndings: true } : {}),
1046
+ };
1047
+ }
1016
1048
  async function appendLockedProjectLog(input, dependencies, logPath) {
1049
+ // Normal append prepares fresh state only after taking its required lock.
1050
+ const prepared = await prepareProjectLogAppend(input, dependencies, logPath);
1051
+ if (prepared.status !== 'ready')
1052
+ return prepared;
1053
+ if (prepared.instantiated !== undefined) {
1054
+ await dependencies.writeLog(logPath, prepared.instantiated);
1055
+ }
1056
+ await appendEntry(logPath, prepared.heading, prepared.body, prepared.versionNote, dependencies);
1057
+ return {
1058
+ status: 'appended',
1059
+ logPath,
1060
+ heading: prepared.heading,
1061
+ created: prepared.instantiated !== undefined,
1062
+ ...(prepared.normalized ? { normalizedLineEndings: true } : {}),
1063
+ };
1064
+ }
1065
+ /** Read-only validation shared by advisory previews and locked mutations. */
1066
+ async function prepareProjectLogAppend(input, dependencies, logPath) {
1017
1067
  const logExists = await fileExists(logPath);
1018
1068
  if (!logExists) {
1019
1069
  const effective = await dependencies.resolveEffectiveConfig(input.repoRoot, join(input.home ?? homedir(), '.oat'));
@@ -1024,7 +1074,7 @@ async function appendLockedProjectLog(input, dependencies, logPath) {
1024
1074
  const date = dependencies.now().toISOString().slice(0, 10);
1025
1075
  const { heading, body, versionNote, normalized } = validateEntry(input, date);
1026
1076
  const idempotencyKey = validateIdempotencyKey(input.idempotencyKey, body);
1027
- let created = false;
1077
+ let instantiated;
1028
1078
  if (logExists) {
1029
1079
  const content = await dependencies.readLog(logPath);
1030
1080
  // A log whose markers two readers resolve differently cannot answer
@@ -1111,7 +1161,7 @@ async function appendLockedProjectLog(input, dependencies, logPath) {
1111
1161
  if (!logExists) {
1112
1162
  const assetsRoot = await dependencies.resolveAssetsRoot();
1113
1163
  const template = await readFile(join(assetsRoot, 'templates', PROJECT_LOG_FILENAME), 'utf8');
1114
- const instantiated = instantiateProjectLogTemplate(template, basename(dirname(logPath)), date);
1164
+ instantiated = instantiateProjectLogTemplate(template, basename(dirname(logPath)), date);
1115
1165
  // The project directory's basename lands in the template's title line, so a
1116
1166
  // directory named `demo<U+2028>## Notes` would create a log this command
1117
1167
  // then refuses on its next append. A mutator must never write content it
@@ -1119,16 +1169,15 @@ async function appendLockedProjectLog(input, dependencies, logPath) {
1119
1169
  if (containsAmbiguousProjectLogMarker(instantiated)) {
1120
1170
  throw new Error(`Project name ${basename(dirname(logPath))} would put a '## ' heading after a carriage return, U+2028, or U+2029 in the new log's title, leaving a file with two readings. Rename the project directory using line feeds only.`);
1121
1171
  }
1122
- await dependencies.writeLog(logPath, instantiated);
1123
- created = true;
1124
1172
  }
1125
- await appendEntry(logPath, heading, body, versionNote, dependencies);
1126
1173
  return {
1127
- status: 'appended',
1174
+ status: 'ready',
1128
1175
  logPath,
1129
1176
  heading,
1130
- created,
1131
- ...(normalized ? { normalizedLineEndings: true } : {}),
1177
+ body,
1178
+ versionNote,
1179
+ normalized,
1180
+ instantiated,
1132
1181
  };
1133
1182
  }
1134
1183
  async function samePath(left, right) {
@@ -1277,7 +1326,7 @@ async function runAppendCommand(options, context, dependencies) {
1277
1326
  }
1278
1327
  let result;
1279
1328
  try {
1280
- result = await appendProjectLog({
1329
+ result = await (options.dryRun ? previewProjectLogAppend : appendProjectLog)({
1281
1330
  repoRoot,
1282
1331
  home: context.home,
1283
1332
  ...options,
@@ -1306,7 +1355,9 @@ async function runAppendCommand(options, context, dependencies) {
1306
1355
  }
1307
1356
  let commit;
1308
1357
  let receiptRemoved = false;
1309
- if (options.commit && result.status !== 'skipped') {
1358
+ if (options.commit &&
1359
+ result.status !== 'skipped' &&
1360
+ result.status !== 'dry-run') {
1310
1361
  commit = await commitProjectLog({
1311
1362
  repoRoot,
1312
1363
  logPath: result.logPath,
@@ -1330,6 +1381,14 @@ async function runAppendCommand(options, context, dependencies) {
1330
1381
  : {}),
1331
1382
  });
1332
1383
  }
1384
+ else if (result.status === 'dry-run') {
1385
+ const prediction = result.outcome === 'append'
1386
+ ? `Would append project log entry: ${result.logPath}${result.wouldCreate ? ' (would create log)' : ''}.`
1387
+ : result.outcome === 'already-appended'
1388
+ ? `Project log entry already present: ${result.heading}.`
1389
+ : 'Would skip project log append (projectLog=false).';
1390
+ context.logger.success(`${prediction} No changes were made.`);
1391
+ }
1333
1392
  else if (result.status === 'appended') {
1334
1393
  context.logger.success(`Appended project log entry: ${result.logPath}`);
1335
1394
  }
@@ -1360,6 +1419,7 @@ export function createProjectLogAppendCommand(overrides = {}) {
1360
1419
  const dependencies = { ...DEFAULT_COMMAND_DEPENDENCIES, ...overrides };
1361
1420
  return new Command('append')
1362
1421
  .description('Append a validated entry to the active project log')
1422
+ .option('--dry-run', 'Validate and preview without writing files, acquiring locks, or committing')
1363
1423
  .option('--structural', 'Write a structural lifecycle entry')
1364
1424
  .option('--type <type>', 'Judgment type: bug, friction, worked-well, feedback')
1365
1425
  .option('--scope <scope>', 'Judgment scope: project or general')
@@ -0,0 +1,3 @@
1
+ /** Specific advisory serialization for cooperating AGENTS.md appenders. */
2
+ export declare function acquireAgentsMdAppendLock(repoRoot: string, targetPath: string): Promise<() => Promise<void>>;
3
+ //# sourceMappingURL=agents-md-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents-md-lock.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/agents-md-lock.ts"],"names":[],"mappings":"AAOA,2EAA2E;AAC3E,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CA6I9B"}
@@ -0,0 +1,130 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { lstat, open, unlink } from 'node:fs/promises';
4
+ import { hostname } from 'node:os';
5
+ import { relative } from 'node:path';
6
+ import { setTimeout as delay } from 'node:timers/promises';
7
+ /** Specific advisory serialization for cooperating AGENTS.md appenders. */
8
+ export async function acquireAgentsMdAppendLock(repoRoot, targetPath) {
9
+ const lockPath = `${targetPath}.oat-guidance.lock`;
10
+ const location = relative(repoRoot, lockPath);
11
+ const recovery = `Check the recorded host/PID and remove ${location} manually only after verifying its owner is gone, then rerun.`;
12
+ const token = randomUUID();
13
+ for (let attempt = 0; attempt < 40; attempt += 1) {
14
+ let handle;
15
+ try {
16
+ handle = await open(lockPath, constants.O_RDWR |
17
+ constants.O_CREAT |
18
+ constants.O_EXCL |
19
+ constants.O_NOFOLLOW |
20
+ constants.O_NONBLOCK, 0o600);
21
+ }
22
+ catch (error) {
23
+ if (!(error instanceof Error) ||
24
+ !('code' in error) ||
25
+ error.code !== 'EEXIST') {
26
+ throw new Error(`AGENTS.md append lock could not be acquired. ${recovery}`, { cause: error });
27
+ }
28
+ // Never steal, truncate, or follow a contender's lock, including stale locks.
29
+ let reader;
30
+ try {
31
+ reader = await open(lockPath, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
32
+ const stat = await reader.stat();
33
+ if (!stat.isFile() || stat.nlink !== 1 || stat.size > 4096)
34
+ throw new Error('Unsafe lock', { cause: error });
35
+ const content = await reader.readFile('utf8');
36
+ if (content === '' && attempt < 39) {
37
+ await delay(25);
38
+ continue;
39
+ }
40
+ const owner = JSON.parse(content);
41
+ if (typeof owner.token !== 'string' ||
42
+ typeof owner.host !== 'string' ||
43
+ !Number.isSafeInteger(owner.pid) ||
44
+ owner.pid <= 0)
45
+ throw new Error('Malformed lock', { cause: error });
46
+ }
47
+ catch (inspectionError) {
48
+ if (inspectionError instanceof Error &&
49
+ 'code' in inspectionError &&
50
+ inspectionError.code === 'ENOENT')
51
+ continue;
52
+ throw new Error(`AGENTS.md append lock is unsafe or malformed. ${recovery}`, { cause: inspectionError });
53
+ }
54
+ finally {
55
+ await reader?.close();
56
+ }
57
+ if (attempt < 39)
58
+ await delay(25);
59
+ continue;
60
+ }
61
+ const identity = await handle.stat();
62
+ const bytes = JSON.stringify({ token, host: hostname(), pid: process.pid });
63
+ let releasing;
64
+ let interrupted;
65
+ const onInt = () => {
66
+ interrupted = 'SIGINT';
67
+ };
68
+ const onTerm = () => {
69
+ interrupted = 'SIGTERM';
70
+ };
71
+ const release = () => {
72
+ releasing ??= (async () => {
73
+ process.removeListener('SIGINT', onInt);
74
+ process.removeListener('SIGTERM', onTerm);
75
+ try {
76
+ const current = await lstat(lockPath);
77
+ if (current.isFile() &&
78
+ current.nlink === 1 &&
79
+ current.dev === identity.dev &&
80
+ current.ino === identity.ino &&
81
+ current.size === Buffer.byteLength(bytes) &&
82
+ (await readOwnedBytes()) === bytes) {
83
+ const verified = await lstat(lockPath);
84
+ if (verified.dev === identity.dev && verified.ino === identity.ino)
85
+ await unlink(lockPath);
86
+ }
87
+ }
88
+ catch (error) {
89
+ if (!(error instanceof Error) ||
90
+ !('code' in error) ||
91
+ error.code !== 'ENOENT')
92
+ throw error;
93
+ }
94
+ finally {
95
+ await handle.close();
96
+ if (interrupted)
97
+ process.kill(process.pid, interrupted);
98
+ }
99
+ })().catch((error) => {
100
+ throw new Error(`AGENTS.md append lock cleanup failed; guidance may already have been written. ${recovery}`, { cause: error });
101
+ });
102
+ return releasing;
103
+ };
104
+ const readOwnedBytes = async () => {
105
+ const buffer = Buffer.alloc(Buffer.byteLength(bytes));
106
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
107
+ return buffer.subarray(0, bytesRead).toString('utf8');
108
+ };
109
+ try {
110
+ await handle.writeFile(bytes);
111
+ }
112
+ catch (error) {
113
+ // Initialization failed before returning ownership; remove only this inode.
114
+ try {
115
+ const current = await lstat(lockPath);
116
+ if (current.dev === identity.dev && current.ino === identity.ino)
117
+ await unlink(lockPath);
118
+ }
119
+ finally {
120
+ await handle.close();
121
+ }
122
+ throw error;
123
+ }
124
+ // Cleanup runs after the critical section and on catchable termination.
125
+ process.once('SIGINT', onInt);
126
+ process.once('SIGTERM', onTerm);
127
+ return release;
128
+ }
129
+ throw new Error(`AGENTS.md append lock is held by another writer. ${recovery}`);
130
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"agents-md.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/agents-md.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agents-md.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/agents-md.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { constants } from 'node:fs';
2
2
  import { lstat, open, readFile, readlink, realpath, writeFile, } from 'node:fs/promises';
3
3
  import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
4
+ import { acquireAgentsMdAppendLock } from './agents-md-lock.js';
4
5
  const defaultFileSystem = {
5
6
  lstat,
6
7
  open,
@@ -496,7 +497,7 @@ function classifySections(plan, desired, content, removeSectionKeys, appendRefus
496
497
  };
497
498
  }
498
499
  /** Inspect current bytes and identity without opening a write handle. */
499
- async function inspectSections(plan, sections, removeSectionKeys, fileSystem) {
500
+ async function inspectSections(plan, sections, removeSectionKeys, fileSystem, validateContent = true) {
500
501
  const desired = sections.map(({ key, body }) => ({
501
502
  key,
502
503
  block: buildSection(key, body),
@@ -505,7 +506,7 @@ async function inspectSections(plan, sections, removeSectionKeys, fileSystem) {
505
506
  ? ''
506
507
  : await fileSystem.readFile(plan.targetPath, 'utf8');
507
508
  const classification = classifySections(plan, desired, content, removeSectionKeys);
508
- await assertPlanUnchanged(plan, fileSystem, plan.kind === 'missing' ? undefined : content);
509
+ await assertPlanUnchanged(plan, fileSystem, plan.kind === 'missing' || !validateContent ? undefined : content);
509
510
  return { content, desired, classification };
510
511
  }
511
512
  async function upsertSectionsInternal(repoRoot, sections, removeSectionKeys, fileSystem, allowReplan) {
@@ -518,7 +519,7 @@ async function upsertSectionsInternal(repoRoot, sections, removeSectionKeys, fil
518
519
  return Object.fromEntries(sections.map(({ key }) => [key, blocked]));
519
520
  }
520
521
  try {
521
- const { desired, content, classification } = await inspectSections(plan, sections, removeSectionKeys, fileSystem);
522
+ let { desired, content, classification } = await inspectSections(plan, sections, removeSectionKeys, fileSystem, false);
522
523
  if (plan.kind === 'missing') {
523
524
  const creation = await createMissingFile(plan, `${desired.map(({ block }) => block).join('\n\n')}\n`, fileSystem);
524
525
  if (creation === 'created')
@@ -528,12 +529,29 @@ async function upsertSectionsInternal(repoRoot, sections, removeSectionKeys, fil
528
529
  }
529
530
  throw new Error(IDENTITY_CHANGED);
530
531
  }
532
+ if (classification.absentBlocks.length === 0) {
533
+ // Non-appending outcomes retain their original content-stability check
534
+ // without creating a lock; only append candidates use the locked re-read.
535
+ await assertPlanUnchanged(plan, fileSystem, content);
536
+ return classification.results;
537
+ }
531
538
  if (classification.absentBlocks.length > 0) {
532
- const appended = await appendAbsentSections(plan, classification.absentBlocks, fileSystem);
533
- if (appended.kind === 'blocked')
534
- throw new Error(appended.reason);
535
- if (appended.kind === 'refused') {
536
- return classifySections(plan, desired, content, removeSectionKeys, appended.cause).results;
539
+ const release = await acquireAgentsMdAppendLock(plan.repoRoot, plan.targetPath);
540
+ try {
541
+ // Revalidate the original identity and classify fresh bytes under the lock.
542
+ ({ desired, content, classification } = await inspectSections(plan, sections, removeSectionKeys, fileSystem));
543
+ if (classification.absentBlocks.length > 0) {
544
+ const appended = await appendAbsentSections(plan, classification.absentBlocks, fileSystem);
545
+ if (appended.kind === 'blocked')
546
+ throw new Error(appended.reason);
547
+ if (appended.kind === 'refused') {
548
+ return classifySections(plan, desired, content, removeSectionKeys, appended.cause).results;
549
+ }
550
+ }
551
+ return classification.results;
552
+ }
553
+ finally {
554
+ await release();
537
555
  }
538
556
  }
539
557
  return classification.results;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/cli",
3
- "version": "0.3.17",
3
+ "version": "0.3.18",
4
4
  "private": false,
5
5
  "description": "Open Agent Toolkit CLI",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
@@ -35,7 +35,7 @@
35
35
  "ora": "^9.0.0",
36
36
  "yaml": "2.8.2",
37
37
  "zod": "^3.25.76",
38
- "@open-agent-toolkit/control-plane": "0.3.17"
38
+ "@open-agent-toolkit/control-plane": "0.3.18"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^22.10.0",