@codyswann/lisa 4.34.21 → 4.35.1

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 (94) hide show
  1. package/all/copy-overwrite/scripts/check-npm-publish-landed.mjs +49 -26
  2. package/all/copy-overwrite/scripts/lib/kill-marks.mjs +1 -0
  3. package/all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh +49 -10
  4. package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +42 -4
  5. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +33 -12
  6. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +9 -4
  7. package/all/copy-overwrite/scripts/lisa-work-item.mjs +9 -6
  8. package/dist/codex/scripts/_extract-edit-paths.sh +4 -0
  9. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  10. package/dist/core/lisa-owned-hash-ledger.js +20 -0
  11. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  12. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  13. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  14. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  15. package/dist/core/upstream-evidence-manifest.js +21 -10
  16. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  17. package/dist/migrations/ensure-lisa-postinstall.d.ts +6 -37
  18. package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
  19. package/dist/migrations/ensure-lisa-postinstall.js +20 -67
  20. package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
  21. package/dist/opencode/plugin-templates/parity-safety-net.sh +33 -12
  22. package/package.json +4 -4
  23. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa/hooks/block-direct-issue-create.sh +49 -10
  26. package/plugins/lisa/hooks/block-managed-file-edits.sh +42 -4
  27. package/plugins/lisa/hooks/parity-safety-net.sh +33 -12
  28. package/plugins/lisa/rules/eager/measurement-integrity.md +18 -0
  29. package/plugins/lisa/rules/reference/measurement-integrity.md +109 -0
  30. package/plugins/lisa-agy/hooks/block-direct-issue-create.sh +49 -10
  31. package/plugins/lisa-agy/hooks/parity-safety-net.sh +33 -12
  32. package/plugins/lisa-agy/plugin.json +1 -1
  33. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  36. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-copilot/hooks/block-direct-issue-create.sh +49 -10
  40. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +42 -4
  41. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +33 -12
  42. package/plugins/lisa-copilot/rules/eager/measurement-integrity.md +18 -0
  43. package/plugins/lisa-copilot/rules/reference/measurement-integrity.md +109 -0
  44. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-cursor/hooks/block-direct-issue-create.sh +49 -10
  46. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +42 -4
  47. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +33 -12
  48. package/plugins/lisa-cursor/rules/measurement-integrity-reference.mdc +114 -0
  49. package/plugins/lisa-cursor/rules/measurement-integrity.mdc +23 -0
  50. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-expo-agy/plugin.json +1 -1
  53. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  58. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  63. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  68. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  73. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  77. package/plugins/lisa-rails-agy/plugin.json +1 -1
  78. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  82. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  83. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  87. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  88. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  90. package/plugins/src/base/hooks/block-direct-issue-create.sh +49 -10
  91. package/plugins/src/base/hooks/block-managed-file-edits.sh +42 -4
  92. package/plugins/src/base/hooks/parity-safety-net.sh +33 -12
  93. package/plugins/src/base/rules/eager/measurement-integrity.md +18 -0
  94. package/plugins/src/base/rules/reference/measurement-integrity.md +109 -0
@@ -1,48 +1,17 @@
1
1
  import type { Migration, MigrationContext, MigrationResult } from "./migration.interface.js";
2
- /**
3
- * What the operator sees on stderr when the bootstrap apply fails.
4
- *
5
- * Written for someone scrolling past a wall of install output who may not know
6
- * what Lisa is: it says what stopped working (templates and guardrails are no
7
- * longer arriving), that the real error is directly above, and the two commands
8
- * that reproduce and diagnose it. ASCII and free of `"`, `$`, and backticks so
9
- * it survives being embedded in a JSON string inside a `sh -c` command.
10
- */
11
- export declare const APPLY_FAILURE_NOTICE: string;
12
2
  /**
13
3
  * The bootstrap invocation chained into a host project's `postinstall`.
14
4
  *
15
- * Shape: **loud and fatal**. Two earlier shapes each discarded the apply's exit
16
- * code. `2>/dev/null || true` discarded its stderr too, so a totally failed
17
- * apply was byte-for-byte indistinguishable from success and a repo could
18
- * silently stop receiving template updates (CodySwannGT/lisa#2467). Replacing
19
- * `true` with `echo` recovered the reason but not the fact: `echo` also exits 0,
20
- * so a failed apply still reported success and the one warning line scrolled
21
- * away in a wall of install output (CodySwannGT/lisa#3466). The failure branch
22
- * now prints the notice and exits 1.
23
- *
24
- * Failing the install is a deliberate trade against the previous reasoning,
25
- * which was that a non-zero postinstall would convert a template-sync problem
26
- * into an install outage across the fleet. Two things bound that risk. The
27
- * `[ -n "$CI" ] ||` guard means this branch never executes when `CI` is set, so
28
- * no CI pipeline can be broken by it and the exposure is local installs only;
29
- * and `CI=1` remains the escape hatch for anyone who needs to install past a
30
- * broken apply. Against that, the apply receipt this failed run did NOT write —
31
- * previously offered as the durable signal — cannot tell `lisa doctor` that an
32
- * apply *failed*, only that none has succeeded recently, which is equally true
33
- * of a project that was never applied or was deliberately skipped. The exit code
34
- * is the only signal that distinguishes them.
35
- *
36
- * The cost, stated plainly: {@link composePostinstall} builds
37
- * `<lisa> && <the host's own postinstall>`, so a failed apply now also skips
38
- * whatever the host chained after it (`patch-package` and friends). That is the
39
- * honest outcome — the install genuinely did not complete — but it is a wider
40
- * change than the exit code alone.
5
+ * Shape: bounded, loud, durable, and deliberately non-fatal. The runner owns
6
+ * the timeout, exposes the real error, and records a marker for `lisa doctor`;
7
+ * `|| true` keeps a template failure from stranding the host before it can
8
+ * install the fix. This is the same invocation the package template writes,
9
+ * so a migration cannot leave older callers on the superseded direct apply.
41
10
  *
42
11
  * Exported so tests can execute the real script under `sh` rather than assert
43
12
  * on a string.
44
13
  */
