@andyqiu/codeforge 0.5.10 → 0.5.11

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.
Files changed (2) hide show
  1. package/dist/index.js +100 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13262,12 +13262,25 @@ async function mergeSessionBack(opts) {
13262
13262
  }
13263
13263
  const hasDevOnce = await packageHasScript(mainRoot, "dev:once");
13264
13264
  if (hasDevOnce) {
13265
- try {
13266
- await runCmd("npm", ["run", "dev:once"], mainRoot);
13267
- } catch (err) {
13268
- await runGit2(mainRoot, ["reset", "--hard", "HEAD"]).catch(() => {});
13269
- const msg = err instanceof Error ? err.message : String(err);
13270
- throw new Error(`dev:once 失败已 reset 主仓: ${msg}`);
13265
+ const stagedRaw = await runGit2(mainRoot, [
13266
+ "diff",
13267
+ "--cached",
13268
+ "--name-only",
13269
+ "--diff-filter=ACMR"
13270
+ ]);
13271
+ const stagedPaths = stagedRaw.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
13272
+ const canSkipDevOnce = await shouldSkipDevOnce(mainRoot, stagedPaths);
13273
+ if (canSkipDevOnce) {
13274
+ const sourceCount = stagedPaths.filter((p) => /^(plugins|lib|src)\//.test(p) && !/\.(md|test\.ts)$/.test(p)).length;
13275
+ console.log(`[session-worktree] skip dev:once: dist 已是最新(${sourceCount} staged 源文件 mtime <= dist mtime)`);
13276
+ } else {
13277
+ try {
13278
+ await runCmd("npm", ["run", "dev:once"], mainRoot);
13279
+ } catch (err) {
13280
+ await runGit2(mainRoot, ["reset", "--hard", "HEAD"]).catch(() => {});
13281
+ const msg = err instanceof Error ? err.message : String(err);
13282
+ throw new Error(`dev:once 失败已 reset 主仓: ${msg}`);
13283
+ }
13271
13284
  }
13272
13285
  } else {
13273
13286
  console.log(`[session-worktree] skip dev:once: not configured in ${mainRoot}/package.json`);
@@ -13275,7 +13288,9 @@ async function mergeSessionBack(opts) {
13275
13288
  const squashedRaw = await runGit2(wt, ["log", "--format=%s", `${baseSha}..HEAD`]);
13276
13289
  const squashedCommits = squashedRaw.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
13277
13290
  const message = opts.commitMessage ?? buildMergeMessage(opts.sessionId, branch, baseSha, squashedCommits);
13278
- await runGit2(mainRoot, ["commit", "-m", message]);
13291
+ await runGitWithEnv(mainRoot, ["commit", "-m", message], {
13292
+ SKIP_DEV_SYNC_CHECK: "1"
13293
+ });
13279
13294
  const newSha = (await runGit2(mainRoot, ["rev-parse", "HEAD"])).trim();
13280
13295
  try {
13281
13296
  await removeWorktree({ root: mainRoot, worktree_path: wt, force: true });
@@ -13376,6 +13391,24 @@ function runGit2(cwd, args, timeoutMs = 1e4) {
13376
13391
  });
13377
13392
  });
13378
13393
  }
13394
+ function runGitWithEnv(cwd, args, envOverrides, timeoutMs = 1e4) {
13395
+ const inheritedEnv = process["env"];
13396
+ return new Promise((resolve11, reject) => {
13397
+ execFile3("git", args, {
13398
+ cwd,
13399
+ timeout: timeoutMs,
13400
+ windowsHide: true,
13401
+ encoding: "utf8",
13402
+ env: Object.assign({}, inheritedEnv, envOverrides)
13403
+ }, (err, stdout, stderr) => {
13404
+ if (err) {
13405
+ reject(new Error(`git ${args.join(" ")} (cwd=${cwd}) 失败: ${stderr?.trim() || err.message}`));
13406
+ return;
13407
+ }
13408
+ resolve11(stdout);
13409
+ });
13410
+ });
13411
+ }
13379
13412
  async function packageHasScript(mainRoot, scriptName) {
13380
13413
  try {
13381
13414
  const pkgPath = path13.join(mainRoot, "package.json");
@@ -13388,7 +13421,31 @@ async function packageHasScript(mainRoot, scriptName) {
13388
13421
  return false;
13389
13422
  }
13390
13423
  }
13391
- function runCmd(cmd, args, cwd, timeoutMs = 120000) {
13424
+ async function shouldSkipDevOnce(mainRoot, stagedPaths) {
13425
+ let distMtimeSec;
13426
+ try {
13427
+ const st = await fs10.stat(path13.join(mainRoot, "dist/index.js"));
13428
+ distMtimeSec = Math.floor(st.mtimeMs / 1000);
13429
+ } catch {
13430
+ return false;
13431
+ }
13432
+ const relevant = stagedPaths.filter((p) => /^(plugins|lib|src)\//.test(p) && !/\.(md|test\.ts)$/.test(p));
13433
+ if (relevant.length === 0)
13434
+ return true;
13435
+ for (const rel of relevant) {
13436
+ try {
13437
+ const abs = path13.join(mainRoot, rel);
13438
+ const st = await fs10.stat(abs);
13439
+ const srcMtimeSec = Math.floor(st.mtimeMs / 1000);
13440
+ if (srcMtimeSec > distMtimeSec)
13441
+ return false;
13442
+ } catch {
13443
+ return false;
13444
+ }
13445
+ }
13446
+ return true;
13447
+ }
13448
+ function runCmd(cmd, args, cwd, timeoutMs = 300000) {
13392
13449
  return new Promise((resolve11, reject) => {
13393
13450
  execFile3(cmd, args, { cwd, timeout: timeoutMs, windowsHide: true, encoding: "utf8" }, (err, stdout, stderr) => {
13394
13451
  if (err) {
@@ -21232,7 +21289,11 @@ var RISK_PATTERNS = [
21232
21289
  kinds: ["bash", "other"],
21233
21290
  re: /\b(DROP\s+(DATABASE|TABLE)|TRUNCATE\s+TABLE|DROP\s+SCHEMA)\b/i
21234
21291
  },
21235
- { tag: "write_secrets", re: /(\.env(?:\.\w+)?|id_[edr]sa|\.ssh\/id_|\.pem|\.p12|secret\.json)/i },
21292
+ {
21293
+ tag: "write_secrets",
21294
+ re: /(\.env(?:\.\w+)?|id_[edr]sa|\.ssh\/id_|\.pem|\.p12|secret\.json)/i,
21295
+ matchOn: ["command", "filePath", "path"]
21296
+ },
21236
21297
  {
21237
21298
  tag: "write_etc",
21238
21299
  kinds: ["bash", "edit"],
@@ -21313,11 +21374,11 @@ function classifyTool(tool2) {
21313
21374
  }
21314
21375
  function evaluateRisk(tool2, args) {
21315
21376
  const kind = classifyTool(tool2);
21316
- const haystack = buildHaystack(args);
21317
21377
  const hits = [];
21318
21378
  for (const pattern of RISK_PATTERNS) {
21319
21379
  if (pattern.kinds && !pattern.kinds.includes(kind))
21320
21380
  continue;
21381
+ const haystack = buildHaystackFor(args, pattern.matchOn);
21321
21382
  const m = haystack.match(pattern.re);
21322
21383
  if (m) {
21323
21384
  hits.push({
@@ -21338,6 +21399,19 @@ function buildHaystack(args) {
21338
21399
  return String(args);
21339
21400
  }
21340
21401
  }
21402
+ function buildHaystackFor(args, matchOn) {
21403
+ if (!matchOn || matchOn.length === 0) {
21404
+ return buildHaystack(args);
21405
+ }
21406
+ const parts = [];
21407
+ for (const key of matchOn) {
21408
+ if (key in args) {
21409
+ const val = args[key];
21410
+ parts.push(typeof val === "string" ? val : JSON.stringify(val));
21411
+ }
21412
+ }
21413
+ return parts.join(" ");
21414
+ }
21341
21415
 
21342
21416
  // lib/file-regex-acl.ts
21343
21417
  import * as path23 from "node:path";
@@ -21636,7 +21710,7 @@ import * as zlib from "node:zlib";
21636
21710
  // lib/version-injected.ts
21637
21711
  function getInjectedVersion() {
21638
21712
  try {
21639
- const v = "0.5.10";
21713
+ const v = "0.5.11";
21640
21714
  if (typeof v === "string" && /^\d+\.\d+\.\d+/.test(v)) {
21641
21715
  return v;
21642
21716
  }
@@ -22970,6 +23044,20 @@ function commandContainsMainRoot(command, mainRoot) {
22970
23044
  const re = new RegExp(`${escapeRegex2(mainRoot)}(?=[\\s'"\`)]|$)`);
22971
23045
  return re.test(command);
22972
23046
  }
23047
+ function commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePath) {
23048
+ if (!worktreePath || worktreePath === mainRoot) {
23049
+ return commandContainsMainRoot(command, mainRoot);
23050
+ }
23051
+ const wtpIdx = command.indexOf(worktreePath);
23052
+ if (wtpIdx !== -1) {
23053
+ const afterWtp = command.slice(wtpIdx + worktreePath.length);
23054
+ if (/(?:^|[/\\])\.\.(?:[/\\\s'";|<>]|$)/.test(afterWtp)) {
23055
+ return true;
23056
+ }
23057
+ }
23058
+ const sanitized = command.split(worktreePath).join("");
23059
+ return commandContainsMainRoot(sanitized, mainRoot);
23060
+ }
22973
23061
  function detectBashWriteIntent(command, mainRoot) {
22974
23062
  if (isReadOnlyBashCommand(command))
22975
23063
  return false;
@@ -23227,7 +23315,7 @@ var sessionWorktreeGuardPlugin = async (ctx) => {
23227
23315
  }
23228
23316
  if (toolName === "bash") {
23229
23317
  const command = argsObj["command"];
23230
- if (typeof command === "string" && commandContainsMainRoot(command, mainRoot) && detectBashWriteIntent(command, mainRoot)) {
23318
+ if (typeof command === "string" && commandContainsMainRootExcludingWorktree(command, mainRoot, worktreePath) && detectBashWriteIntent(command, mainRoot)) {
23231
23319
  const caller = await resolveAgentForGuard({ sessionID: input.sessionID, agent: input.agent }, ctx.client, log14);
23232
23320
  if (caller !== null && CLASS_B_CALLER_WHITELIST.has(caller)) {
23233
23321
  log14.debug?.(`[class-b-whitelist] allow caller=${caller}`, { sessionId, tool: toolName, command: command.slice(0, 200) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andyqiu/codeforge",
3
- "version": "0.5.10",
3
+ "version": "0.5.11",
4
4
  "description": "CodeForge — opencode 的零侵入扩展包",
5
5
  "type": "module",
6
6
  "private": false,