@synkro-sh/cli 1.7.37 → 1.7.39

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 CHANGED
@@ -146,7 +146,15 @@ function installCCHooks(settingsPath, config) {
146
146
  type: "command",
147
147
  command: config.bashJudgeScriptPath,
148
148
  timeout: 50
149
- }
149
+ },
150
+ // skill-judge shares this entry so CC runs it in parallel and waits for it
151
+ // before processing denies — its verdict is its OWN system-message block,
152
+ // separate from bashGuard. No-ops on pure reads (empty command).
153
+ ...config.skillJudgeScriptPath ? [{
154
+ type: "command",
155
+ command: config.skillJudgeScriptPath,
156
+ timeout: 50
157
+ }] : []
150
158
  ],
151
159
  [SYNKRO_MARKER]: true
152
160
  });
@@ -167,7 +175,13 @@ function installCCHooks(settingsPath, config) {
167
175
  type: "command",
168
176
  command: config.cvePrecheckScriptPath,
169
177
  timeout: 10
170
- }
178
+ },
179
+ // skill-judge — its OWN system-message block, parallel with edit-precheck.
180
+ ...config.skillJudgeScriptPath ? [{
181
+ type: "command",
182
+ command: config.skillJudgeScriptPath,
183
+ timeout: 50
184
+ }] : []
171
185
  ],
172
186
  [SYNKRO_MARKER]: true
173
187
  });
@@ -395,6 +409,12 @@ function installCursorHooks(hooksJsonPath, config) {
395
409
  failClosed: false,
396
410
  [SYNKRO_MARKER2]: true
397
411
  });
412
+ if (config.skillJudgeScriptPath) h.beforeShellExecution.push({
413
+ command: bunRunCmd(config.skillJudgeScriptPath),
414
+ timeout: 30,
415
+ failClosed: false,
416
+ [SYNKRO_MARKER2]: true
417
+ });
398
418
  pushCcHook(h, "afterShellExecution", config.bashFollowupScriptPath, { timeout: 10 });
399
419
  h.preToolUse = h.preToolUse ?? [];