45
- export declare const LISA_INVOCATION = "[ -n \"$CI\" ] || LISA_BOOTSTRAP=1 LISA_POSTINSTALL=1 node node_modules/@codyswann/lisa/dist/index.js --yes --skip-git-check . || { echo \"lisa: TEMPLATE APPLY FAILED - this project is NOT receiving Lisa template or guardrail updates. The error is above. Reproduce it with: LISA_POSTINSTALL=1 node node_modules/@codyswann/lisa/dist/index.js --yes --skip-git-check . - then run: node node_modules/@codyswann/lisa/dist/index.js doctor\" >&2; exit 1; }";
14
+ export declare const LISA_INVOCATION = "[ -n \"$CI\" ] || LISA_BOOTSTRAP=1 node node_modules/@codyswann/lisa/all/copy-overwrite/scripts/lisa-postinstall.mjs || true";
46
15
  /**
47
16
  * Migration: ensure Node-based projects chain Lisa into their postinstall script.
48
17
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ensure-lisa-postinstall.d.ts","sourceRoot":"","sources":["../../src/migrations/ensure-lisa-postinstall.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAqBlC;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,QAG8C,CAAC;AAYhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,eAAe,scAAyG,CAAC;AA0FtI;;;;;;;;;GASG;AACH,qBAAa,8BAA+B,YAAW,SAAS;IAC9D,QAAQ,CAAC,IAAI,6BAA6B;IAC1C,QAAQ,CAAC,WAAW,qEACgD;IAEpE;;;;;;;;;;;;;OAaG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAyBtD;;;;OAIG;IACG,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;CAqC7D"}
1
+ {"version":3,"file":"ensure-lisa-postinstall.d.ts","sourceRoot":"","sources":["../../src/migrations/ensure-lisa-postinstall.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AASlC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,iIAAmF,CAAC;AAoGhH;;;;;;;;;GASG;AACH,qBAAa,8BAA+B,YAAW,SAAS;IAC9D,QAAQ,CAAC,IAAI,6BAA6B;IAC1C,QAAQ,CAAC,WAAW,qEACgD;IAEpE;;;;;;;;;;;;;OAaG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0BtD;;;;OAIG;IACG,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;CAqC7D"}
@@ -4,76 +4,21 @@ import { readJsonOrNull, writeJson } from "../utils/json-utils.js";
4
4
  const PACKAGE_JSON = "package.json";
5
5
  const CI_GUARD_PREFIX = '[ -n "$CI" ] || ';
6
6
  const BOOTSTRAP_PREFIX = "LISA_BOOTSTRAP=1 ";
7
- /**
8
- * The hook's declaration that it is a package-manager install lifecycle.
9
- *
10
- * This — not `--skip-git-check` — is what selects the reduced
11
- * `postinstall-safe` apply (CodySwannGT/lisa#3066). It is spelled as an
12
- * environment prefix rather than a flag on purpose: the command tail stays
13
- * byte-identical, so {@link LISA_INVOCATION_RE} keeps matching every hook
14
- * already installed in a consumer's `package.json` with one added alternative
15
- * in a group it already had.
16
- */
17
- const POSTINSTALL_PREFIX = "LISA_POSTINSTALL=1 ";
18
- const LISA_MARKER = "node_modules/@codyswann/lisa/dist/index.js";
19
- const LISA_APPLY_COMMAND = `node ${LISA_MARKER} --yes --skip-git-check .`;
20
- /** The apply command with the declaration, as an operator would reproduce it. */
21
- const LISA_APPLY_COMMAND_AS_POSTINSTALL = `${POSTINSTALL_PREFIX}${LISA_APPLY_COMMAND}`;
22
- /**
23
- * What the operator sees on stderr when the bootstrap apply fails.
24
- *
25
- * Written for someone scrolling past a wall of install output who may not know
26
- * what Lisa is: it says what stopped working (templates and guardrails are no
27
- * longer arriving), that the real error is directly above, and the two commands
28
- * that reproduce and diagnose it. ASCII and free of `"`, `$`, and backticks so
29
- * it survives being embedded in a JSON string inside a `sh -c` command.
30
- */
31
- export const APPLY_FAILURE_NOTICE = "lisa: TEMPLATE APPLY FAILED - this project is NOT receiving Lisa template " +
32
- "or guardrail updates. The error is above. Reproduce it with: " +
33
- `${LISA_APPLY_COMMAND_AS_POSTINSTALL} - then run: node ${LISA_MARKER} doctor`;
34
- /**
35
- * The failure branch: say why, then exit non-zero.
36
- *
37
- * `exit 1` rather than a bare `false` because this invocation is routinely
38
- * chained (`<lisa> && <the host's own postinstall>`); `exit` ends the script at
39
- * the failure regardless of what a consumer composed around it, so the status
40
- * cannot be overwritten by a later command in the chain.
41
- */
42
- const APPLY_FAILURE_TAIL = ` || { echo "${APPLY_FAILURE_NOTICE}" >&2; exit 1; }`;
7
+ const LEGACY_LISA_MARKER = "node_modules/@codyswann/lisa/dist/index.js";
8
+ const POSTINSTALL_RUNNER_MARKER = "node_modules/@codyswann/lisa/all/copy-overwrite/scripts/lisa-postinstall.mjs";
43
9
  /**
44
10
  * The bootstrap invocation chained into a host project's `postinstall`.
45
11
  *
46
- * Shape: **loud and fatal**. Two earlier shapes each discarded the apply's exit
47
- * code. `2>/dev/null || true` discarded its stderr too, so a totally failed
48
- * apply was byte-for-byte indistinguishable from success and a repo could
49
- * silently stop receiving template updates (CodySwannGT/lisa#2467). Replacing
50
- * `true` with `echo` recovered the reason but not the fact: `echo` also exits 0,
51
- * so a failed apply still reported success and the one warning line scrolled
52
- * away in a wall of install output (CodySwannGT/lisa#3466). The failure branch
53
- * now prints the notice and exits 1.
54
- *
55
- * Failing the install is a deliberate trade against the previous reasoning,
56
- * which was that a non-zero postinstall would convert a template-sync problem
57
- * into an install outage across the fleet. Two things bound that risk. The
58
- * `[ -n "$CI" ] ||` guard means this branch never executes when `CI` is set, so
59
- * no CI pipeline can be broken by it and the exposure is local installs only;
60
- * and `CI=1` remains the escape hatch for anyone who needs to install past a
61
- * broken apply. Against that, the apply receipt this failed run did NOT write —
62
- * previously offered as the durable signal — cannot tell `lisa doctor` that an
63
- * apply *failed*, only that none has succeeded recently, which is equally true
64
- * of a project that was never applied or was deliberately skipped. The exit code
65
- * is the only signal that distinguishes them.
66
- *
67
- * The cost, stated plainly: {@link composePostinstall} builds
68
- * `<lisa> && <the host's own postinstall>`, so a failed apply now also skips
69
- * whatever the host chained after it (`patch-package` and friends). That is the
70
- * honest outcome — the install genuinely did not complete — but it is a wider
71
- * change than the exit code alone.
12
+ * Shape: bounded, loud, durable, and deliberately non-fatal. The runner owns
13
+ * the timeout, exposes the real error, and records a marker for `lisa doctor`;
14
+ * `|| true` keeps a template failure from stranding the host before it can
15
+ * install the fix. This is the same invocation the package template writes,
16
+ * so a migration cannot leave older callers on the superseded direct apply.
72
17
  *
73
18
  * Exported so tests can execute the real script under `sh` rather than assert
74
19
  * on a string.
75
20
  */
