@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.
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/workflows/projects/execution/project-log.md +22 -0
- package/assets/public-package-versions.json +4 -4
- package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -1
- package/dist/commands/init/tools/shared/skill-script-references.js +8 -1
- package/dist/commands/project/log/append.d.ts.map +1 -1
- package/dist/commands/project/log/append.js +70 -10
- package/dist/commands/shared/agents-md-lock.d.ts +3 -0
- package/dist/commands/shared/agents-md-lock.d.ts.map +1 -0
- package/dist/commands/shared/agents-md-lock.js +130 -0
- package/dist/commands/shared/agents-md.d.ts.map +1 -1
- package/dist/commands/shared/agents-md.js +26 -8
- package/package.json +2 -2
|
@@ -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 +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;
|
|
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
|
|
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
|
-
|
|
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: '
|
|
1174
|
+
status: 'ready',
|
|
1128
1175
|
logPath,
|
|
1129
1176
|
heading,
|
|
1130
|
-
|
|
1131
|
-
|
|
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 &&
|
|
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 @@
|
|
|
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":"AACA,OAAO,EACL,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,EACV,MAAM,kBAAkB,CAAC;
|
|
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
|
-
|
|
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
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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.
|
|
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.
|
|
38
|
+
"@open-agent-toolkit/control-plane": "0.3.18"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.10.0",
|