cursedops 0.2.4 → 0.2.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 CHANGED
@@ -88,6 +88,12 @@ A generation is defined by a `forge.env` above the checkout — the same rule it
88
88
  `null` rather than guessing, because a function that invented a root would write files
89
89
  into a stranger's tree.
90
90
 
91
+ 🔴 **A git worktree counts** (0.2.5). Worktrees live outside every generation by
92
+ construction, so the walk up alone answered `null` there and a repo whose gate asks for
93
+ the root was red in every worktree — the runner's subset gate included. When the walk
94
+ fails, `git rev-parse --git-common-dir` leads back to the primary checkout and the walk
95
+ runs from there. A clone is its own primary and still gets `null`.
96
+
91
97
  🔴 The state root is **read out of `forge.env`**, never spelled here. A published
92
98
  library that hardcoded `$HOME/.<name>` would be correct for exactly one generation and
93
99
  silently wrong for its successor — which is the defect this replaced.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursedops",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "The build-and-ops answers this generation's apps wrote independently and identically: finding a generation's roots without knowing a path, macOS launchd agent install/replace/remove, the scaffolding of a deployed smoke, and the static-serving helpers eight apps copied — the path-traversal guard among them — and the API floor that keeps an unmatched /api/... from ever being answered with the app shell. Mechanism only — no app knows its name from here. Bun, zero dependencies, ships source.",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/roots.ts CHANGED
@@ -31,6 +31,20 @@
31
31
  * doing nothing and saying so; {@link requireForgeState} is the one-line way to do
32
32
  * that with a message worth reading.
33
33
  *
34
+ * 🔴 **A git worktree of a generation's repo IS inside that generation**, even though no
35
+ * `forge.env` sits above it: the working agreement puts every worktree under the
36
+ * machine-wide worktrees root, outside every generation by construction. Walking up
37
+ * from one finds nothing, so until 0.2.5 every caller here answered `null` in the
38
+ * one place agents are told to work — and the runner's subset gate, which proves a
39
+ * pathspec commit in a throwaway worktree, found `desk`'s gate red on EVERY change
40
+ * (`test/build/advisories-queue.test.ts` asks for the root; 2026-09-21, the
41
+ * `cursedauth` bump stranded for a day and blamed on the work). A worktree's `.git`
42
+ * is a file pointing at the primary checkout's git directory, so
43
+ * {@link findForgeRootViaWorktree} follows it back with no environment variable and
44
+ * no guess — the same rule every repo's `scripts/paths.ts` already used. A clone,
45
+ * an archive or a `node_modules` copy is not a worktree of anything in a generation
46
+ * and still gets `null`.
47
+ *
34
48
  * 🔴 **This library is published, so it may not know a generation's NAME either.**
35
49
  * `apps/flix/scripts/forgeRoot.ts` fell back to `$HOME/.cursedforge` when the
36
50
  * environment carried no `FORGE_STATE`, which is correct for exactly one
@@ -39,6 +53,7 @@
39
53
  * generation being called something else.
40
54
  */
41
55
 
56
+ import { spawnSync } from "node:child_process";
42
57
  import { existsSync, readFileSync } from "node:fs";
43
58
  import { dirname, join, resolve } from "node:path";
44
59
  import { fileURLToPath } from "node:url";
@@ -71,9 +86,40 @@ function walkUp(from: string, marker: string): string | null {
71
86
  return null;
72
87
  }
73
88
 
74
- /** The directory holding `forge.env`, or `null` when `from` is not inside a generation. */
89
+ /**
90
+ * The generation above the checkout a git WORKTREE at `from` was cut from, or `null`.
91
+ *
92
+ * `git rev-parse --git-common-dir` names the primary checkout's git directory; the
93
+ * ordinary walk runs from the directory holding it. For a primary checkout, a clone
94
+ * or a plain repo that is its own `.git`, so the answer is exactly what the walk up
95
+ * from `from` already said. `null` on anything unexpected — no git, not a repo, a
96
+ * directory that is not there — because inventing a root is the failure this module
97
+ * is against.
98
+ */
99
+ export function findForgeRootViaWorktree(from: string): string | null {
100
+ try {
101
+ const git = spawnSync("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], {
102
+ cwd: from,
103
+ encoding: "utf8",
104
+ timeout: 5_000,
105
+ });
106
+ if (git.status !== 0) return null;
107
+ const common = (git.stdout ?? "").trim();
108
+ if (!common.startsWith("/")) return null;
109
+ return walkUp(dirname(common), MARKER);
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * The directory holding `forge.env`, or `null` when `from` is not inside a generation.
117
+ *
118
+ * The walk up first — the only answer for a launchd job, and git is never asked when
119
+ * it succeeds — then a worktree followed back to its primary checkout.
120
+ */
75
121
  export function findForgeRoot(from: string): string | null {
76
- return walkUp(from, MARKER);
122
+ return walkUp(from, MARKER) ?? findForgeRootViaWorktree(from);
77
123
  }
78
124
 
79
125
  /** {@link findForgeRoot} from a module's own location. Pass `import.meta.url`. */