@synkro-sh/cli 1.6.44 → 1.6.45
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/bootstrap.js +53 -22
- package/dist/bootstrap.js.map +1 -1
- package/package.json +5 -2
package/dist/bootstrap.js
CHANGED
|
@@ -350,13 +350,13 @@ function installCursorHooks(hooksJsonPath, config) {
|
|
|
350
350
|
});
|
|
351
351
|
h.beforeShellExecution.push({
|
|
352
352
|
command: cursorCcCmd(config.cwePrecheckScriptPath),
|
|
353
|
-
timeout:
|
|
353
|
+
timeout: 30,
|
|
354
354
|
failClosed: false,
|
|
355
355
|
[SYNKRO_MARKER2]: true
|
|
356
356
|
});
|
|
357
357
|
h.beforeShellExecution.push({
|
|
358
358
|
command: bunRunCmd(config.bashJudgeScriptPath),
|
|
359
|
-
timeout:
|
|
359
|
+
timeout: 30,
|
|
360
360
|
failClosed: false,
|
|
361
361
|
[SYNKRO_MARKER2]: true
|
|
362
362
|
});
|
|
@@ -371,36 +371,36 @@ function installCursorHooks(hooksJsonPath, config) {
|
|
|
371
371
|
});
|
|
372
372
|
h.preToolUse.push({
|
|
373
373
|
command: cursorCcCmd(config.cwePrecheckScriptPath),
|
|
374
|
-
timeout:
|
|
374
|
+
timeout: 30,
|
|
375
375
|
failClosed: false,
|
|
376
376
|
matcher: "Shell|Bash|terminal|run_terminal_cmd|execute_command",
|
|
377
377
|
[SYNKRO_MARKER2]: true
|
|
378
378
|
});
|
|
379
379
|
h.preToolUse.push({
|
|
380
380
|
command: bunRunCmd(config.bashJudgeScriptPath),
|
|
381
|
-
timeout:
|
|
381
|
+
timeout: 30,
|
|
382
382
|
failClosed: false,
|
|
383
383
|
matcher: "Shell|Bash|Read|ReadFile|Grep|Glob|terminal|run_terminal_cmd|execute_command|read_file|grep_search|file_search|list_dir|codebase_search|delete_file",
|
|
384
384
|
[SYNKRO_MARKER2]: true
|
|
385
385
|
});
|
|
386
386
|
pushCcHook(h, "preToolUse", config.editPrecheckScriptPath, {
|
|
387
|
-
timeout:
|
|
387
|
+
timeout: 30,
|
|
388
388
|
matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
|
|
389
389
|
});
|
|
390
390
|
pushCcHook(h, "preToolUse", config.cwePrecheckScriptPath, {
|
|
391
|
-
timeout:
|
|
391
|
+
timeout: 30,
|
|
392
392
|
matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
|
|
393
393
|
});
|
|
394
394
|
pushCcHook(h, "preToolUse", config.cvePrecheckScriptPath, {
|
|
395
|
-
timeout:
|
|
395
|
+
timeout: 10,
|
|
396
396
|
matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
|
|
397
397
|
});
|
|
398
398
|
pushCcHook(h, "preToolUse", config.agentJudgeScriptPath, {
|
|
399
|
-
timeout:
|
|
399
|
+
timeout: 30,
|
|
400
400
|
matcher: "Agent|Task"
|
|
401
401
|
});
|
|
402
402
|
pushCcHook(h, "preToolUse", config.planJudgeScriptPath, {
|
|
403
|
-
timeout:
|
|
403
|
+
timeout: 45,
|
|
404
404
|
matcher: "ExitPlanMode|SwitchMode|CreatePlan"
|
|
405
405
|
});
|
|
406
406
|
h.afterFileEdit = h.afterFileEdit ?? [];
|
|
@@ -3218,6 +3218,7 @@ export function isCursorInvokingCcHook(agentKind: string, model: string): boolea
|
|
|
3218
3218
|
}
|
|
3219
3219
|
|
|
3220
3220
|
let cursorHookExited = false;
|
|
3221
|
+
let hookFinalized = false;
|
|
3221
3222
|
|
|
3222
3223
|
export function setupCursorHookSignals(): void {
|
|
3223
3224
|
if (!isCursorHookFormat()) return;
|
|
@@ -3226,9 +3227,28 @@ export function setupCursorHookSignals(): void {
|
|
|
3226
3227
|
|
|
3227
3228
|
function cursorHookExit(): never {
|
|
3228
3229
|
cursorHookExited = true;
|
|
3230
|
+
hookFinalized = true;
|
|
3229
3231
|
process.exit(0);
|
|
3230
3232
|
}
|
|
3231
3233
|
|
|
3234
|
+
// Self-imposed deadline. CC/Cursor kill the hook process at the configured hook
|
|
3235
|
+
// timeout with a signal; if a grade fetch is still in flight when that happens,
|
|
3236
|
+
// the runtime dumps the in-flight request body (the grader prompt) to stderr and
|
|
3237
|
+
// exits non-zero \u2014 surfacing as a "hook error" toast with leaked prompt text.
|
|
3238
|
+
// To prevent that, each grading hook arms this watchdog a few seconds UNDER its
|
|
3239
|
+
// configured budget: when it fires we emit a clean empty result and exit 0
|
|
3240
|
+
// ourselves, so the harness never has to kill us. The timer is unref'd, so a
|
|
3241
|
+
// hook that finishes early exits normally and the watchdog never runs.
|
|
3242
|
+
export function installHookWatchdog(budgetMs: number): void {
|
|
3243
|
+
const t = setTimeout(() => {
|
|
3244
|
+
if (hookFinalized) return;
|
|
3245
|
+
hookFinalized = true;
|
|
3246
|
+
try { process.stdout.write('{}\\n'); } catch {}
|
|
3247
|
+
process.exit(0);
|
|
3248
|
+
}, budgetMs);
|
|
3249
|
+
if (typeof (t as any).unref === 'function') (t as any).unref();
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3232
3252
|
// \u2500\u2500\u2500 Grader-unavailable diagnostic log \u2500\u2500\u2500
|
|
3233
3253
|
// Records every time a hook tried to call the local grader and fell open
|
|
3234
3254
|
// because the call failed. JSONL at ~/.synkro/grader-unavailable.log so the
|
|
@@ -3308,6 +3328,8 @@ export function outputJson(obj: any): void {
|
|
|
3308
3328
|
outputEmpty();
|
|
3309
3329
|
return;
|
|
3310
3330
|
}
|
|
3331
|
+
if (hookFinalized) return;
|
|
3332
|
+
hookFinalized = true;
|
|
3311
3333
|
console.log(JSON.stringify(obj));
|
|
3312
3334
|
}
|
|
3313
3335
|
|
|
@@ -3319,6 +3341,8 @@ export function outputEmpty(): void {
|
|
|
3319
3341
|
}
|
|
3320
3342
|
cursorHookExit();
|
|
3321
3343
|
}
|
|
3344
|
+
if (hookFinalized) return;
|
|
3345
|
+
hookFinalized = true;
|
|
3322
3346
|
console.log('{}');
|
|
3323
3347
|
}
|
|
3324
3348
|
`;
|
|
@@ -3328,7 +3352,7 @@ import {
|
|
|
3328
3352
|
parseVerdict, dispatchCapture, ruleMode, reconstructContent, isPathUnder, postWithRetry,
|
|
3329
3353
|
readStdin, extractTranscript, readLastPrompt, findNearestDeps, filePathFromToolInput,
|
|
3330
3354
|
appendSessionAction, readSessionLog, compressSessionLog, log,
|
|
3331
|
-
outputJson, outputEmpty, setupCursorHookSignals, isEditTool, hookSessionId, GATEWAY_URL,
|
|
3355
|
+
outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isEditTool, hookSessionId, GATEWAY_URL,
|
|
3332
3356
|
logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, countEditLineDelta,
|
|
3333
3357
|
captureLineMetrics, cursorModelFromPayload, resolveTranscriptPath, isCursorInvokingCcHook,
|
|
3334
3358
|
loadSynkroFile, effectiveGraderPool,
|
|
@@ -3341,6 +3365,7 @@ const agentKind = (process.env.SYNKRO_HOOK_FORMAT === 'cursor' || process.argv.i
|
|
|
3341
3365
|
|
|
3342
3366
|
async function main() {
|
|
3343
3367
|
setupCursorHookSignals();
|
|
3368
|
+
installHookWatchdog(27000);
|
|
3344
3369
|
try {
|
|
3345
3370
|
const input = await readStdin();
|
|
3346
3371
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -3463,7 +3488,7 @@ async function main() {
|
|
|
3463
3488
|
'Last user prompt: ' + (lastPrompt || 'none'),
|
|
3464
3489
|
'Org rules: ' + JSON.stringify(relevantRules),
|
|
3465
3490
|
'IMPORTANT: If a rule is violated, ALWAYS return ok=false with the rule_id and reason, regardless of the rule mode. Do NOT pass a command just because the rule mode is "fix". The enforcement layer handles ask vs fix \u2014 your job is only to detect violations.',
|
|
3466
|
-
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass \u2014 but each distinct command is consumed once. Look for the sequence: block event \u2192 user acknowledgment \u2192 retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block \u2192 consent cycle). Example: R012 covers deploy, publish, push. Block on deploy \u2192 user consents \u2192 deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block.
|
|
3491
|
+
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass \u2014 but each distinct command is consumed once. Look for the sequence: block event \u2192 user acknowledgment \u2192 retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block \u2192 consent cycle). Example: R012 covers deploy, publish, push. Block on deploy \u2192 user consents \u2192 deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block. An initial user instruction is NEVER consent \u2014 only a response to a shown block counts.',
|
|
3467
3492
|
'The rules shown were pre-selected as the ones relevant to this edit \u2014 every rule here IS relevant, do not label any "not relevant". When passing (ok=true), give a terse, specific reason each rule passes. Format: "R003: no hardcoded secrets in file. R005: in-repo path only." Cover every rule shown.',
|
|
3468
3493
|
].join('\\n');
|
|
3469
3494
|
|
|
@@ -3585,7 +3610,7 @@ main();
|
|
|
3585
3610
|
import {
|
|
3586
3611
|
loadJwt, ensureFreshJwt, detectRepo, loadConfig, cweRoute, tag,
|
|
3587
3612
|
localGradeCwe, parseVerdict, reconstructContent, readStdin, log,
|
|
3588
|
-
outputJson, outputEmpty, setupCursorHookSignals, isEditTool, isShellTool, isCursorHookFormat,
|
|
3613
|
+
outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isEditTool, isShellTool, isCursorHookFormat,
|
|
3589
3614
|
extractShellCodeWrites, hookSessionId, filePathFromToolInput, emitBlockScanFindings, dispatchFinding, dispatchCapture, GATEWAY_URL,
|
|
3590
3615
|
logGraderUnavailable, graderUnavailableMessage, resolveTranscriptPath, isCursorInvokingCcHook,
|
|
3591
3616
|
loadSynkroFile, effectiveGraderPool,
|
|
@@ -3725,6 +3750,7 @@ function scanPackageCapabilities(pkgName: string, cwd: string): PackageCapabilit
|
|
|
3725
3750
|
|
|
3726
3751
|
async function main() {
|
|
3727
3752
|
setupCursorHookSignals();
|
|
3753
|
+
installHookWatchdog(27000);
|
|
3728
3754
|
try {
|
|
3729
3755
|
const input = await readStdin();
|
|
3730
3756
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -4548,7 +4574,7 @@ import {
|
|
|
4548
4574
|
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
|
|
4549
4575
|
parseVerdict, dispatchCapture, dispatchFinding, ruleMode, postWithRetry, readStdin,
|
|
4550
4576
|
extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, log,
|
|
4551
|
-
outputJson, outputEmpty, setupCursorHookSignals, isShellTool, hookSessionId, GATEWAY_URL,
|
|
4577
|
+
outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isShellTool, hookSessionId, GATEWAY_URL,
|
|
4552
4578
|
logGraderUnavailable, graderUnavailableMessage, filterRules, ruleFilterText, normalizeMode, appendLocalTelemetry, isSafeInRepoRead,
|
|
4553
4579
|
loadSynkroFile, effectiveGraderPool,
|
|
4554
4580
|
hashCommand, resolveTranscriptPath, isCursorHookFormat,
|
|
@@ -4591,6 +4617,7 @@ function isDuplicate(command: string, sessionId: string): boolean {
|
|
|
4591
4617
|
|
|
4592
4618
|
async function main() {
|
|
4593
4619
|
setupCursorHookSignals();
|
|
4620
|
+
installHookWatchdog(27000);
|
|
4594
4621
|
try {
|
|
4595
4622
|
const input = await readStdin();
|
|
4596
4623
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -4722,7 +4749,7 @@ async function main() {
|
|
|
4722
4749
|
'Org rules: ' + JSON.stringify(relevantRules),
|
|
4723
4750
|
scanConcern,
|
|
4724
4751
|
'IMPORTANT: If a rule is violated, ALWAYS return ok=false with the rule_id and reason, regardless of the rule mode. Do NOT pass a command just because the rule mode is "fix". The enforcement layer handles ask vs fix — your job is only to detect violations.',
|
|
4725
|
-
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass — but each distinct command is consumed once. Look for the sequence: block event → user acknowledgment → retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block → consent cycle). Example: R012 covers deploy, publish, push. Block on deploy → user consents → deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block.
|
|
4752
|
+
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass — but each distinct command is consumed once. Look for the sequence: block event → user acknowledgment → retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block → consent cycle). Example: R012 covers deploy, publish, push. Block on deploy → user consents → deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block. An initial user instruction is NEVER consent — only a response to a shown block counts.',
|
|
4726
4753
|
'The rules shown were pre-selected as the ones relevant to this command — every rule here IS relevant, do not label any "not relevant". When passing (ok=true), give a terse, specific reason each rule passes. Format: "R003: no secrets in grep args. R005: in-repo path only." Cover every rule shown.',
|
|
4727
4754
|
'Rules with preconditions (e.g. "run X before Y") are CONSUMED after the protected action completes. Use the session history timestamps to determine ordering: a precondition satisfied before the last occurrence of the protected action does NOT satisfy the next occurrence. Each new protected action needs its precondition re-satisfied.',
|
|
4728
4755
|
].filter(Boolean).join('\\n');
|
|
@@ -4845,7 +4872,7 @@ import {
|
|
|
4845
4872
|
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
|
|
4846
4873
|
parseVerdict, dispatchCapture, ruleMode, postWithRetry, readStdin,
|
|
4847
4874
|
extractTranscript, readLastPrompt, appendSessionAction, readSessionLog, compressSessionLog, log,
|
|
4848
|
-
outputJson, outputEmpty, setupCursorHookSignals, isAgentTool, hookSessionId, GATEWAY_URL,
|
|
4875
|
+
outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isAgentTool, hookSessionId, GATEWAY_URL,
|
|
4849
4876
|
logGraderUnavailable, graderUnavailableMessage, filterRules, normalizeMode, resolveTranscriptPath, isCursorInvokingCcHook,
|
|
4850
4877
|
loadSynkroFile, effectiveGraderPool,
|
|
4851
4878
|
type HookConfig, type Rule,
|
|
@@ -4855,6 +4882,7 @@ const agentKind = (process.env.SYNKRO_HOOK_FORMAT === 'cursor' || process.argv.i
|
|
|
4855
4882
|
|
|
4856
4883
|
async function main() {
|
|
4857
4884
|
setupCursorHookSignals();
|
|
4885
|
+
installHookWatchdog(27000);
|
|
4858
4886
|
try {
|
|
4859
4887
|
const input = await readStdin();
|
|
4860
4888
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -4934,7 +4962,7 @@ async function main() {
|
|
|
4934
4962
|
'Last user prompt: ' + (lastPrompt || 'none'),
|
|
4935
4963
|
'Org rules: ' + JSON.stringify(relevantRules),
|
|
4936
4964
|
'IMPORTANT: If a rule is violated, ALWAYS return ok=false with the rule_id and reason, regardless of the rule mode. Do NOT pass a command just because the rule mode is "fix". The enforcement layer handles ask vs fix \u2014 your job is only to detect violations.',
|
|
4937
|
-
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass \u2014 but each distinct command is consumed once. Look for the sequence: block event \u2192 user acknowledgment \u2192 retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block \u2192 consent cycle). Example: R012 covers deploy, publish, push. Block on deploy \u2192 user consents \u2192 deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block.
|
|
4965
|
+
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass \u2014 but each distinct command is consumed once. Look for the sequence: block event \u2192 user acknowledgment \u2192 retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block \u2192 consent cycle). Example: R012 covers deploy, publish, push. Block on deploy \u2192 user consents \u2192 deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block. An initial user instruction is NEVER consent \u2014 only a response to a shown block counts.',
|
|
4938
4966
|
].filter(Boolean).join('\\n');
|
|
4939
4967
|
|
|
4940
4968
|
let gradeResp: string;
|
|
@@ -5032,7 +5060,7 @@ main();
|
|
|
5032
5060
|
import {
|
|
5033
5061
|
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
|
|
5034
5062
|
parseVerdict, dispatchCapture, appendSessionAction, readSessionLog, compressSessionLog, postWithRetry, readStdin, log,
|
|
5035
|
-
outputJson, outputEmpty, setupCursorHookSignals, isPlanTool, hookSessionId, GATEWAY_URL,
|
|
5063
|
+
outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isPlanTool, hookSessionId, GATEWAY_URL,
|
|
5036
5064
|
filterRules, graderUnavailableMessage, resolveTranscriptPath, isCursorInvokingCcHook,
|
|
5037
5065
|
loadSynkroFile, effectiveGraderPool,
|
|
5038
5066
|
} from './_synkro-common.ts';
|
|
@@ -5084,6 +5112,7 @@ function appendReviewToPlan(planFile: string, verdict: string): void {
|
|
|
5084
5112
|
|
|
5085
5113
|
async function main() {
|
|
5086
5114
|
setupCursorHookSignals();
|
|
5115
|
+
installHookWatchdog(42000);
|
|
5087
5116
|
try {
|
|
5088
5117
|
const input = await readStdin();
|
|
5089
5118
|
if (!input.trim()) { outputEmpty(); return; }
|
|
@@ -5590,8 +5619,10 @@ function isDuplicate(command: string, sessionId: string): boolean {
|
|
|
5590
5619
|
return false;
|
|
5591
5620
|
}
|
|
5592
5621
|
|
|
5593
|
-
// Cursor
|
|
5594
|
-
|
|
5622
|
+
// Cursor hook timeouts now match CC (30s for bash/edit/agent), so use the same
|
|
5623
|
+
// 24s internal grade budget as the CC path \u2014 stays under the 30s hook timeout
|
|
5624
|
+
// (JWT refresh + grade) and fails open cleanly via the caller's catch.
|
|
5625
|
+
const CURSOR_GRADE_TIMEOUT_MS = 24000;
|
|
5595
5626
|
const CURSOR_CLOUD_TIMEOUT_MS = 9000;
|
|
5596
5627
|
|
|
5597
5628
|
let hookDone = false;
|
|
@@ -5733,7 +5764,7 @@ async function main() {
|
|
|
5733
5764
|
'Org rules: ' + JSON.stringify(relevantRules),
|
|
5734
5765
|
scanConcern,
|
|
5735
5766
|
'IMPORTANT: If a rule is violated, ALWAYS return ok=false with the rule_id and reason, regardless of the rule mode. Do NOT pass a command just because the rule mode is "fix". The enforcement layer handles ask vs fix \u2014 your job is only to detect violations.',
|
|
5736
|
-
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass \u2014 but each distinct command is consumed once. Look for the sequence: block event \u2192 user acknowledgment \u2192 retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block \u2192 consent cycle). Example: R012 covers deploy, publish, push. Block on deploy \u2192 user consents \u2192 deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block.
|
|
5767
|
+
'CRITICAL: The user requesting or instructing an action does NOT exempt it from rules. Even if the user explicitly said "drop the database" or "delete everything", you MUST still flag the rule violation on first encounter. User intent is NOT consent. However, for ask-mode rules ONLY: if the session history shows a prior block for the SAME rule AND the user explicitly consented after seeing that block, subsequent commands covered by that same rule may pass \u2014 but each distinct command is consumed once. Look for the sequence: block event \u2192 user acknowledgment \u2192 retry. Once a specific command has successfully executed under that consent, it is consumed. If the same command appears again later, it requires fresh consent (a new block \u2192 consent cycle). Example: R012 covers deploy, publish, push. Block on deploy \u2192 user consents \u2192 deploy passes (consumed), publish passes (consumed), push passes (consumed). A later deploy triggers a fresh block. An initial user instruction is NEVER consent \u2014 only a response to a shown block counts.',
|
|
5737
5768
|
'The rules shown were pre-selected as the ones relevant to this command \u2014 every rule here IS relevant, do not label any "not relevant". When passing (ok=true), give a terse, specific reason each rule passes. Format: "R003: no secrets in grep args. R005: in-repo path only." Cover every rule shown.',
|
|
5738
5769
|
'Rules with preconditions (e.g. "run X before Y") are CONSUMED after the protected action completes. Use the session history timestamps to determine ordering: a precondition satisfied before the last occurrence of the protected action does NOT satisfy the next occurrence. Each new protected action needs its precondition re-satisfied.',
|
|
5739
5770
|
].filter(Boolean).join('\\n');
|
|
@@ -8270,7 +8301,7 @@ function writeConfigEnv(opts) {
|
|
|
8270
8301
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
8271
8302
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
8272
8303
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
8273
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
8304
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.45")}`
|
|
8274
8305
|
];
|
|
8275
8306
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
8276
8307
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -11355,7 +11386,7 @@ var args = process.argv.slice(2);
|
|
|
11355
11386
|
var cmd = args[0] || "";
|
|
11356
11387
|
var subArgs = args.slice(1);
|
|
11357
11388
|
function printVersion() {
|
|
11358
|
-
console.log("1.6.
|
|
11389
|
+
console.log("1.6.45");
|
|
11359
11390
|
}
|
|
11360
11391
|
function printHelp2() {
|
|
11361
11392
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|