@codyswann/lisa 3.17.2 → 3.17.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 (66) hide show
  1. package/all/copy-overwrite/scripts/lisa-destructive-guard.mjs +30 -1
  2. package/all/copy-overwrite/scripts/lisa-work-item.mjs +41 -2
  3. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  4. package/dist/core/lisa-owned-hash-ledger.js +3 -0
  5. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  7. package/dist/core/upstream-evidence-manifest.js +9 -6
  8. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  9. package/package.json +2 -2
  10. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  12. package/plugins/lisa/hooks/threshold-ratchet.mjs +44 -4
  13. package/plugins/lisa-agy/plugin.json +1 -1
  14. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  17. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-copilot/hooks/threshold-ratchet.mjs +44 -4
  21. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-cursor/hooks/threshold-ratchet.mjs +44 -4
  23. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-expo-agy/plugin.json +1 -1
  26. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  31. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  36. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  41. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  46. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-rails-agy/plugin.json +1 -1
  51. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  56. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  61. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/src/base/hooks/threshold-ratchet.mjs +44 -4
  64. package/rails/copy-overwrite/scripts/check-threshold-ratchet.mjs +44 -4
  65. package/typescript/copy-contents/.husky/commit-msg +10 -3
  66. package/typescript/copy-overwrite/scripts/check-threshold-ratchet.mjs +44 -4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.17.2",
3
+ "version": "3.17.3",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.17.2",
3
+ "version": "3.17.3",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -430,9 +430,49 @@ function main() {
430
430
  return 0;
431
431
  }
432
432
 
