driftseal 1.1.3 → 1.1.5
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/README.md +11 -4
- package/README.zh-CN.md +9 -4
- package/bin/driftseal.js +232 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,12 @@ driftseal init
|
|
|
36
36
|
|
|
37
37
|
`driftseal init` writes the protocol to `AGENTS.md`, including how to `absorb`
|
|
38
38
|
colliding worktree logs, and configures the local git merge driver. It can be
|
|
39
|
-
run again without duplicating either.
|
|
39
|
+
run again without duplicating either. Pass `--lang zh-CN` (or another
|
|
40
|
+
[BCP 47](https://www.rfc-editor.org/rfc/rfc5646.html) tag) to declare the
|
|
41
|
+
language agents should use for intent and decision prose; the default is `en`.
|
|
42
|
+
Command names, flags, status tokens, ids, and MADR section headings stay in
|
|
43
|
+
English. Re-running `init` without `--lang` preserves the declared language
|
|
44
|
+
while upgrading the protocol. DriftSeal requires Node.js 18+.
|
|
40
45
|
|
|
41
46
|
For local development from this checkout:
|
|
42
47
|
|
|
@@ -231,7 +236,7 @@ content change starts a new work round.
|
|
|
231
236
|
| `driftseal mcp install --target TARGET [--scope project\|global] [--root path] [--force]` | Install the repository-pinned MCP server into Codex, Kimi Code, OpenCode, Claude Code, or Cursor. |
|
|
232
237
|
| `driftseal hook install --target TARGET [--scope project\|global] [--root path] [--force]` | Install advisory lifecycle reminders into Kimi Code, Claude Code, or Codex. |
|
|
233
238
|
| `driftseal hook prompt\|stop [--format plain\|claude-code]` | Emit the reminder a lifecycle hook injects; never blocks. |
|
|
234
|
-
| `driftseal init` | Add the adoption protocol to `AGENTS.md` and configure the git merge driver. |
|
|
239
|
+
| `driftseal init [--lang <tag>]` | Add the adoption protocol to `AGENTS.md` and configure the git merge driver. `--lang` sets the intent/decision log language (BCP 47, default `en`). |
|
|
235
240
|
| `driftseal --version` or `driftseal -V` | Print the installed DriftSeal version. |
|
|
236
241
|
| `driftseal help` | Print CLI usage. |
|
|
237
242
|
|
|
@@ -274,8 +279,10 @@ conflicts cannot block later decision work.
|
|
|
274
279
|
New events carry a schema version. DriftSeal rejects newer unsupported schemas and
|
|
275
280
|
fails closed if a legacy client closes a linked intent without reconciliation.
|
|
276
281
|
`driftseal init` writes versioned managed blocks and upgrades only exact, recognized
|
|
277
|
-
older blocks.
|
|
278
|
-
|
|
282
|
+
older blocks. Current-version blocks that differ only by log language are also
|
|
283
|
+
recognized, so `--lang` can change the language without rewriting policy by hand.
|
|
284
|
+
It refuses newer protocol versions and any unrecognized or customized block
|
|
285
|
+
without changing `AGENTS.md`.
|
|
279
286
|
|
|
280
287
|
`--count` prints only the number of records remaining after status filtering.
|
|
281
288
|
It cannot be combined with `--last`, whose limiting semantics would make the
|
package/README.zh-CN.md
CHANGED
|
@@ -35,7 +35,11 @@ driftseal init
|
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
`driftseal init` 会把协议写入 `AGENTS.md`(包括 worktree 撞号后如何 `absorb`),
|
|
38
|
-
并配置 local git merge driver
|
|
38
|
+
并配置 local git merge driver。重复运行不会产生副本。用 `--lang zh-CN`(或其他
|
|
39
|
+
[BCP 47](https://www.rfc-editor.org/rfc/rfc5646.html) 标签)声明 agent 写入
|
|
40
|
+
intent / decision 正文时应使用的语言,默认是 `en`。命令名、flag、status token、
|
|
41
|
+
id 以及 MADR 小节标题仍保持英文。再次运行不带 `--lang` 的 `init` 会保留已声明
|
|
42
|
+
的语言并升级协议。DriftSeal 需要 Node.js 18+。
|
|
39
43
|
|
|
40
44
|
从当前 checkout 本地开发时:
|
|
41
45
|
|
|
@@ -220,7 +224,7 @@ Git 操作完全不计入 intent log,因为 Git 会自行维护历史。查看
|
|
|
220
224
|
| `driftseal mcp install --target TARGET [--scope project\|global] [--root path] [--force]` | 把固定到 repository 的 MCP server 安装到 Codex、Kimi Code、OpenCode、Claude Code 或 Cursor。 |
|
|
221
225
|
| `driftseal hook install --target TARGET [--scope project\|global] [--root path] [--force]` | 把建议性的 lifecycle 提醒安装到 Kimi Code、Claude Code 或 Codex。 |
|
|
222
226
|
| `driftseal hook prompt\|stop [--format plain\|claude-code]` | 输出 lifecycle hook 注入的提醒;绝不阻断。 |
|
|
223
|
-
| `driftseal init` | 把接入协议写入 `AGENTS.md`,并配置 git merge driver
|
|
227
|
+
| `driftseal init [--lang <tag>]` | 把接入协议写入 `AGENTS.md`,并配置 git merge driver。`--lang` 设置 intent / decision log 的语言(BCP 47,默认 `en`)。 |
|
|
224
228
|
| `driftseal --version` 或 `driftseal -V` | 输出当前安装的 DriftSeal 版本。 |
|
|
225
229
|
| `driftseal help` | 查看 CLI 用法。 |
|
|
226
230
|
|
|
@@ -257,8 +261,9 @@ recovery 只处理当前 intent,因此历史冲突不会阻塞之后的 decisi
|
|
|
257
261
|
新 event 带有 schema version。遇到更高且不支持的版本时,DriftSeal 会拒绝继续;如果
|
|
258
262
|
旧 client 未经 reconciliation 就关闭 linked intent,新 client 也会 fail closed。
|
|
259
263
|
`driftseal init` 会写入带版本的 managed blocks,并且只升级内容完全匹配的已知旧版本。
|
|
260
|
-
|
|
261
|
-
|
|
264
|
+
当前版本的 block 如果只是 log language 不同,也会被识别,因此可以用 `--lang`
|
|
265
|
+
改语言而不必手改协议。遇到更新的协议版本、无法识别的 block 或自定义内容时,
|
|
266
|
+
它会保持 `AGENTS.md` 不变并拒绝继续。
|
|
262
267
|
|
|
263
268
|
`--count` 只输出 status 筛选后的记录总数。它不能与 `--last` 一起使用,以免
|
|
264
269
|
“先限制再计数”造成歧义。Decision 文件名会构成一个轻量的内存索引:`show` 只
|
package/bin/driftseal.js
CHANGED
|
@@ -36,7 +36,8 @@ const DECISION_STATUSES = [
|
|
|
36
36
|
'superseded',
|
|
37
37
|
];
|
|
38
38
|
const EVENT_SCHEMA_VERSION = 3;
|
|
39
|
-
const PROTOCOL_VERSION =
|
|
39
|
+
const PROTOCOL_VERSION = 11;
|
|
40
|
+
const DEFAULT_LOG_LANGUAGE = 'en';
|
|
40
41
|
const LOCK_STALE_MS = 30 * 60 * 1000;
|
|
41
42
|
const LOCK_INIT_STALE_MS = 5 * 1000;
|
|
42
43
|
const MAX_DECISION_SLUG_LENGTH = 180;
|
|
@@ -1122,10 +1123,213 @@ const INTENT_PROTOCOL_MARKER = '<!-- driftseal -->';
|
|
|
1122
1123
|
const INTENT_PROTOCOL_END = '<!-- /driftseal -->';
|
|
1123
1124
|
const DECISION_PROTOCOL_MARKER = '<!-- driftseal-decisions -->';
|
|
1124
1125
|
const DECISION_PROTOCOL_END = '<!-- /driftseal-decisions -->';
|
|
1126
|
+
const LOG_LANGUAGE_COMMENT_RE = /^<!-- driftseal-log-language: ([^>\r\n]+) -->\r?$/m;
|
|
1127
|
+
const LOG_LANGUAGE_PROSE_RE = /\*\*Log language:\*\* `([^`]+)`/;
|
|
1128
|
+
const IRREGULAR_GRANDFATHERED_TAGS = new Map([
|
|
1129
|
+
['en-gb-oed', 'en-GB-oed'],
|
|
1130
|
+
['i-ami', 'i-ami'],
|
|
1131
|
+
['i-bnn', 'i-bnn'],
|
|
1132
|
+
['i-default', 'i-default'],
|
|
1133
|
+
['i-enochian', 'i-enochian'],
|
|
1134
|
+
['i-hak', 'i-hak'],
|
|
1135
|
+
['i-klingon', 'i-klingon'],
|
|
1136
|
+
['i-lux', 'i-lux'],
|
|
1137
|
+
['i-mingo', 'i-mingo'],
|
|
1138
|
+
['i-navajo', 'i-navajo'],
|
|
1139
|
+
['i-pwn', 'i-pwn'],
|
|
1140
|
+
['i-tao', 'i-tao'],
|
|
1141
|
+
['i-tay', 'i-tay'],
|
|
1142
|
+
['i-tsu', 'i-tsu'],
|
|
1143
|
+
['sgn-be-fr', 'sgn-BE-FR'],
|
|
1144
|
+
['sgn-be-nl', 'sgn-BE-NL'],
|
|
1145
|
+
['sgn-ch-de', 'sgn-CH-DE'],
|
|
1146
|
+
]);
|
|
1147
|
+
|
|
1148
|
+
function titleCaseSubtag(value) {
|
|
1149
|
+
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
function isBcp47Variant(subtag) {
|
|
1153
|
+
return /^[A-Za-z0-9]{5,8}$/.test(subtag) || /^[0-9][A-Za-z0-9]{3}$/.test(subtag);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function isBcp47Singleton(subtag) {
|
|
1157
|
+
return /^[0-9A-WY-Za-wy-z]$/.test(subtag);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function parsePrivateUseSubtags(subtags, start, parts) {
|
|
1161
|
+
if (start >= subtags.length || !/^x$/i.test(subtags[start])) return null;
|
|
1162
|
+
parts.push('x');
|
|
1163
|
+
let index = start + 1;
|
|
1164
|
+
let count = 0;
|
|
1165
|
+
while (index < subtags.length && /^[A-Za-z0-9]{1,8}$/.test(subtags[index])) {
|
|
1166
|
+
parts.push(subtags[index].toLowerCase());
|
|
1167
|
+
index += 1;
|
|
1168
|
+
count += 1;
|
|
1169
|
+
}
|
|
1170
|
+
if (count === 0 || index !== subtags.length) return null;
|
|
1171
|
+
return parts.join('-');
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function wellFormedBcp47(tag) {
|
|
1175
|
+
if (typeof tag !== 'string' || tag.length === 0 || tag.startsWith('-') || tag.endsWith('-')) {
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
1178
|
+
const irregular = IRREGULAR_GRANDFATHERED_TAGS.get(tag.toLowerCase());
|
|
1179
|
+
if (irregular) return irregular;
|
|
1180
|
+
|
|
1181
|
+
const subtags = tag.split('-');
|
|
1182
|
+
if (subtags.some((subtag) => subtag.length === 0)) return null;
|
|
1183
|
+
|
|
1184
|
+
const parts = [];
|
|
1185
|
+
if (/^x$/i.test(subtags[0])) return parsePrivateUseSubtags(subtags, 0, parts);
|
|
1186
|
+
|
|
1187
|
+
let index = 0;
|
|
1188
|
+
const language = subtags[index];
|
|
1189
|
+
if (!/^[A-Za-z]{2,8}$/.test(language)) return null;
|
|
1190
|
+
parts.push(language.toLowerCase());
|
|
1191
|
+
index += 1;
|
|
1192
|
+
if (language.length <= 3) {
|
|
1193
|
+
let extlang = 0;
|
|
1194
|
+
while (index < subtags.length && /^[A-Za-z]{3}$/.test(subtags[index]) && extlang < 3) {
|
|
1195
|
+
parts.push(subtags[index].toLowerCase());
|
|
1196
|
+
index += 1;
|
|
1197
|
+
extlang += 1;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (index < subtags.length && /^[A-Za-z]{4}$/.test(subtags[index])) {
|
|
1202
|
+
parts.push(titleCaseSubtag(subtags[index]));
|
|
1203
|
+
index += 1;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (index < subtags.length && /^[A-Za-z]{2}$/.test(subtags[index])) {
|
|
1207
|
+
parts.push(subtags[index].toUpperCase());
|
|
1208
|
+
index += 1;
|
|
1209
|
+
} else if (index < subtags.length && /^[0-9]{3}$/.test(subtags[index])) {
|
|
1210
|
+
parts.push(subtags[index]);
|
|
1211
|
+
index += 1;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
const variants = new Set();
|
|
1215
|
+
while (index < subtags.length && isBcp47Variant(subtags[index])) {
|
|
1216
|
+
const variant = subtags[index].toLowerCase();
|
|
1217
|
+
if (variants.has(variant)) return null;
|
|
1218
|
+
variants.add(variant);
|
|
1219
|
+
parts.push(variant);
|
|
1220
|
+
index += 1;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
const singletons = new Set();
|
|
1224
|
+
while (index < subtags.length && isBcp47Singleton(subtags[index])) {
|
|
1225
|
+
const singleton = subtags[index].toLowerCase();
|
|
1226
|
+
if (singletons.has(singleton)) return null;
|
|
1227
|
+
singletons.add(singleton);
|
|
1228
|
+
parts.push(singleton);
|
|
1229
|
+
index += 1;
|
|
1230
|
+
let following = 0;
|
|
1231
|
+
while (index < subtags.length && /^[A-Za-z0-9]{2,8}$/.test(subtags[index])) {
|
|
1232
|
+
parts.push(subtags[index].toLowerCase());
|
|
1233
|
+
index += 1;
|
|
1234
|
+
following += 1;
|
|
1235
|
+
}
|
|
1236
|
+
if (following === 0) return null;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
if (index < subtags.length && /^x$/i.test(subtags[index])) {
|
|
1240
|
+
return parsePrivateUseSubtags(subtags, index, parts);
|
|
1241
|
+
}
|
|
1242
|
+
if (index !== subtags.length) return null;
|
|
1243
|
+
return parts.join('-');
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
function canonicalizeLogLanguage(value) {
|
|
1247
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
1248
|
+
fail('invalid log language: use a BCP 47 tag such as en or zh-CN');
|
|
1249
|
+
}
|
|
1250
|
+
const canonical = wellFormedBcp47(value.trim());
|
|
1251
|
+
if (!canonical) {
|
|
1252
|
+
fail(`invalid log language "${value}": use a BCP 47 tag such as en or zh-CN`);
|
|
1253
|
+
}
|
|
1254
|
+
return canonical;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function intentLogLanguageParagraph(language) {
|
|
1258
|
+
return `**Log language:** \`${language}\`. Write intent-log prose (intent, note,
|
|
1259
|
+
verify-result, and reclaim/unreclaim reason) in that language. Keep command
|
|
1260
|
+
names, flags, status tokens, and ids in English.`;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
function decisionLogLanguageParagraph(language) {
|
|
1264
|
+
return `**Log language:** \`${language}\`. Write decision-log prose (title, context,
|
|
1265
|
+
outcome, drivers, options, consequences, and update notes) in that language.
|
|
1266
|
+
Keep MADR section headings, status tokens, and ids in English.`;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
function parseLogLanguageFromBlock(block, label) {
|
|
1270
|
+
const commentMatch = block.match(LOG_LANGUAGE_COMMENT_RE);
|
|
1271
|
+
const proseMatch = block.match(LOG_LANGUAGE_PROSE_RE);
|
|
1272
|
+
const comment = commentMatch ? canonicalizeLogLanguage(commentMatch[1]) : null;
|
|
1273
|
+
const prose = proseMatch ? canonicalizeLogLanguage(proseMatch[1]) : null;
|
|
1274
|
+
if (comment && prose && comment !== prose) {
|
|
1275
|
+
fail(
|
|
1276
|
+
`${label} declares different log languages in the comment (${comment}) and prose (${prose}); pass --lang to set one`
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
return comment || prose || null;
|
|
1280
|
+
}
|
|
1125
1281
|
|
|
1126
|
-
function
|
|
1282
|
+
function extractManagedBlock(content, marker, endMarker) {
|
|
1283
|
+
const start = content.indexOf(marker);
|
|
1284
|
+
if (start === -1) return null;
|
|
1285
|
+
const end = content.indexOf(endMarker, start);
|
|
1286
|
+
if (end === -1) return null;
|
|
1287
|
+
return content.slice(start, end + endMarker.length);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function resolveInitLogLanguage(requested, content) {
|
|
1291
|
+
if (requested !== undefined) return canonicalizeLogLanguage(requested);
|
|
1292
|
+
const languages = new Set();
|
|
1293
|
+
const intent = extractManagedBlock(content, INTENT_PROTOCOL_MARKER, INTENT_PROTOCOL_END);
|
|
1294
|
+
const decision = extractManagedBlock(content, DECISION_PROTOCOL_MARKER, DECISION_PROTOCOL_END);
|
|
1295
|
+
if (intent) {
|
|
1296
|
+
const language = parseLogLanguageFromBlock(intent, 'intent protocol');
|
|
1297
|
+
if (language) languages.add(language);
|
|
1298
|
+
}
|
|
1299
|
+
if (decision) {
|
|
1300
|
+
const language = parseLogLanguageFromBlock(decision, 'decision protocol');
|
|
1301
|
+
if (language) languages.add(language);
|
|
1302
|
+
}
|
|
1303
|
+
if (languages.size > 1) {
|
|
1304
|
+
fail(
|
|
1305
|
+
`intent and decision protocols declare different log languages (${[...languages].join(', ')}); pass --lang to set one`
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
return languages.size === 1 ? [...languages][0] : DEFAULT_LOG_LANGUAGE;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
function protocolBlockKey(block) {
|
|
1312
|
+
return block
|
|
1313
|
+
.replace(/^<!-- driftseal-log-language: [^>\r\n]+ -->\r?$/m, '<!-- driftseal-log-language: -->')
|
|
1314
|
+
.replace(/\*\*Log language:\*\* `[^`]+`/g, '**Log language:** ``');
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function stripIntentLogLanguage(block, language = DEFAULT_LOG_LANGUAGE) {
|
|
1318
|
+
return block
|
|
1319
|
+
.replace(`\n<!-- driftseal-log-language: ${language} -->`, '')
|
|
1320
|
+
.replace(`\n${intentLogLanguageParagraph(language)}\n`, '');
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
function stripDecisionLogLanguage(block, language = DEFAULT_LOG_LANGUAGE) {
|
|
1324
|
+
return block
|
|
1325
|
+
.replace(`\n<!-- driftseal-log-language: ${language} -->`, '')
|
|
1326
|
+
.replace(`\n${decisionLogLanguageParagraph(language)}\n`, '');
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
function intentProtocolBlock(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE) {
|
|
1127
1330
|
return `${INTENT_PROTOCOL_MARKER}
|
|
1128
1331
|
<!-- driftseal-version: ${version} -->
|
|
1332
|
+
<!-- driftseal-log-language: ${language} -->
|
|
1129
1333
|
|
|
1130
1334
|
## Agent protocol: intent write-ahead log
|
|
1131
1335
|
|
|
@@ -1135,6 +1339,8 @@ This \`AGENTS.md\` protocol is the source of truth. Use the \`driftseal\` CLI by
|
|
|
1135
1339
|
default; the companion skill only helps discover and resume the workflow, while
|
|
1136
1340
|
MCP and lifecycle hooks are optional adapters.
|
|
1137
1341
|
|
|
1342
|
+
${intentLogLanguageParagraph(language)}
|
|
1343
|
+
|
|
1138
1344
|
1. **Write intent first**, before modifying, creating, or deleting files, or
|
|
1139
1345
|
making any other non-Git change that may need a rollback:
|
|
1140
1346
|
\`driftseal begin "<what this round will accomplish>" --verify "<command or check that proves it>"\`.
|
|
@@ -1178,8 +1384,9 @@ ${INTENT_PROTOCOL_END}`;
|
|
|
1178
1384
|
}
|
|
1179
1385
|
|
|
1180
1386
|
function previousIntentProtocolBlock(version) {
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1387
|
+
const v10 = stripIntentLogLanguage(intentProtocolBlock(version, DEFAULT_LOG_LANGUAGE));
|
|
1388
|
+
if (version >= 10) return v10;
|
|
1389
|
+
const v9 = v10.replace(
|
|
1183
1390
|
'1. **Write intent first**, before modifying, creating, or deleting files, or\n' +
|
|
1184
1391
|
' making any other non-Git change that may need a rollback:\n' +
|
|
1185
1392
|
' `driftseal begin "<what this round will accomplish>" --verify "<command or check that proves it>"`.\n' +
|
|
@@ -1258,9 +1465,10 @@ function protocolEol(content, eol) {
|
|
|
1258
1465
|
return eol === '\n' ? content : content.replace(/\n/g, eol);
|
|
1259
1466
|
}
|
|
1260
1467
|
|
|
1261
|
-
function decisionProtocolBlock(version = PROTOCOL_VERSION) {
|
|
1468
|
+
function decisionProtocolBlock(version = PROTOCOL_VERSION, language = DEFAULT_LOG_LANGUAGE) {
|
|
1262
1469
|
return `${DECISION_PROTOCOL_MARKER}
|
|
1263
1470
|
<!-- driftseal-decisions-version: ${version} -->
|
|
1471
|
+
<!-- driftseal-log-language: ${language} -->
|
|
1264
1472
|
|
|
1265
1473
|
## Agent protocol: decision log
|
|
1266
1474
|
|
|
@@ -1270,6 +1478,8 @@ revisiting, non-obvious rationale behind a long-lived or costly-to-reverse accep
|
|
|
1270
1478
|
choice, or a deprecated or superseded decision. Do not record routine, local,
|
|
1271
1479
|
readily reversible choices.
|
|
1272
1480
|
|
|
1481
|
+
${decisionLogLanguageParagraph(language)}
|
|
1482
|
+
|
|
1273
1483
|
\`driftseal decision add "<title>" --context "<problem and constraints>" --outcome "<decision and rationale>" --driver "<decision driver>" --option "<considered option>" --consequence "<result>"\`
|
|
1274
1484
|
|
|
1275
1485
|
Add one \`--driver\`, \`--option\`, or \`--consequence\` flag per item. Use
|
|
@@ -1338,9 +1548,9 @@ Commit \`.decision-log/\` with the code.`;
|
|
|
1338
1548
|
}
|
|
1339
1549
|
|
|
1340
1550
|
function previousDecisionProtocolBlock(version) {
|
|
1341
|
-
const
|
|
1342
|
-
if (version >= 9) return
|
|
1343
|
-
const v8 =
|
|
1551
|
+
const v10 = stripDecisionLogLanguage(decisionProtocolBlock(version, DEFAULT_LOG_LANGUAGE));
|
|
1552
|
+
if (version >= 9) return v10;
|
|
1553
|
+
const v8 = v10.replace(
|
|
1344
1554
|
'\nAfter a merge, colliding decision ids are remapped with `driftseal absorb`;\n' +
|
|
1345
1555
|
'concurrent edits of a shared decision are not auto-merged.',
|
|
1346
1556
|
''
|
|
@@ -1381,7 +1591,11 @@ function upgradeManagedBlock({
|
|
|
1381
1591
|
`protocol version ${version} requires a newer DriftSeal client (supported: ${PROTOCOL_VERSION})`
|
|
1382
1592
|
);
|
|
1383
1593
|
}
|
|
1384
|
-
if (
|
|
1594
|
+
if (
|
|
1595
|
+
block !== replacement &&
|
|
1596
|
+
!knownManagedBlocks.includes(block) &&
|
|
1597
|
+
protocolBlockKey(block) !== protocolBlockKey(replacement)
|
|
1598
|
+
) {
|
|
1385
1599
|
fail(`cannot safely upgrade customized protocol block beginning with ${marker}`);
|
|
1386
1600
|
}
|
|
1387
1601
|
return {
|
|
@@ -3322,14 +3536,15 @@ const commands = {
|
|
|
3322
3536
|
},
|
|
3323
3537
|
|
|
3324
3538
|
init(argv) {
|
|
3325
|
-
const { positionals } = parseArgs(argv, {});
|
|
3326
|
-
if (positionals.length > 0) fail('usage: driftseal init');
|
|
3539
|
+
const { positionals, flags } = parseArgs(argv, { lang: 'single' });
|
|
3540
|
+
if (positionals.length > 0) fail('usage: driftseal init [--lang <tag>]');
|
|
3327
3541
|
const target = path.join(process.cwd(), 'AGENTS.md');
|
|
3328
3542
|
const existed = fs.existsSync(target);
|
|
3329
3543
|
const current = existed ? fs.readFileSync(target, 'utf8') : '';
|
|
3330
3544
|
const eol = current.includes('\r\n') ? '\r\n' : '\n';
|
|
3331
|
-
const
|
|
3332
|
-
const
|
|
3545
|
+
const language = resolveInitLogLanguage(flags.lang, current);
|
|
3546
|
+
const intentBlock = protocolEol(intentProtocolBlock(PROTOCOL_VERSION, language), eol);
|
|
3547
|
+
const decisionBlock = protocolEol(decisionProtocolBlock(PROTOCOL_VERSION, language), eol);
|
|
3333
3548
|
let updated = current;
|
|
3334
3549
|
const intent = upgradeManagedBlock({
|
|
3335
3550
|
content: updated,
|
|
@@ -3346,6 +3561,7 @@ const commands = {
|
|
|
3346
3561
|
protocolEol(previousIntentProtocolBlock(7), eol),
|
|
3347
3562
|
protocolEol(previousIntentProtocolBlock(8), eol),
|
|
3348
3563
|
protocolEol(previousIntentProtocolBlock(9), eol),
|
|
3564
|
+
protocolEol(previousIntentProtocolBlock(10), eol),
|
|
3349
3565
|
],
|
|
3350
3566
|
knownLegacyBlocks: [protocolEol(legacyIntentProtocolBlock(), eol)],
|
|
3351
3567
|
});
|
|
@@ -3365,6 +3581,7 @@ const commands = {
|
|
|
3365
3581
|
protocolEol(previousDecisionProtocolBlock(7), eol),
|
|
3366
3582
|
protocolEol(previousDecisionProtocolBlock(8), eol),
|
|
3367
3583
|
protocolEol(previousDecisionProtocolBlock(9), eol),
|
|
3584
|
+
protocolEol(previousDecisionProtocolBlock(10), eol),
|
|
3368
3585
|
],
|
|
3369
3586
|
knownLegacyBlocks: [protocolEol(legacyDecisionProtocolBlock(), eol)],
|
|
3370
3587
|
});
|
|
@@ -3445,7 +3662,8 @@ usage:
|
|
|
3445
3662
|
targets: kimi-code (global only), claude-code, codex (prompt only)
|
|
3446
3663
|
driftseal hook prompt|stop [--format plain|claude-code]
|
|
3447
3664
|
emit the reminder a lifecycle hook injects; never blocks
|
|
3448
|
-
driftseal init
|
|
3665
|
+
driftseal init [--lang <tag>] inject protocols into ./AGENTS.md and configure the git merge driver
|
|
3666
|
+
--lang sets the intent/decision log language (BCP 47, default: en)
|
|
3449
3667
|
driftseal --version | -V print the installed DriftSeal version
|
|
3450
3668
|
driftseal help
|
|
3451
3669
|
|