snipara-companion 1.4.2 → 1.4.3

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 +46 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11075,7 +11075,12 @@ function loadCollaborationState(rootDir = process.cwd()) {
11075
11075
  if (!fs16.existsSync(statePath)) {
11076
11076
  return createEmptyCollaborationState();
11077
11077
  }
11078
- const parsed = JSON.parse(fs16.readFileSync(statePath, "utf8"));
11078
+ let parsed;
11079
+ try {
11080
+ parsed = JSON.parse(fs16.readFileSync(statePath, "utf8"));
11081
+ } catch {
11082
+ return createEmptyCollaborationState();
11083
+ }
11079
11084
  return {
11080
11085
  schemaVersion: "snipara.collaboration.v1",
11081
11086
  updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString(),
@@ -11098,8 +11103,16 @@ function loadCollaborationState(rootDir = process.cwd()) {
11098
11103
  function saveCollaborationState(state, rootDir = process.cwd()) {
11099
11104
  const statePath = getCollaborationStatePath(rootDir);
11100
11105
  fs16.mkdirSync(path15.dirname(statePath), { recursive: true });
11101
- fs16.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}
11106
+ const tempPath = `${statePath}.${process.pid}.${Date.now()}.tmp`;
11107
+ try {
11108
+ fs16.writeFileSync(tempPath, `${JSON.stringify(state, null, 2)}
11102
11109
  `, "utf8");
11110
+ fs16.renameSync(tempPath, statePath);
11111
+ } finally {
11112
+ if (fs16.existsSync(tempPath)) {
11113
+ fs16.unlinkSync(tempPath);
11114
+ }
11115
+ }
11103
11116
  }
11104
11117
  function buildCollaborationActor(options, config) {
11105
11118
  const username = safeUsername();
@@ -11905,13 +11918,27 @@ function deriveContextResources(context) {
11905
11918
  return resources;
11906
11919
  }
11907
11920
  function readGitDirtyFiles(rootDir) {
11908
- const status = readGitValue3(rootDir, ["status", "--short"]);
11921
+ const status = readGitRawValue(rootDir, ["status", "--porcelain=v1", "-z"]);
11909
11922
  if (!status) {
11910
11923
  return [];
11911
11924
  }
11912
- return normalizeFiles2(
11913
- status.split(/\r?\n/).map((line) => line.slice(3).trim()).map((file) => file.split(" -> ").pop() ?? file)
11914
- );
11925
+ return normalizeFiles2(parseGitPorcelainZ(status));
11926
+ }
11927
+ function parseGitPorcelainZ(output) {
11928
+ const entries = output.split("\0").filter(Boolean);
11929
+ const files = [];
11930
+ for (let index = 0; index < entries.length; index += 1) {
11931
+ const entry = entries[index];
11932
+ if (entry.length < 4) {
11933
+ continue;
11934
+ }
11935
+ const status = entry.slice(0, 2);
11936
+ files.push(entry.slice(3));
11937
+ if (status.includes("R") || status.includes("C")) {
11938
+ index += 1;
11939
+ }
11940
+ }
11941
+ return files;
11915
11942
  }
11916
11943
  function deriveNextRouteResource(filePath) {
11917
11944
  const appRouterMatch = filePath.match(
@@ -12015,6 +12042,10 @@ function readRepositoryId(rootDir) {
12015
12042
  return remote;
12016
12043
  }
12017
12044
  function readGitValue3(rootDir, args) {
12045
+ const output = readGitRawValue(rootDir, args);
12046
+ return output?.trim() || void 0;
12047
+ }
12048
+ function readGitRawValue(rootDir, args) {
12018
12049
  try {
12019
12050
  const execOptions = {
12020
12051
  cwd: rootDir,
@@ -12022,7 +12053,7 @@ function readGitValue3(rootDir, args) {
12022
12053
  stdio: ["ignore", "pipe", "ignore"],
12023
12054
  timeout: 1e3
12024
12055
  };
12025
- const output = (0, import_node_child_process4.execFileSync)("git", args, execOptions).trim();
12056
+ const output = (0, import_node_child_process4.execFileSync)("git", args, execOptions);
12026
12057
  return output || void 0;
12027
12058
  } catch {
12028
12059
  return void 0;
@@ -12036,8 +12067,15 @@ function buildCollaborationGitHookBlock(hookName) {
12036
12067
  return [
12037
12068
  collaborationHookBlockMarker(hookName, "start"),
12038
12069
  "# Block unsafe parallel edits before code leaves the local worktree.",
12039
- "if command -v snipara-companion >/dev/null 2>&1; then",
12070
+ 'if [ "${SNIPARA_COLLABORATION_GUARD:-1}" = "0" ]; then',
12071
+ ' echo "Snipara collaboration guard bypassed by SNIPARA_COLLABORATION_GUARD=0" >&2',
12072
+ "elif command -v snipara-companion >/dev/null 2>&1; then",
12040
12073
  ` snipara-companion collaboration guard --profile ${profile} --action ${profile} --enforce --json >/dev/null`,
12074
+ "elif command -v npx >/dev/null 2>&1; then",
12075
+ ` npx --yes snipara-companion@latest collaboration guard --profile ${profile} --action ${profile} --enforce --json >/dev/null`,
12076
+ "else",
12077
+ ' echo "snipara-companion is required for the Snipara collaboration guard. Install it or set SNIPARA_COLLABORATION_GUARD=0 for an explicit emergency bypass." >&2',
12078
+ " exit 1",
12041
12079
  "fi",
12042
12080
  collaborationHookBlockMarker(hookName, "end")
12043
12081
  ].join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "Snipara Git-style companion CLI for hosted context, agent continuity, hooks, and automation workflows",
5
5
  "main": "dist/index.js",
6
6
  "bin": {