433
- const isDirectRun =
434
- process.argv[1] &&
435
- fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
436
- if (isDirectRun) {
433
+ /**
434
+ * True when `moduleUrl` names the module node was asked to run.
435
+ *
436
+ * The one implementation of this lives at `scripts/lib/invoked-as-script.mjs`,
437
+ * and every other shipped entry point imports it. This file cannot: it is
438
+ * materialized from `plugins/src/base/hooks/`, where it also runs as an
439
+ * agent-time hook, and a plugin payload has no `./lib/` to import from. So the
440
+ * rule is written out here rather than pointed at — the same accommodation
441
+ * `preflight-secrets.mjs` makes, for the same reason.
442
+ *
443
+ * Both sides are realpath'd. The previous spelling compared
444
+ * `fileURLToPath(import.meta.url)` against `path.resolve(process.argv[1])`, and
445
+ * `resolve` makes a path absolute without following symlinks. `import.meta.url`
446
+ * is the REAL path — node resolves ESM through realpath unless
447
+ * `--preserve-symlinks` or `--preserve-symlinks-main` is set — while `argv[1]`
448
+ * is whatever the caller typed. Reached through a symlinked checkout, a git
449
+ * worktree, or a `/tmp` path on macOS (`/tmp` being a symlink to `/private/tmp`),
450
+ * the two disagreed, `main()` never ran, and the process exited 0.
451
+ *
452
+ * For a CHECK that is a fail-OPEN, not a mild degradation: no output, exit 0,
453
+ * the npm script "succeeds", and the ratchet silently stops having an opinion.
454
+ * It was measured, not theorised — every control in the #2531 promotion tests
455
+ * no-opped until the fixture directory was realpath'd.
456
+ *
457
+ * Realpathing BOTH sides rather than only `argv[1]` matters under
458
+ * `--preserve-symlinks-main`, which tells node not to resolve the main entry:
459
+ * normalizing one side then compares a real path against a symlinked one and
460
+ * answers `false` for an entry point that WAS invoked directly. Any resolution
461
+ * error returns `false` — node loaded the entry from that path moments ago, so
462
+ * a path that will not resolve now is not the path this module came through.
463
+ * @param {string} moduleUrl - The caller's own `import.meta.url`.
464
+ * @param {string | undefined} [argv1] - Entry path; defaults to `process.argv[1]`.
465
+ * @returns {boolean} Whether the caller should run its CLI body.
466
+ */
467
+ export function invokedAsScript(moduleUrl, argv1 = process.argv[1]) {
468
+ if (!argv1) return false;
469
+ try {
470
+ return fs.realpathSync(argv1) === fs.realpathSync(fileURLToPath(moduleUrl));
471
+ } catch {
472
+ return false;
473
+ }
474
+ }
475
+
476
+ if (invokedAsScript(import.meta.url)) {
437
477
  process.exit(main());
438
478
  }
@@ -433,9 +433,49 @@ function main() {
433
433
  return 0;
434
434
  }
435
435
 
436
- const isDirectRun =
437
- process.argv[1] &&
438
- fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
439
- if (isDirectRun) {
436
+ /**
437
+ * True when `moduleUrl` names the module node was asked to run.
438
+ *
439
+ * The one implementation of this lives at `scripts/lib/invoked-as-script.mjs`,
440
+ * and every other shipped entry point imports it. This file cannot: it is
441
+ * materialized from `plugins/src/base/hooks/`, where it also runs as an
442
+ * agent-time hook, and a plugin payload has no `./lib/` to import from. So the
443
+ * rule is written out here rather than pointed at — the same accommodation
444
+ * `preflight-secrets.mjs` makes, for the same reason.
445
+ *
446
+ * Both sides are realpath'd. The previous spelling compared
447
+ * `fileURLToPath(import.meta.url)` against `path.resolve(process.argv[1])`, and
448
+ * `resolve` makes a path absolute without following symlinks. `import.meta.url`
449
+ * is the REAL path — node resolves ESM through realpath unless
450
+ * `--preserve-symlinks` or `--preserve-symlinks-main` is set — while `argv[1]`
451
+ * is whatever the caller typed. Reached through a symlinked checkout, a git
452
+ * worktree, or a `/tmp` path on macOS (`/tmp` being a symlink to `/private/tmp`),
453
+ * the two disagreed, `main()` never ran, and the process exited 0.
454
+ *
455
+ * For a CHECK that is a fail-OPEN, not a mild degradation: no output, exit 0,
456
+ * the npm script "succeeds", and the ratchet silently stops having an opinion.
457
+ * It was measured, not theorised — every control in the #2531 promotion tests
458
+ * no-opped until the fixture directory was realpath'd.
459
+ *
460
+ * Realpathing BOTH sides rather than only `argv[1]` matters under
461
+ * `--preserve-symlinks-main`, which tells node not to resolve the main entry:
462
+ * normalizing one side then compares a real path against a symlinked one and
463
+ * answers `false` for an entry point that WAS invoked directly. Any resolution
464
+ * error returns `false` — node loaded the entry from that path moments ago, so
465
+ * a path that will not resolve now is not the path this module came through.
466
+ * @param {string} moduleUrl - The caller's own `import.meta.url`.
467
+ * @param {string | undefined} [argv1] - Entry path; defaults to `process.argv[1]`.
468
+ * @returns {boolean} Whether the caller should run its CLI body.
469
+ */
470
+ export function invokedAsScript(moduleUrl, argv1 = process.argv[1]) {
471
+ if (!argv1) return false;
472
+ try {
473
+ return fs.realpathSync(argv1) === fs.realpathSync(fileURLToPath(moduleUrl));
474
+ } catch {
475
+ return false;
476
+ }
477
+ }
478
+
479
+ if (invokedAsScript(import.meta.url)) {
440
480
  process.exit(main());
441
481
  }
@@ -54,15 +54,22 @@ fi
54
54
 
55
55
  # Check for Co-Authored-By line (skip for merge commits)
56
56
  COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
57
+ # `printf '%s\n'`, never `echo "$COMMIT_MSG"`. Under a POSIX/XSI `echo` the
58
+ # backslash escapes are interpreted, and `\c` means "stop output here" — so a
59
+ # message containing `\copy`, `\cite`, or a Windows path like `C:\config` is
60
+ # silently truncated at that point before grep ever sees it. The
61
+ # `Co-Authored-By:` trailer is always the last line, so it is always the part
62
+ # lost, and the hook then rejects a commit whose trailer is present and
63
+ # correctly formatted while pointing the author at the trailer.
57
64
 
58
65
  # Skip AI co-authorship check for merge commits (e.g., from git pull)
59
- if echo "$COMMIT_MSG" | grep -qE "^Merge (branch|pull request|remote-tracking)"; then
66
+ if printf '%s\n' "$COMMIT_MSG" | grep -qE "^Merge (branch|pull request|remote-tracking)"; then
60
67
  echo "🔀 Merge commit detected, skipping AI co-authorship check"
61
68
  exit 0
62
69
  fi
63
70
 
64
71
  # Skip AI co-authorship check for automated release commits (e.g., from standard-version in CI)
65
- if echo "$COMMIT_MSG" | grep -qE "^chore\(release\):"; then
72
+ if printf '%s\n' "$COMMIT_MSG" | grep -qE "^chore\(release\):"; then
66
73
  echo "🚀 Release commit detected, skipping AI co-authorship check"
67
74
  exit 0
68
75
  fi
@@ -72,7 +79,7 @@ echo "🤖 Checking for AI co-authorship..."
72
79
  # Accept attribution from any supported coding agent (Claude or Codex),
73
80
  # case-insensitively. Claude Code emits "Co-Authored-By: Claude"; the Codex CLI
74
81
  # emits "Co-authored-by: Codex <noreply@openai.com>".
75
- if ! echo "$COMMIT_MSG" | grep -Eiq "Co-authored-by:.*(Claude|Codex)"; then
82
+ if ! printf '%s\n' "$COMMIT_MSG" | grep -Eiq "Co-authored-by:.*(Claude|Codex)"; then
76
83
  echo ""
77
84
  echo "❌ Commit message must include AI co-authorship!"
78
85
  echo ""
@@ -433,9 +433,49 @@ function main() {
433
433
  return 0;
434
434
  }
435
435
 
436
- const isDirectRun =
437
- process.argv[1] &&
438
- fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
439
- if (isDirectRun) {
436
+ /**
437
+ * True when `moduleUrl` names the module node was asked to run.
438
+ *
439
+ * The one implementation of this lives at `scripts/lib/invoked-as-script.mjs`,
440
+ * and every other shipped entry point imports it. This file cannot: it is
441
+ * materialized from `plugins/src/base/hooks/`, where it also runs as an
442
+ * agent-time hook, and a plugin payload has no `./lib/` to import from. So the
443
+ * rule is written out here rather than pointed at — the same accommodation
444
+ * `preflight-secrets.mjs` makes, for the same reason.
445
+ *
446
+ * Both sides are realpath'd. The previous spelling compared
447
+ * `fileURLToPath(import.meta.url)` against `path.resolve(process.argv[1])`, and
448
+ * `resolve` makes a path absolute without following symlinks. `import.meta.url`
449
+ * is the REAL path — node resolves ESM through realpath unless
450
+ * `--preserve-symlinks` or `--preserve-symlinks-main` is set — while `argv[1]`
451
+ * is whatever the caller typed. Reached through a symlinked checkout, a git
452
+ * worktree, or a `/tmp` path on macOS (`/tmp` being a symlink to `/private/tmp`),
453
+ * the two disagreed, `main()` never ran, and the process exited 0.
454
+ *
455
+ * For a CHECK that is a fail-OPEN, not a mild degradation: no output, exit 0,
456
+ * the npm script "succeeds", and the ratchet silently stops having an opinion.
457
+ * It was measured, not theorised — every control in the #2531 promotion tests
458
+ * no-opped until the fixture directory was realpath'd.
459
+ *
460
+ * Realpathing BOTH sides rather than only `argv[1]` matters under
461
+ * `--preserve-symlinks-main`, which tells node not to resolve the main entry:
462
+ * normalizing one side then compares a real path against a symlinked one and
463
+ * answers `false` for an entry point that WAS invoked directly. Any resolution
464
+ * error returns `false` — node loaded the entry from that path moments ago, so
465
+ * a path that will not resolve now is not the path this module came through.
466
+ * @param {string} moduleUrl - The caller's own `import.meta.url`.
467
+ * @param {string | undefined} [argv1] - Entry path; defaults to `process.argv[1]`.
468
+ * @returns {boolean} Whether the caller should run its CLI body.
469
+ */
470
+ export function invokedAsScript(moduleUrl, argv1 = process.argv[1]) {
471
+ if (!argv1) return false;
472
+ try {
473
+ return fs.realpathSync(argv1) === fs.realpathSync(fileURLToPath(moduleUrl));
474
+ } catch {
475
+ return false;
476
+ }
477
+ }
478
+
479
+ if (invokedAsScript(import.meta.url)) {
440
480
  process.exit(main());
441
481
  }