76
- export const LISA_INVOCATION = `${CI_GUARD_PREFIX}${BOOTSTRAP_PREFIX}${POSTINSTALL_PREFIX}${LISA_APPLY_COMMAND}${APPLY_FAILURE_TAIL}`;
21
+ export const LISA_INVOCATION = `${CI_GUARD_PREFIX}${BOOTSTRAP_PREFIX}node ${POSTINSTALL_RUNNER_MARKER} || true`;
77
22
  /**
78
23
  * Project types that do not use Node.js postinstall hooks (e.g. Rails).
79
24
  * Projects detected as only these types are skipped by this migration.
@@ -108,11 +53,15 @@ async function readPackageJson(projectDir) {
108
53
  * never re-applies keeps its old spelling forever, which is why an old
109
54
  * alternative may never be removed.
110
55
  */
111
- const LISA_INVOCATION_RE = new RegExp('(?:(?:\\[ -n "\\$CI" \\] \\|\\| )|(?:LISA_BOOTSTRAP=1 )|(?:LISA_POSTINSTALL=1 ))*' +
56
+ const LEGACY_LISA_INVOCATION_RE = new RegExp('(?:(?:\\[ -n "\\$CI" \\] \\|\\| )|(?:LISA_BOOTSTRAP=1 )|(?:LISA_POSTINSTALL=1 ))*' +
112
57
  "node node_modules/@codyswann/lisa/dist/index\\.js --yes --skip-git-check \\." +
113
58
  "(?: 2>/dev/null \\|\\| true" +
114
59
  '| \\|\\| \\{ echo "[^"]*" >&2; exit 1; \\}' +
115
60
  '| \\|\\| echo "[^"]*" >&2)?');
61
+ /** The bounded runner spelling currently written by Lisa. */
62
+ const POSTINSTALL_RUNNER_RE = new RegExp('(?:(?:\\[ -n "\\$CI" \\] \\|\\| )|(?:LISA_BOOTSTRAP=1 ))*' +
63
+ "node node_modules/@codyswann/lisa/all/copy-overwrite/scripts/" +
64
+ "lisa-postinstall\\.mjs(?: \\|\\| true)?");
116
65
  /**
117
66
  * Compose the new postinstall, prepending the Lisa invocation to any existing command.
118
67
  * If the existing script already contains a legacy Lisa invocation (no CI guard),
@@ -125,8 +74,11 @@ function composePostinstall(existing) {
125
74
  if (!trimmed) {
126
75
  return LISA_INVOCATION;
127
76
  }
128
- if (trimmed.includes(LISA_MARKER)) {
129
- return trimmed.replace(LISA_INVOCATION_RE, LISA_INVOCATION);
77
+ if (trimmed.includes(POSTINSTALL_RUNNER_MARKER)) {
78
+ return trimmed.replace(POSTINSTALL_RUNNER_RE, LISA_INVOCATION);
79
+ }
80
+ if (trimmed.includes(LEGACY_LISA_MARKER)) {
81
+ return trimmed.replace(LEGACY_LISA_INVOCATION_RE, LISA_INVOCATION);
130
82
  }
131
83
  return `${LISA_INVOCATION} && ${trimmed}`;
132
84
  }
@@ -187,7 +139,8 @@ export class EnsureLisaPostinstallMigration {
187
139
  }
188
140
  const normalizedPostinstall = composePostinstall(postinstall);
189
141
  if (!hasNodePostinstallType(ctx.detectedTypes)) {
190
- return (postinstall.includes(LISA_MARKER) &&
142
+ return ((postinstall.includes(LEGACY_LISA_MARKER) ||
143
+ postinstall.includes(POSTINSTALL_RUNNER_MARKER)) &&
191
144
  normalizedPostinstall !== postinstall);
192
145
  }
193
146
  return normalizedPostinstall !== postinstall;
@@ -1 +1 @@
1
- {"version":3,"file":"ensure-lisa-postinstall.js","sourceRoot":"","sources":["../../src/migrations/ensure-lisa-postinstall.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ensure-lisa-postinstall.js","sourceRoot":"","sources":["../../src/migrations/ensure-lisa-postinstall.ts"],"names":[],"mappings":"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"}
@@ -179,12 +179,14 @@ EOF
179
179
  # The patch file is per-worktree: `mktemp` names it, so nothing has to invent a
180
180
  # filename, and it lives in this agent's own TMPDIR where no sibling can reach
181
181
  # it. Untracked files are unaffected by both refused operations, so `git diff`
182
- # captures everything at risk.
182
+ # captures everything at risk. `HEAD` includes staged and unstaged changes, and
183
+ # `--binary` keeps binary edits recoverable rather than emitting a text-only
184
+ # placeholder that cannot be applied.
183
185
  PRESERVE_GUIDANCE="$(
184
186
  cat <<'EOF'
185
187
  Preserve the work first, per-worktree:
186
188
 
187
- git diff [-- <path>] > "$(mktemp "${TMPDIR:-/tmp}/lisa-preserve-XXXXXX.patch")"
189
+ git diff --binary HEAD [-- <path>] > "$(mktemp "${TMPDIR:-/tmp}/lisa-preserve-XXXXXX.patch")"
188
190
 
189
191
  and restore it later with `git apply <that file>`.
190
192
 
@@ -444,6 +446,7 @@ follow_refuse() {
444
446
  # so peak process count never rises. The latency was the whole problem.
445
447
  follow_unwrapped=""
446
448
  follow_operand_index=0
449
+ follow_located=""
447
450
 
448
451
  # Strip substitution, quote and alias wrappers off one token. A sibling of
449
452
  # strip_subst_wrappers() below rather than a call to it: that one is defined
@@ -568,6 +571,7 @@ follow_add_base() {
568
571
  # this hook cannot evaluate.
569
572
  follow_locate() {
570
573
  local token="$1" home="${HOME:-}" tmp="${TMPDIR:-/tmp}" proj="${CLAUDE_PROJECT_DIR:-$PWD}" base
574
+ follow_located=""
571
575
  tmp="${tmp%/}"
572
576
  [ -n "$tmp" ] || tmp="/tmp"
573
577
  # The tilde arms below are LITERAL text: this is the unexpanded spelling the
@@ -589,14 +593,14 @@ follow_locate() {
589
593
  "" | *'$'* | *'*'* | *'?'* | *'`'* | *'{'* | *'['*) return 1 ;;
590
594
  /*)
591
595
  [ -f "$token" ] && [ -r "$token" ] || return 1
592
- printf '%s' "$token"
596
+ follow_located="$token"
593
597
  return 0
594
598
  ;;
595
599
  esac
596
600
  while IFS= read -r base; do
597
601
  [ -n "$base" ] || continue
598
602
  if [ -f "$base/$token" ] && [ -r "$base/$token" ]; then
599
- printf '%s' "$base/$token"
603
+ follow_located="$base/$token"
600
604
  return 0
601
605
  fi
602
606
  done <<<"$PWD"$'\n'"$proj$follow_bases"
@@ -641,12 +645,13 @@ follow_take() {
641
645
  follow_target() {
642
646
  local token="$1" depth="$2" path=""
643
647
  [ -n "$token" ] || return 0
644
- if ! path="$(follow_locate "$token")"; then
648
+ if ! follow_locate "$token"; then
645
649
  if [ "$depth" -eq 0 ]; then
646
650
  follow_refuse "$token" "no readable file could be resolved for it"
647
651
  fi
648
652
  return 0
649
653
  fi
654
+ path="$follow_located"
650
655
  follow_take "$path" "$depth"
651
656
  }
652
657
 
@@ -667,11 +672,18 @@ follow_target() {
667
672
  # Accepted residual: a shebang script whose relative path resolves only from a
668
673
  # working directory this hook does not share is not followed.
669
674
  follow_direct() {
670
- local token="$1" depth="$2" path="" first=""
671
- if ! path="$(follow_locate "$token")"; then
675
+ local token="$1" depth="$2" path="" first="" raw="" char="" k=0
676
+ local LC_ALL=C
677
+ if ! follow_locate "$token"; then
672
678
  return 0
673
679
  fi
674
- first="$(LC_ALL=C head -c 256 -- "$path" 2>/dev/null | LC_ALL=C head -n 1 2>/dev/null | LC_ALL=C tr -dc '[:print:]' 2>/dev/null)" || first=""
680
+ path="$follow_located"
681
+ IFS= read -r -n 256 raw <"$path" 2>/dev/null || true
682
+ while [ "$k" -lt "${#raw}" ]; do
683
+ char="${raw:k:1}"
684
+ case "$char" in [[:print:]]) first="$first$char" ;; esac
685
+ k=$((k + 1))
686
+ done
675
687
  case "$first" in
676
688
  '#!'*[/[:space:]]bash* | '#!'*[/[:space:]]sh* | '#!'*[/[:space:]]zsh* \
677
689
  | '#!'*[/[:space:]]ksh* | '#!'*[/[:space:]]dash* | '#!'*[/[:space:]]ash*) ;;
@@ -701,7 +713,7 @@ follow_next_operand() {
701
713
  follow_scan() {
702
714
  local text="$1" depth="$2"
703
715
  local split_out line sep stmt t tj i j n cmd_pos eval_mode inline arg_exec
704
- local heredoc redirected pipe_cat prev_cat pending_operand
716
+ local heredoc redirected pipe_cat prev_cat pending_operand stdin_mode
705
717
  local find_mode dispatch_pending
706
718
  local -a toks=()
707
719
  split_out="$(follow_split "$text")"
@@ -888,6 +900,7 @@ follow_scan() {
888
900
  inline=0
889
901
  redirected=""
890
902
  heredoc=0
903
+ stdin_mode=0
891
904
  j=$((i + 1))
892
905
  while [ "$j" -lt "$n" ]; do
893
906
  follow_unwrap "${toks[j]}"
@@ -920,6 +933,7 @@ follow_scan() {
920
933
  ;;
921
934
  --*) j=$((j + 1)) ;;
922
935
  -*c*) inline=1; j=$((j + 1)) ;;
936
+ -s | -[a-zA-Z]*s[a-zA-Z]*) stdin_mode=1; j=$((j + 1)) ;;
923
937
  -*) j=$((j + 1)) ;;
924
938
  *) break ;;
925
939
  esac
@@ -944,6 +958,14 @@ follow_scan() {
944
958
  i="$j"
945
959
  continue
946
960
  fi
961
+ if [ "$stdin_mode" -eq 1 ]; then
962
+ # `sh -s -- -y` reads its program from stdin; tokens after `--` are
963
+ # positional arguments to that program, never a script path.
964
+ [ -z "$pipe_cat" ] || follow_target "$pipe_cat" "$depth"
965
+ cmd_pos=0
966
+ i="$n"
967
+ continue
968
+ fi
947
969
  if [ "$j" -ge "$n" ]; then
948
970
  # A dispatcher supplies the script from ANOTHER command's output, so
949
971
  # the operand exists but is unreadable here. That is the fail-closed
@@ -1806,15 +1828,14 @@ readonly SQL_TRUNCATE_EVIDENCE='\btruncate[[:space:]]+(table[[:space:]]+[[:alnum
1806
1828
  # the same command must also invoke a command-line database client. Prose in a
1807
1829
  # tracker filing or a commit message invokes none, which is exactly the
1808
1830
  # difference the text scan could not see on its own.
1809
- readonly SQL_CLIENT='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?(psql|pgcli|mysql|mysqladmin|mysqlsh|mariadb|mycli|sqlite3|sqlcmd|cqlsh|clickhouse-client|cockroach|mongosh|duckdb|usql)([[:space:]]|$)'
1810
- readonly SQL_TRUNCATE_BARE='\btruncate[[:space:]]+(only[[:space:]]+)?[[:alnum:]_."`]'
1831
+ readonly SQL_CLIENT_TRUNCATE='(^|[;&|()][[:space:]]*)([[:alnum:]_./-]*/)?(psql|pgcli|mysql|mysqladmin|mysqlsh|mariadb|mycli|sqlite3|sqlcmd|cqlsh|clickhouse-client|cockroach|mongosh|duckdb|usql)([[:space:]]+[^;&|()]*)?[[:space:]]+(-e|-c|--command|--eval)(=|[[:space:]]+)[^;&|()]*\btruncate[[:space:]]+(only[[:space:]]+)?[[:alnum:]_."`]'
1811
1832
  # Residual, accepted and stated: a bare `TRUNCATE <name>` with no terminator,
1812
1833
  # no qualification and no recognized client in the same command goes unmatched
1813
1834
  # — the same trade the `drop` arm has always made for `drop the ball`. No DROP
1814
1835
  # shape is weakened, and the guard still fails in the safe direction.
1815
1836
  if matches "$SQL_DROP" \
1816
1837
  || matches "$SQL_TRUNCATE_EVIDENCE" \
1817
- || { matches "$SQL_CLIENT" && matches "$SQL_TRUNCATE_BARE"; }; then
1838
+ || matches "$SQL_CLIENT_TRUNCATE"; then
1818
1839
  block "destructive SQL (DROP/TRUNCATE) detected"
1819
1840
  fi
1820
1841
 
package/package.json CHANGED
@@ -166,7 +166,7 @@
166
166
  "zod-validation-error": "^4.0.0"
167
167
  },