400
420
  h.preToolUse.push({
@@ -418,10 +438,21 @@ function installCursorHooks(hooksJsonPath, config) {
418
438
  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",
419
439
  [SYNKRO_MARKER2]: true
420
440
  });
441
+ if (config.skillJudgeScriptPath) h.preToolUse.push({
442
+ command: bunRunCmd(config.skillJudgeScriptPath),
443
+ timeout: 30,
444
+ failClosed: false,
445
+ matcher: "Shell|Bash|terminal|run_terminal_cmd|execute_command",
446
+ [SYNKRO_MARKER2]: true
447
+ });
421
448
  pushCcHook(h, "preToolUse", config.editPrecheckScriptPath, {
422
449
  timeout: 30,
423
450
  matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
424
451
  });
452
+ if (config.skillJudgeScriptPath) pushCcHook(h, "preToolUse", config.skillJudgeScriptPath, {
453
+ timeout: 30,
454
+ matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
455
+ });
425
456
  pushCcHook(h, "preToolUse", config.cwePrecheckScriptPath, {
426
457
  timeout: 30,
427
458
  matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
@@ -738,7 +769,7 @@ var init_skillParser = __esm({
738
769
  function stubHook(surface, optsLiteral) {
739
770
  return "#!/usr/bin/env bun\nimport { runStub } from './_synkro-stub-common.ts';\nrunStub(" + JSON.stringify(surface) + ", " + optsLiteral + ");\n";
740
771
  }
741
- var STUB_COMMON_TS, STUB_EDIT_PRECHECK_TS, STUB_CWE_PRECHECK_TS, STUB_CVE_PRECHECK_TS, STUB_BASH_JUDGE_TS, STUB_INSTALL_SCAN_TS, STUB_AGENT_JUDGE_TS, STUB_MCP_GATE_TS, STUB_PLAN_JUDGE_TS, STUB_STOP_SUMMARY_TS, STUB_SESSION_START_TS, STUB_TRANSCRIPT_SYNC_TS, STUB_USER_PROMPT_SUBMIT_TS, STUB_BASH_FOLLOWUP_TS, STUB_TASK_ACTIVATE_INTENT_TS, STUB_CURSOR_BASH_JUDGE_TS, STUB_CURSOR_EDIT_CAPTURE_TS, STUB_CURSOR_AGENT_CAPTURE_TS;
772
+ var STUB_COMMON_TS, STUB_EDIT_PRECHECK_TS, STUB_CWE_PRECHECK_TS, STUB_CVE_PRECHECK_TS, STUB_BASH_JUDGE_TS, STUB_SKILL_JUDGE_TS, STUB_INSTALL_SCAN_TS, STUB_AGENT_JUDGE_TS, STUB_MCP_GATE_TS, STUB_PLAN_JUDGE_TS, STUB_STOP_SUMMARY_TS, STUB_SESSION_START_TS, STUB_TRANSCRIPT_SYNC_TS, STUB_USER_PROMPT_SUBMIT_TS, STUB_BASH_FOLLOWUP_TS, STUB_TASK_ACTIVATE_INTENT_TS, STUB_CURSOR_BASH_JUDGE_TS, STUB_CURSOR_SKILL_JUDGE_TS, STUB_CURSOR_EDIT_CAPTURE_TS, STUB_CURSOR_AGENT_CAPTURE_TS;
742
773
  var init_hookScriptsTs = __esm({
743
774
  "cli/installer/hookScriptsTs.ts"() {
744
775
  "use strict";
@@ -839,34 +870,6 @@ function filePathFromToolInput(ti: any): string {
839
870
  return ti.file_path || ti.notebook_path || ti.path || ti.target_file || '';
840
871
  }
841
872
 
842
- // Latest real human message from a CC transcript (JSONL). Scans from the end for a
843
- // user turn, skipping sidechain (sub-agent) turns, meta entries, tool-result-only
844
- // turns, and the synthetic local-command/caveat wrappers. Returns '' if none found.
845
- function latestUserText(transcript: string): string {
846
- const lines = transcript.split('\n');
847
- for (let i = lines.length - 1; i >= 0; i--) {
848
- const ln = lines[i].trim();
849
- if (!ln) continue;
850
- let o: any;
851
- try { o = JSON.parse(ln); } catch { continue; }
852
- if (o && (o.isSidechain || o.isMeta)) continue;
853
- const m = o && o.message;
854
- const role = (m && m.role) || (o && o.type);
855
- if (role !== 'user') continue;
856
- const c = m && m.content;
857
- let text = '';
858
- if (typeof c === 'string') text = c;
859
- else if (Array.isArray(c)) {
860
- text = c.filter((b: any) => b && b.type === 'text' && typeof b.text === 'string').map((b: any) => b.text).join(' ');
861
- }
862
- text = text.trim();
863
- if (!text) continue;
864
- if (text.startsWith('<local-command') || text.startsWith('<command-') || text.startsWith('Caveat:') || text.startsWith('<system-reminder')) continue;
865
- return text;
866
- }
867
- return '';
868
- }
869
-
870
873
  interface StubOpts {
871
874
  needsFile?: boolean;
872
875
  needsTranscript?: boolean;
@@ -914,29 +917,6 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
914
917
  // Gather host-only inputs the container can't read.
915
918
  const envelope: any = { payload, harness, cwd: root || cwd, sessionId, synkroFileText };
916
919
 
917
- // Sub-agent attribution (host-side — the container has no access to ~/.claude).
918
- // A spawned sub-agent's tool-call hooks carry the ROOT session_id but a sidechain
919
- // transcript at agent-<id>.jsonl (vs the main <session_id>.jsonl). Detect it from
920
- // transcript_path, and read the ROOT transcript for the human turn that dispatched
921
- // this (the sub-agent's own transcript starts with the task prompt, not the human).
922
- try {
923
- const tpath = typeof payload.transcript_path === 'string' ? payload.transcript_path : '';
924
- const tbase = tpath.split('/').pop() || '';
925
- const am = tbase.match(/^agent-(.+)\.jsonl$/);
926
- if (am) {
927
- envelope.isSubagent = true;
928
- envelope.agentId = am[1];
929
- try {
930
- const dir = tpath.slice(0, tpath.length - tbase.length);
931
- const rootPath = dir + sessionId + '.jsonl';
932
- if (rootPath !== tpath && existsSync(rootPath)) {
933
- const msg = latestUserText(readFileSync(rootPath, 'utf-8'));
934
- if (msg) envelope.triggeringMessage = msg.slice(0, 2000);
935
- }
936
- } catch {}
937
- }
938
- } catch {}
939
-
940
920
  if (opts.needsFile) {
941
921
  const fp = filePathFromToolInput(payload.tool_input || {});
942
922
  if (fp && existsSync(fp)) {
@@ -961,7 +941,12 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
961
941
  // result (the server waits up to 30s for the completion grade), so it needs a
962
942
  // wait above that budget — not the 6s telemetry default — or the stub abandons
963
943
  // the request before the result lands and the completion shows up a hook late.
964
- const timeoutMs = surface === 'bash-followup' ? 32000 : (opts.telemetry ? 6000 : 28000);
944
+ // prompt-submit is the opposite: it's INTERACTIVE (blocks the user's prompt from
945
+ // being sent) under a short 5s hook timeout, so it gets ONE short attempt (below)
946
+ // and a tight per-attempt budget that fits inside 5s — never the 6s×3 telemetry path.
947
+ const timeoutMs = surface === 'bash-followup' ? 32000
948
+ : surface === 'prompt-submit' ? 3500
949
+ : (opts.telemetry ? 6000 : 28000);
965
950
  // Cloud has no local container to reach. Post the SAME envelope to the org's grader
966
951
  // via the gateway's /grade/submit as role 'scan:<surface>'; the container runs
967
952
  // scanRouter (runScan) server-side and returns the SAME shaped decision — so cloud
@@ -987,7 +972,11 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
987
972
  // — the server side is idempotent-ish (completion in-flight guard, dup evidence
988
973
  // is harmless). Blocking hooks make ONE attempt: their response IS the verdict
989
974
  // and a retry would double-grade. Normal case: first attempt wins, no delay.
990
- const attempts = opts.telemetry ? 3 : 1;
975
+ // prompt-submit is EXCLUDED from the retry: it runs on the interactive prompt
976
+ // path under a 5s hook timeout, and 3×6s+backoff (19.5s) blew that budget and
977
+ // fired "hook timed out after 5s" on every prompt whenever the server was slow.
978
+ // Dropping an occasional prompt tick is harmless; stalling the user is not.
979
+ const attempts = (opts.telemetry && surface !== 'prompt-submit') ? 3 : 1;
991
980
  let text = '';
992
981
  for (let i = 0; i < attempts; i++) {
993
982
  try {
@@ -1006,6 +995,7 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
1006
995
  STUB_CWE_PRECHECK_TS = stubHook("cwe-precheck", "{ needsFile: true, needsTranscript: true }");
1007
996
  STUB_CVE_PRECHECK_TS = stubHook("cve-precheck", "{ needsFile: true, needsTranscript: true }");
1008
997
  STUB_BASH_JUDGE_TS = stubHook("bash-judge", "{ needsTranscript: true }");
998
+ STUB_SKILL_JUDGE_TS = stubHook("skill-judge", "{ needsFile: true, needsTranscript: true }");
1009
999
  STUB_INSTALL_SCAN_TS = stubHook("install-scan", "{ needsTranscript: true }");
1010
1000
  STUB_AGENT_JUDGE_TS = stubHook("agent-judge", "{ needsTranscript: true }");
1011
1001
  STUB_MCP_GATE_TS = stubHook("mcp-gate", "{ needsTranscript: true }");
@@ -1035,6 +1025,7 @@ try {
1035
1025
  process.stdout.write('{}\\n');
1036
1026
  `;
1037
1027
  STUB_CURSOR_BASH_JUDGE_TS = stubHook("bash-judge", "{ needsTranscript: true, harness: 'cursor' }");
1028
+ STUB_CURSOR_SKILL_JUDGE_TS = stubHook("skill-judge", "{ needsFile: true, needsTranscript: true, harness: 'cursor' }");
1038
1029
  STUB_CURSOR_EDIT_CAPTURE_TS = stubHook("edit-precheck", "{ needsFile: true, needsTranscript: true, harness: 'cursor' }");
1039
1030
  STUB_CURSOR_AGENT_CAPTURE_TS = stubHook("agent-judge", "{ needsTranscript: true, harness: 'cursor' }");
1040
1031
  }
@@ -3815,6 +3806,10 @@ async function dockerInstall(opts = {}) {
3815
3806
  // interactive AskUserQuestion poll and fall through to generate-the-fix. Only
3816
3807
  // passed when the operator set it; otherwise the image default (1) applies.
3817
3808
  ...process.env.SYNKRO_FIX_POLL ? ["-e", `SYNKRO_FIX_POLL=${process.env.SYNKRO_FIX_POLL}`] : [],
3809
+ // Route CWE grades to the cloud /api/v1/cwe-scan endpoint (arm-C: full-catalog
3810
+ // retrieval + ensemble) instead of the local single-prompt path. Grading stays
3811
+ // local for every other surface. Benchmark lever; default off.
3812
+ ...process.env.SYNKRO_CWE_VIA_ENDPOINT ? ["-e", `SYNKRO_CWE_VIA_ENDPOINT=${process.env.SYNKRO_CWE_VIA_ENDPOINT}`] : [],
3818
3813
  // Connected repo — the server seeds the local app + ruleset named after it.
3819
3814
  ...opts.connectedRepo ? ["-e", `SYNKRO_CONNECTED_REPO=${opts.connectedRepo}`] : [],
3820
3815
  // Real account identity (from config.env via process.env) — telemetry is
@@ -4728,6 +4723,8 @@ function ensureSynkroDir() {
4728
4723
  function writeHookScripts() {
4729
4724
  const installExtractCorePath = join11(HOOKS_DIR, "installExtractCore.ts");
4730
4725
  const bashScriptPath = join11(HOOKS_DIR, "cc-bash-judge.ts");
4726
+ const skillJudgeScriptPath = join11(HOOKS_DIR, "cc-skill-judge.ts");
4727
+ const cursorSkillJudgePath = join11(HOOKS_DIR, "cursor-skill-judge.ts");
4731
4728
  const bashFollowupScriptPath = join11(HOOKS_DIR, "cc-bash-followup.ts");
4732
4729
  const editPrecheckScriptPath = join11(HOOKS_DIR, "cc-edit-precheck.ts");
4733
4730
  const cwePrecheckScriptPath = join11(HOOKS_DIR, "cc-cwe-precheck.ts");
@@ -4751,6 +4748,8 @@ function writeHookScripts() {
4751
4748
  const stubFiles = [
4752
4749
  [stubCommonPath, STUB_COMMON_TS],
4753
4750
  [bashScriptPath, STUB_BASH_JUDGE_TS],
4751
+ [skillJudgeScriptPath, STUB_SKILL_JUDGE_TS],
4752
+ [cursorSkillJudgePath, STUB_CURSOR_SKILL_JUDGE_TS],
4754
4753
  [bashFollowupScriptPath, STUB_BASH_FOLLOWUP_TS],
4755
4754
  [editPrecheckScriptPath, STUB_EDIT_PRECHECK_TS],
4756
4755
  [cwePrecheckScriptPath, STUB_CWE_PRECHECK_TS],
@@ -4782,6 +4781,8 @@ function writeHookScripts() {
4782
4781
  }
4783
4782
  return {
4784
4783
  bashScript: bashScriptPath,
4784
+ skillJudgeScript: skillJudgeScriptPath,
4785
+ cursorSkillJudgeScript: cursorSkillJudgePath,
4785
4786
  bashFollowupScript: bashFollowupScriptPath,
4786
4787
  editPrecheckScript: editPrecheckScriptPath,
4787
4788
  cwePrecheckScript: cwePrecheckScriptPath,
@@ -4829,7 +4830,7 @@ function writeConfigEnv(opts) {
4829
4830
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
4830
4831
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
4831
4832
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
4832
- `SYNKRO_VERSION=${shellQuoteSingle("1.7.37")}`
4833
+ `SYNKRO_VERSION=${shellQuoteSingle("1.7.39")}`
4833
4834
  ];
4834
4835
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
4835
4836
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -5472,6 +5473,7 @@ async function installCommand(opts = {}) {
5472
5473
  hasClaudeCode = true;
5473
5474
  installCCHooks(agent.settingsPath, {
5474
5475
  bashJudgeScriptPath: scripts.bashScript,
5476
+ skillJudgeScriptPath: scripts.skillJudgeScript,
5475
5477
  bashFollowupScriptPath: scripts.bashFollowupScript,
5476
5478
  editPrecheckScriptPath: scripts.editPrecheckScript,
5477
5479
  cwePrecheckScriptPath: scripts.cwePrecheckScript,
@@ -5492,6 +5494,7 @@ async function installCommand(opts = {}) {
5492
5494
  hasCursor = true;
5493
5495
  installCursorHooks(agent.settingsPath, {
5494
5496
  bashJudgeScriptPath: scripts.cursorBashJudgeScript,
5497
+ skillJudgeScriptPath: scripts.cursorSkillJudgeScript,
5495
5498
  editCaptureScriptPath: scripts.cursorEditCaptureScript,
5496
5499
  agentCaptureScriptPath: scripts.cursorAgentCaptureScript,
5497
5500
  bashFollowupScriptPath: scripts.bashFollowupScript,
@@ -5970,6 +5973,7 @@ function reconcileHarness() {
5970
5973
  if (wantCC) {
5971
5974
  installCCHooks(ccSettings, {
5972
5975
  bashJudgeScriptPath: scripts.bashScript,
5976
+ skillJudgeScriptPath: scripts.skillJudgeScript,
5973
5977
  bashFollowupScriptPath: scripts.bashFollowupScript,
5974
5978
  editPrecheckScriptPath: scripts.editPrecheckScript,
5975
5979
  cwePrecheckScriptPath: scripts.cwePrecheckScript,
@@ -6002,6 +6006,7 @@ function reconcileHarness() {
6002
6006
  if (wantCursor) {
6003
6007
  installCursorHooks(cursorHooks, {
6004
6008
  bashJudgeScriptPath: scripts.cursorBashJudgeScript,
6009
+ skillJudgeScriptPath: scripts.cursorSkillJudgeScript,
6005
6010
  editCaptureScriptPath: scripts.cursorEditCaptureScript,
6006
6011
  agentCaptureScriptPath: scripts.cursorAgentCaptureScript,
6007
6012
  bashFollowupScriptPath: scripts.bashFollowupScript,
@@ -9027,7 +9032,7 @@ var args = process.argv.slice(2);
9027
9032
  var cmd = args[0] || "";
9028
9033
  var subArgs = args.slice(1);
9029
9034
  function printVersion() {
9030
- console.log("1.7.37");
9035
+ console.log("1.7.39");
9031
9036
  }
9032
9037
  function printHelp2() {
9033
9038
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents