@lannguyensi/harness 0.10.1 → 0.11.0

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 (47) hide show
  1. package/CHANGELOG.md +78 -7
  2. package/README.md +1 -1
  3. package/dist/cli/approve/understanding.d.ts +8 -0
  4. package/dist/cli/approve/understanding.js +56 -7
  5. package/dist/cli/approve/understanding.js.map +1 -1
  6. package/dist/cli/audit.d.ts +8 -0
  7. package/dist/cli/audit.js +2 -2
  8. package/dist/cli/audit.js.map +1 -1
  9. package/dist/cli/doctor/format.js +7 -1
  10. package/dist/cli/doctor/format.js.map +1 -1
  11. package/dist/cli/doctor/index.js +62 -5
  12. package/dist/cli/doctor/index.js.map +1 -1
  13. package/dist/cli/doctor/types.d.ts +15 -0
  14. package/dist/cli/dry-run.js +9 -3
  15. package/dist/cli/dry-run.js.map +1 -1
  16. package/dist/cli/explain.d.ts +8 -0
  17. package/dist/cli/explain.js +6 -4
  18. package/dist/cli/explain.js.map +1 -1
  19. package/dist/cli/index.js +40 -1
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/cli/init/dependencies.js +17 -7
  22. package/dist/cli/init/dependencies.js.map +1 -1
  23. package/dist/cli/init/templates.d.ts +1 -1
  24. package/dist/cli/init/templates.js +14 -5
  25. package/dist/cli/init/templates.js.map +1 -1
  26. package/dist/cli/pack/hook-pre-tool-use.d.ts +2 -0
  27. package/dist/cli/pack/hook-pre-tool-use.js +34 -2
  28. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  29. package/dist/cli/policy/intercept.d.ts +7 -1
  30. package/dist/cli/policy/intercept.js +28 -6
  31. package/dist/cli/policy/intercept.js.map +1 -1
  32. package/dist/cli/session-start/index.d.ts +52 -0
  33. package/dist/cli/session-start/index.js +195 -0
  34. package/dist/cli/session-start/index.js.map +1 -0
  35. package/dist/runtime/git-context.d.ts +16 -0
  36. package/dist/runtime/git-context.js +97 -0
  37. package/dist/runtime/git-context.js.map +1 -0
  38. package/dist/runtime/index.d.ts +1 -0
  39. package/dist/runtime/index.js +1 -0
  40. package/dist/runtime/index.js.map +1 -1
  41. package/dist/runtime/pending-approval.d.ts +31 -0
  42. package/dist/runtime/pending-approval.js +80 -0
  43. package/dist/runtime/pending-approval.js.map +1 -0
  44. package/dist/runtime/session-id.d.ts +40 -1
  45. package/dist/runtime/session-id.js +99 -8
  46. package/dist/runtime/session-id.js.map +1 -1
  47. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-approval.js","sourceRoot":"","sources":["../../src/runtime/pending-approval.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,4EAA4E;AAC5E,sEAAsE;AACtE,uEAAuE;AACvE,oEAAoE;AACpE,EAAE;AACF,yEAAyE;AACzE,gEAAgE;AAChE,0EAA0E;AAC1E,4EAA4E;AAC5E,eAAe;AACf,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,qEAAqE;AACrE,wDAAwD;AAExD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,yEAAyE;AACzE,oEAAoE;AACpE,sEAAsE;AACtE,oBAAoB;AACpB,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAErD,MAAM,CAAC,MAAM,yBAAyB,GAAG,mBAAmB,CAAC;AAE7D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAGnC;IACC,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IAClF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,yBAAyB,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,YAAoB,EAAE,SAAiB;IAC1E,eAAe,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,GAAG,SAAS,IAAI,CAAC,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,YAAoB;IACvD,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Resolve the active grounding session id.
2
+ * Resolve the active grounding session id (WRITE path).
3
3
  *
4
4
  * Empty strings are treated as "not provided" — both for the explicit
5
5
  * argument and for the env var, since `--session ''` from a forgetful
@@ -8,3 +8,42 @@
8
8
  * empty session.
9
9
  */
10
10
  export declare function resolveSessionId(explicit?: string): string;