168
168
  "name": "@codyswann/lisa",
169
- "version": "4.34.21",
169
+ "version": "4.35.1",
170
170
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
171
171
  "main": "dist/index.js",
172
172
  "exports": {
@@ -314,7 +314,7 @@
314
314
  "test": "tests"
315
315
  },
316
316
  "types": "./dist/index.d.ts",
317
- "lisaReleaseCommit": "f65b16b98f562acb7a06c20b6e01a2cc6c37e200",
318
- "gitHead": "f65b16b98f562acb7a06c20b6e01a2cc6c37e200",
319
- "lisaReleaseTag": "v4.34.21"
317
+ "lisaReleaseCommit": "6fb4eaca043a1b6e8d96be3392dc4a42abd9ce11",
318
+ "gitHead": "6fb4eaca043a1b6e8d96be3392dc4a42abd9ce11",
319
+ "lisaReleaseTag": "v4.35.1"
320
320
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.34.21",
3
+ "version": "4.35.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.34.21",
3
+ "version": "4.35.1",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1426,7 +1426,11 @@ EXEC_WRAPPERS = {
1426
1426
  "nohup": (frozenset(), 0),
1427
1427
  "setsid": (frozenset(), 0),
1428
1428
  "stdbuf": (frozenset({"-i", "-o", "-e"}), 0),
1429
- "sudo": (frozenset({"-u", "--user", "-g", "--group", "-C", "--close-from"}), 0),
1429
+ "sudo": (frozenset({
1430
+ "-u", "--user", "-g", "--group", "-C", "--close-from",
1431
+ "-p", "--prompt", "-h", "--host", "-r", "--role",
1432
+ "-t", "--type", "-U", "--other-user",
1433
+ }), 0),
1430
1434
  "time": (frozenset(), 0),
1431
1435
  "timeout": (frozenset({"-k", "--kill-after", "-s", "--signal"}), 1),
1432
1436
  }
@@ -1447,6 +1451,31 @@ NO_SCRIPT_OPTIONS = {
1447
1451
  "ruby": {"-e"},
1448
1452
  }
1449
1453
 
1454
+ # Interpreter switches whose value is a separate token, followed by the real
1455
+ # script operand. Treating the value as the script makes the guard inspect a
1456
+ # preload or option value and never reach what the interpreter executes.
1457
+ INTERPRETER_VALUE_OPTIONS = {
1458
+ "bash": {"-o", "--option"},
1459
+ "dash": {"-o"},
1460
+ "deno": {"-c", "--config"},
1461
+ "ksh": {"-o"},
1462
+ "node": {"-r", "--require"},
1463
+ "nodejs": {"-r", "--require"},
1464
+ "sh": {"-o"},
1465
+ "zsh": {"-o"},
1466
+ }
1467
+
1468
+ # Runtime subcommands that introduce the script path rather than name it.
1469
+ INTERPRETER_SUBCOMMANDS = {
1470
+ "bun": {"run"},
1471
+ "deno": {"run"},
1472
+ }
1473
+
1474
+
1475
+ def is_assignment_word(token):
1476
+ """Whether token is a POSIX shell assignment in command-prefix position."""
1477
+ return re.match(r"^[A-Za-z_][A-Za-z0-9_]*\+?=", token) is not None
1478
+
1450
1479
  # Programs that READ their operands and never execute them as programs.