11
+ export interface DiscoverSessionOptions {
12
+ /** Override the Claude Code projects root (tests). */
13
+ projectsRoot?: string;
14
+ /** Override `$HOME` used to derive the projects root (tests). */
15
+ homeDir?: string;
16
+ }
17
+ /**
18
+ * Discover the current Claude Code session id from the most recently
19
+ * modified transcript JSONL under `~/.claude/projects/<project>/`.
20
+ *
21
+ * Claude Code appends to the active session's transcript live, so the
22
+ * newest-mtime `<uuid>.jsonl` across all project dirs is the running
23
+ * session. Returns `null` when the projects root is absent or holds no
24
+ * transcript files — callers fall through to their own default.
25
+ */
26
+ export declare function discoverNewestSessionId(opts?: DiscoverSessionOptions): string | null;
27
+ export interface ResolveReadSessionOptions extends DiscoverSessionOptions {
28
+ /**
29
+ * Test seam — override the transcript-discovery tier wholesale. The
30
+ * default scans the real `~/.claude/projects` via
31
+ * `discoverNewestSessionId`.
32
+ */
33
+ discover?: (opts: DiscoverSessionOptions) => string | null;
34
+ }
35
+ /**
36
+ * Resolve the session id for the READ path (`audit`,
37
+ * `explain --trace/--last`).
38
+ *
39
+ * Precedence:
40
+ * 1. explicit value (the `--session` flag)
41
+ * 2. `$CLAUDE_SESSION_ID` env
42
+ * 3. newest Claude Code transcript (the live session)
43
+ * 4. literal `"default"`
44
+ *
45
+ * The WRITE path keeps `resolveSessionId`: it always has
46
+ * `event.session_id`, so it never reaches the discovery tier, and a
47
+ * per-hook-event filesystem scan would be wasteful.
48
+ */
49
+ export declare function resolveReadSessionId(explicit?: string, opts?: ResolveReadSessionOptions): string;
@@ -9,16 +9,28 @@
9
9
  // audit/trace even though the writes had landed correctly under the
10
10
  // real UUID.
11
11
  //
12
- // This helper lifts the precedence chain into one place:
13
- // 1. explicit value (from a `--session` flag or the runtime's
14
- // `event.session_id`)
15
- // 2. `$CLAUDE_SESSION_ID` env (set by the Claude Code harness inside
16
- // every session)
17
- // 3. literal `"default"` — preserves prior behaviour for ad-hoc
18
- // invocations outside a Claude Code session.
12
+ // `resolveSessionId` lifts the precedence chain into one place for the
13
+ // WRITE path (`policy intercept`), which always carries a concrete
14
+ // `event.session_id`:
15
+ // 1. explicit value (the runtime's `event.session_id`)
16
+ // 2. `$CLAUDE_SESSION_ID` env
17
+ // 3. literal `"default"`
18
+ //
19
+ // The READ path (`audit`, `explain --trace/--last`) needs more. The
20
+ // Phase 5 #2 fix assumed `$CLAUDE_SESSION_ID` is exported into the
21
+ // session's shell environment — it is not (Claude Code does not put it
22
+ // in the Bash tool env), so tier 2 is inert in practice and the readers
23
+ // still fell through to `"default"`. `resolveReadSessionId` adds a
24
+ // transcript-discovery tier: when no explicit id and no env, it reads
25
+ // the active session id off the newest Claude Code transcript JSONL.
26
+ // That is the programmatic form of the heuristic `harness approve`'s
27
+ // own help text recommends to humans.
28
+ import * as fs from "node:fs";
29
+ import * as os from "node:os";
30
+ import * as path from "node:path";
19
31
  const FALLBACK = "default";