1451
1480
  #
1452
1481
  # This is an allowlist of readers rather than the inverse, and the asymmetry is
@@ -1501,7 +1530,7 @@ def executing_command(argv):
1501
1530
  index = 0
1502
1531
  while index < len(argv):
1503
1532
  token = argv[index]
1504
- if "=" in token and not token.startswith(("=", "-")):
1533
+ if is_assignment_word(token):
1505
1534
  index += 1
1506
1535
  continue
1507
1536
  program = basename(token)
@@ -1568,16 +1597,20 @@ def executed_operand(argv):
1568
1597
  # shebang — `./create.sh` names no interpreter and runs all the same.
1569
1598
  if program not in EXECUTING_INTERPRETERS and resolve_operand(token) is not None:
1570
1599
  return token
1571
- # `bash < create.sh` runs that file as surely as `bash create.sh` does, and
1572
- # `<` is not a segment boundary, so the pair survives in one argv.
1573
- for index, argument in enumerate(args):
1574
- if argument == "<" and index + 1 < len(args):
1575
- return args[index + 1]
1576
1600
  # Reached by a known interpreter AND by a program this parser cannot name.
1577
1601
  # The unknown case is followed on purpose — see READ_ONLY_PROGRAMS.
1578
- for argument in args:
1579
- if argument == "--":
1602
+ redirected = None
1603
+ index = 0
1604
+ while index < len(args):
1605
+ argument = args[index]
1606
+ if argument == "<":
1607
+ if index + 1 < len(args):
1608
+ redirected = args[index + 1]
1609
+ index += 2
1580
1610
  continue
1611
+ if argument == "--":
1612
+ index += 1
1613
+ return args[index] if index < len(args) else redirected
1581
1614
  if argument.startswith("-"):
1582
1615
  # A command string or a module name; no script file follows.
1583
1616
  head = argument.split("=", 1)[0]
@@ -1588,9 +1621,15 @@ def executed_operand(argv):
1588
1621
  return None
1589
1622
  elif head in NO_SCRIPT_OPTIONS.get(program, set()):
1590
1623
  return None
1624
+ takes_value = head in INTERPRETER_VALUE_OPTIONS.get(program, set())
1625
+ index += 2 if takes_value and "=" not in argument else 1
1626
+ continue
1627
+ if argument in INTERPRETER_SUBCOMMANDS.get(program, set()):
1628
+ index += 1
1591
1629
  continue
1592
1630
  return argument
1593
- return None
1631
+ # `bash < create.sh` runs stdin only when no explicit script was named.
1632
+ return redirected
1594
1633
 
1595
1634
 
1596
1635
  def file_operands(argv):
@@ -90,7 +90,7 @@
90
90
  # from mine", and guessing "behind" is how a fork's stronger guard gets silently
91
91
  # replaced by a weaker upstream one. Add a name here in the same commit that
92
92
  # closes a vector.
93
- # lisa-guard-capabilities: managed-path-resolution, lisaignore-precedence, generated-path-rebuild, redirect-target, tee-target, sed-in-place-all-spellings, executed-script-reach, source-builtin-reach, stdin-redirect-reach, wrapper-positional-operand, direct-script-command-reach, analyzer-failure-visible
93
+ # lisa-guard-capabilities: managed-path-resolution, lisaignore-precedence, generated-path-rebuild, redirect-target, tee-target, sed-in-place-all-spellings, executed-script-reach, source-builtin-reach, stdin-redirect-reach, wrapper-positional-operand, direct-script-command-reach, analyzer-failure-visible, apply-patch-parse-failure-visible, apply-patch-move-target, shell-command-string-reach
94
94
  set -euo pipefail