20
32
  /**
21
- * Resolve the active grounding session id.
33
+ * Resolve the active grounding session id (WRITE path).
22
34
  *
23
35
  * Empty strings are treated as "not provided" — both for the explicit
24
36
  * argument and for the env var, since `--session ''` from a forgetful
@@ -34,4 +46,83 @@ export function resolveSessionId(explicit) {
34
46
  return env;
35
47
  return FALLBACK;
36
48
  }
49
+ // Claude Code names each session's transcript `<uuid>.jsonl`. Anchored
50
+ // so sibling files (memory dirs, `.pending-approval`, etc.) are ignored.
51
+ const SESSION_TRANSCRIPT_RE = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
52
+ /**
53
+ * Discover the current Claude Code session id from the most recently
54
+ * modified transcript JSONL under `~/.claude/projects/<project>/`.
55
+ *
56
+ * Claude Code appends to the active session's transcript live, so the
57
+ * newest-mtime `<uuid>.jsonl` across all project dirs is the running
58
+ * session. Returns `null` when the projects root is absent or holds no
59
+ * transcript files — callers fall through to their own default.
60
+ */
61
+ export function discoverNewestSessionId(opts = {}) {
62
+ const projectsRoot = opts.projectsRoot ??
63
+ path.join(opts.homeDir ?? os.homedir(), ".claude", "projects");
64
+ let projectDirs;
65
+ try {
66
+ projectDirs = fs.readdirSync(projectsRoot);
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ let newest = null;
72
+ for (const dir of projectDirs) {
73
+ const projectPath = path.join(projectsRoot, dir);
74
+ let files;
75
+ try {
76
+ files = fs.readdirSync(projectPath);
77
+ }
78
+ catch {
79
+ continue; // not a directory, or unreadable — skip
80
+ }
81
+ for (const file of files) {
82
+ const match = SESSION_TRANSCRIPT_RE.exec(file);
83
+ if (!match)
84
+ continue;
85
+ let mtimeMs;
86
+ try {
87
+ mtimeMs = fs.statSync(path.join(projectPath, file)).mtimeMs;
88
+ }
89
+ catch {
90
+ continue;
91
+ }
92
+ if (newest === null || mtimeMs > newest.mtimeMs) {
93
+ newest = { id: match[1], mtimeMs };
94
+ }
95
+ }
96
+ }
97
+ return newest === null ? null : newest.id;
98
+ }
99
+ /**
100
+ * Resolve the session id for the READ path (`audit`,
101
+ * `explain --trace/--last`).
102
+ *
103
+ * Precedence:
104
+ * 1. explicit value (the `--session` flag)
105
+ * 2. `$CLAUDE_SESSION_ID` env
106
+ * 3. newest Claude Code transcript (the live session)
107
+ * 4. literal `"default"`
108
+ *
109
+ * The WRITE path keeps `resolveSessionId`: it always has
110
+ * `event.session_id`, so it never reaches the discovery tier, and a
111
+ * per-hook-event filesystem scan would be wasteful.
112
+ */
113
+ export function resolveReadSessionId(explicit, opts = {}) {
114
+ if (typeof explicit === "string" && explicit.length > 0)
115
+ return explicit;
116
+ const env = process.env.CLAUDE_SESSION_ID;
117
+ if (typeof env === "string" && env.length > 0)
118
+ return env;
119
+ const discover = opts.discover ?? discoverNewestSessionId;
120
+ const discovered = discover({
121
+ ...(opts.projectsRoot !== undefined && { projectsRoot: opts.projectsRoot }),
122
+ ...(opts.homeDir !== undefined && { homeDir: opts.homeDir }),
123
+ });
124
+ if (typeof discovered === "string" && discovered.length > 0)
125
+ return discovered;
126
+ return FALLBACK;
127
+ }
37
128
  //# sourceMappingURL=session-id.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-id.js","sourceRoot":"","sources":["../../src/runtime/session-id.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,uEAAuE;AACvE,iEAAiE;AACjE,qEAAqE;AACrE,8DAA8D;AAC9D,wEAAwE;AACxE,iEAAiE;AACjE,oEAAoE;AACpE,aAAa;AACb,EAAE;AACF,yDAAyD;AACzD,gEAAgE;AAChE,2BAA2B;AAC3B,uEAAuE;AACvE,sBAAsB;AACtB,kEAAkE;AAClE,kDAAkD;AAElD,MAAM,QAAQ,GAAG,SAAS,CAAC;AAE3B;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAiB;IAChD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACzE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1D,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"session-id.js","sourceRoot":"","sources":["../../src/runtime/session-id.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,uEAAuE;AACvE,iEAAiE;AACjE,qEAAqE;AACrE,8DAA8D;AAC9D,wEAAwE;AACxE,iEAAiE;AACjE,oEAAoE;AACpE,aAAa;AACb,EAAE;AACF,uEAAuE;AACvE,mEAAmE;AACnE,sBAAsB;AACtB,yDAAyD;AACzD,gCAAgC;AAChC,2BAA2B;AAC3B,EAAE;AACF,oEAAoE;AACpE,mEAAmE;AACnE,uEAAuE;AACvE,wEAAwE;AACxE,mEAAmE;AACnE,sEAAsE;AACtE,qEAAqE;AACrE,qEAAqE;AACrE,sCAAsC;AAEtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,MAAM,QAAQ,GAAG,SAAS,CAAC;AAE3B;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAiB;IAChD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACzE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1D,OAAO,QAAQ,CAAC;AAClB,CAAC;AASD,uEAAuE;AACvE,yEAAyE;AACzE,MAAM,qBAAqB,GACzB,0EAA0E,CAAC;AAE7E;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAA+B,EAAE;IAEjC,MAAM,YAAY,GAChB,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACjE,IAAI,WAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,GAA2C,IAAI,CAAC;IAC1D,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QACjD,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,wCAAwC;QACpD,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;gBAChD,MAAM,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,EAAE,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;AAC5C,CAAC;AAWD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAiB,EACjB,OAAkC,EAAE;IAEpC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACzE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,uBAAuB,CAAC;IAC1D,MAAM,UAAU,GAAG,QAAQ,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;QAC3E,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;KAC7D,CAAC,CAAC;IACH,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC;IAC/E,OAAO,QAAQ,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lannguyensi/harness",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Declarative control plane for agent harnesses — one YAML for grounding, tools, memory, and hooks.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/LanNguyenSi/harness",