95
95
 
96
96
  input="$(cat)"
@@ -399,6 +399,7 @@ case "$tool_name" in
399
399
  # *** Add File: <path>
400
400
  # *** Update File: <path>
401
401
  # *** Delete File: <path>
402
+ # *** Move to: <path>
402
403
  #
403
404
  # So every header is walked and each target classified; one managed
404
405
  # target anywhere in a multi-file patch refuses the whole call, because
@@ -415,14 +416,25 @@ case "$tool_name" in
415
416
  # legitimately cannot be one file. What is NOT restated is the
416
417
  # classification: `managed_source` and `refuse` are the single copy here,
417
418
  # exactly as for every other arm.
418
- patch_text="$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null || true)"
419
+ if ! patch_text="$(printf '%s' "$input" | jq -er '
420
+ if ((.tool_input.command? // "") | type) == "string"
421
+ then (.tool_input.command? // "")
422
+ else error("tool_input.command must be a string")
423
+ end')"; then
424
+ printf 'block-managed-file-edits: could not parse the apply_patch command; managed-file protection refused the edit\n' >&2
425
+ exit 2
426
+ fi
419
427
  [ -n "$patch_text" ] || exit 0
420
428
  while IFS= read -r patch_line; do
421
429
  case "$patch_line" in
422
- "*** Add File: "* | "*** Update File: "* | "*** Delete File: "*) ;;
430
+ "*** Add File: "* | "*** Update File: "* | "*** Delete File: "*)
431
+ candidate="${patch_line#*File: }"
432
+ ;;
433
+ "*** Move to: "*)
434
+ candidate="${patch_line#*** Move to: }"
435
+ ;;
423
436
  *) continue ;;
424
437
  esac
425
- candidate="${patch_line#*File: }"
426
438
  [ -n "$candidate" ] || continue
427
439
  if source_path="$(managed_source "$candidate")"; then
428
440
  refuse "$candidate" "$source_path" "$(relative_path "$candidate")"
@@ -712,6 +724,29 @@ def shell_script_operand(args):
712
724
  return None
713
725
 
714
726
 
727
+ def shell_command_string(statement):
728
+ """The inline command a shell executes with `-c`, or None.
729
+
730
+ Args:
731
+ statement: One statement's tokens.
732
+
733
+ Returns:
734
+ The command string operand, or None when this is not a shell `-c` call.
735
+ """
736
+ _token, program, args = command_word(statement)
737
+ if program not in SHELL_PROGRAMS:
738
+ return None
739
+ for index, token in enumerate(args):
740
+ if token == "--":
741
+ return None
742
+ if token.startswith("-") and not token.startswith("--") and "c" in token[1:]:
743
+ command_index = index + 1
744
+ if command_index < len(args) and args[command_index] == "--":
745
+ command_index += 1
746
+ return args[command_index] if command_index < len(args) else None
747
+ return None
748
+
749
+
715
750
  def write_targets(statement):
716
751
  """Paths this statement WRITES.
717
752
 
@@ -834,6 +869,9 @@ def collect(text, depth, seen, out):
834
869
  if not statement:
835
870
  continue
836
871
  out.extend(write_targets(statement))
872
+ nested = shell_command_string(statement)
873
+ if nested is not None and depth < FOLLOW_MAX_DEPTH:
874
+ collect(nested, depth + 1, seen, out)
837
875
  operand = executed_script(statement)
838
876
  if operand is None:
839
877
  continue