@codyswann/lisa 3.12.0 → 3.13.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.
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +15 -2
- package/all/create-only/scripts/remote-agent-aws-setup.sh +3 -0
- package/cdk/create-only/.github/workflows/deploy.yml +2 -1
- package/cdk/create-only/vitest.config.local.ts +3 -0
- package/dist/cli/doctor-lisa-owned-artifacts.d.ts +11 -2
- package/dist/cli/doctor-lisa-owned-artifacts.d.ts.map +1 -1
- package/dist/cli/doctor-lisa-owned-artifacts.js +60 -15
- package/dist/cli/doctor-lisa-owned-artifacts.js.map +1 -1
- package/dist/core/config.d.ts +21 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +1 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/guard-capabilities.d.ts +60 -0
- package/dist/core/guard-capabilities.d.ts.map +1 -0
- package/dist/core/guard-capabilities.js +102 -0
- package/dist/core/guard-capabilities.js.map +1 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts +11 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -0
- package/dist/core/lisa-owned-hash-ledger.js +127 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -0
- package/dist/core/lisa-owned-provenance.d.ts +75 -0
- package/dist/core/lisa-owned-provenance.d.ts.map +1 -0
- package/dist/core/lisa-owned-provenance.js +127 -0
- package/dist/core/lisa-owned-provenance.js.map +1 -0
- package/dist/core/lisa.d.ts +18 -0
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +38 -0
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +59 -51
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/strategies/copy-overwrite.d.ts +28 -1
- package/dist/strategies/copy-overwrite.d.ts.map +1 -1
- package/dist/strategies/copy-overwrite.js +53 -3
- package/dist/strategies/copy-overwrite.js.map +1 -1
- package/dist/strategies/strategy.interface.d.ts +13 -0
- package/dist/strategies/strategy.interface.d.ts.map +1 -1
- package/expo/create-only/.github/workflows/ci.yml +3 -0
- package/expo/create-only/.github/workflows/deploy.yml +2 -1
- package/expo/create-only/.github/workflows/maestro-e2e.yml +3 -0
- package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +3 -0
- package/expo/create-only/.github/workflows/nightly-e2e-health.yml +3 -0
- package/expo/create-only/.github/workflows/nightly-e2e-report.yml +3 -0
- package/expo/create-only/babel.config.js +3 -0
- package/expo/create-only/jest.config.local.ts +3 -0
- package/expo/create-only/jest.config.react-native-mock.js +3 -0
- package/expo/create-only/jest.setup.local.ts +3 -0
- package/expo/create-only/jest.setup.pre.local.js +3 -0
- package/expo/create-only/scripts/zap-baseline.sh +3 -0
- package/harper-fabric/create-only/.github/workflows/deploy.yml +3 -0
- package/harper-fabric/create-only/.github/workflows/zap-baseline.yml +3 -0
- package/harper-fabric/create-only/scripts/zap-baseline.sh +3 -0
- package/nestjs/create-only/.github/workflows/deploy.yml +3 -0
- package/nestjs/create-only/scripts/zap-baseline.sh +3 -0
- package/nestjs/create-only/vitest.config.local.ts +3 -0
- package/npm-package/create-only/.github/workflows/publish-to-npm.yml +2 -1
- package/package.json +3 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/block-no-verify.sh +15 -2
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +15 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +15 -2
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/block-no-verify.sh +15 -2
- package/rails/create-only/.github/workflows/claude-code-review-response.yml +2 -1
- package/rails/create-only/.github/workflows/claude-nightly-code-complexity.yml +2 -1
- package/rails/create-only/.github/workflows/claude-nightly-test-coverage.yml +2 -1
- package/rails/create-only/.github/workflows/claude-nightly-test-improvement.yml +2 -1
- package/rails/create-only/.github/workflows/claude-sync-down-branches.yml +2 -1
- package/rails/create-only/.github/workflows/deploy.yml +2 -1
- package/rails/create-only/.mutant.yml +3 -0
- package/rails/create-only/.reek.yml +3 -0
- package/rails/create-only/.rubocop_todo.yml +3 -0
- package/rails/create-only/mutation.gate.yml +3 -0
- package/rails/create-only/rubocop.thresholds.yml +3 -0
- package/rails/create-only/spec/rails_helper.rb +2 -0
- package/rails/create-only/spec/spec_helper.rb +2 -0
- package/scripts/generate-lisa-owned-hash-ledger.mjs +226 -0
- package/typescript/create-only/.github/workflows/auto-update-pr-branches-dispatch.yml +2 -1
- package/typescript/create-only/.github/workflows/auto-update-pr-branches.yml +2 -1
- package/typescript/create-only/.github/workflows/claude-ci-auto-fix.yml +2 -1
- package/typescript/create-only/.github/workflows/claude-code-review-response.yml +2 -1
- package/typescript/create-only/.github/workflows/claude-deploy-auto-fix.yml +2 -1
- package/typescript/create-only/.github/workflows/claude-nightly-code-complexity.yml +2 -1
- package/typescript/create-only/.github/workflows/claude-nightly-test-coverage.yml +2 -1
- package/typescript/create-only/.github/workflows/claude-nightly-test-improvement.yml +2 -1
- package/typescript/create-only/.github/workflows/claude-sync-down-branches.yml +2 -1
- package/typescript/create-only/.github/workflows/claude.yml +2 -1
- package/typescript/create-only/.github/workflows/required-checks-drift.yml +2 -1
- package/typescript/create-only/eslint.config.local.ts +3 -0
- package/typescript/create-only/jest.config.local.ts +3 -0
- package/typescript/create-only/vitest.config.local.ts +3 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -5,9 +5,22 @@
|
|
|
5
5
|
# check. See feedback_never_no_verify in user memory.
|
|
6
6
|
#
|
|
7
7
|
# Blocked bypass vectors:
|
|
8
|
-
# 1.
|
|
8
|
+
# 1. --no-verify, and any prefix abbreviation git would accept for it;
|
|
9
9
|
# 2. HUSKY=0 / HUSKY_SKIP_HOOKS=... — disables husky-managed git hooks;
|
|
10
|
-
# 3. core.hooksPath pointed
|
|
10
|
+
# 3. core.hooksPath pointed anywhere but an allowlisted in-repo hooks dir;
|
|
11
|
+
# 4. --config-env=core.hooksPath=VAR, in both the one- and two-token spellings;
|
|
12
|
+
# 5. GIT_CONFIG_KEY_<n>=core.hooksPath env-var-style command-scope config.
|
|
13
|
+
#
|
|
14
|
+
# The line below is what lets `lisa apply` tell a downstream copy of this guard
|
|
15
|
+
# that is BEHIND from one that is AHEAD. Byte comparison cannot: both look like
|
|
16
|
+
# "differs from mine", and guessing "behind" is how a fork's stronger guard gets
|
|
17
|
+
# silently replaced by a weaker upstream one. Refresh compares the two declared
|
|
18
|
+
# sets, and refuses to overwrite a copy declaring anything this one does not.
|
|
19
|
+
#
|
|
20
|
+
# Add a name here in the same commit that closes a vector. A hardening that
|
|
21
|
+
# forgets to is invisible to refresh, and shows up as an unexplained diff at
|
|
22
|
+
# review time instead of a named capability.
|
|
23
|
+
# lisa-guard-capabilities: no-verify-abbrev, husky-env, hookspath-allowlist, config-env, git-config-key
|
|
11
24
|
#
|
|
12
25
|
# Shell-token matching avoids false positives from issue bodies, heredocs, and
|
|
13
26
|
# commit-message prose while still catching quoted real argv values such as
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
2
3
|
# You can customize this file — Lisa will not overwrite it.
|
|
3
4
|
|
|
4
5
|
name: Claude Code Review Response
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
2
3
|
# You can customize this file — Lisa will not overwrite it.
|
|
3
4
|
|
|
4
5
|
name: Claude Nightly Code Complexity
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
2
3
|
# You can customize this file — Lisa will not overwrite it.
|
|
3
4
|
|
|
4
5
|
name: Claude Nightly Test Coverage
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
2
3
|
# You can customize this file — Lisa will not overwrite it.
|
|
3
4
|
|
|
4
5
|
name: Claude Nightly Test Improvement
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
2
3
|
# You can customize this file — Lisa will not overwrite it.
|
|
3
4
|
#
|
|
4
5
|
# Back-syncs merges down the env chain so hotfixes propagate.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
2
3
|
# Release + Deploy workflow for Rails on AWS ECS Fargate.
|
|
3
4
|
#
|
|
4
5
|
# Step 1: Call the reusable release workflow for version bumping and tagging.
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
3
|
+
|
|
1
4
|
# This is the standard RuboCop "fix later" file.
|
|
2
5
|
# Lisa creates it empty so .rubocop.yml's `inherit_from` resolves cleanly
|
|
3
6
|
# in projects that don't yet have legacy violations to track.
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
3
|
+
|
|
1
4
|
# Mutation-testing gate toggle (project-owned).
|
|
2
5
|
# Flip `enabled` to true to turn on the diff-only mutant gate in the pre-push
|
|
3
6
|
# hook and CI. The mutation subjects are configured in `.mutant.yml`.
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
3
|
+
|
|
1
4
|
# RuboCop complexity thresholds — nightly workflows adjust these programmatically.
|
|
2
5
|
# This file is create-only: Lisa won't overwrite it after initial setup.
|
|
3
6
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate the ledger of every content hash Lisa has ever shipped at each
|
|
4
|
+
* Lisa-owned destination path.
|
|
5
|
+
*
|
|
6
|
+
* Refresh uses it to tell a host copy that is *behind* Lisa (its bytes are some
|
|
7
|
+
* past Lisa release, so overwriting is correct) from one that is *ahead* (its
|
|
8
|
+
* bytes match no Lisa release, so overwriting would delete downstream work).
|
|
9
|
+
*
|
|
10
|
+
* Two properties are load-bearing:
|
|
11
|
+
*
|
|
12
|
+
* - **Keyed by destination, not source.** Several stacks ship a variant of the
|
|
13
|
+
* same installed path; a host has exactly one of them and refresh must accept
|
|
14
|
+
* whichever it has. The union across stacks is the honest answer to "has Lisa
|
|
15
|
+
* ever shipped these bytes here?".
|
|
16
|
+
* - **Append-only.** Existing entries are unioned in, never dropped. That makes
|
|
17
|
+
* the generator safe under a shallow clone: less history yields fewer new
|
|
18
|
+
* hashes, never the loss of known-good ones. Losing a hash would reclassify a
|
|
19
|
+
* legitimately stale file as host-modified and stop refreshing it — failing
|
|
20
|
+
* safe, but permanently.
|
|
21
|
+
*/
|
|
22
|
+
import { execFileSync } from "node:child_process";
|
|
23
|
+
import { createHash } from "node:crypto";
|
|
24
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
|
|
27
|
+
const repoRoot = path.resolve(import.meta.dirname, "..");
|
|
28
|
+
const outputPath = path.join(repoRoot, "src/core/lisa-owned-hash-ledger.ts");
|
|
29
|
+
const COPY_OVERWRITE = "copy-overwrite";
|
|
30
|
+
const LISA_NAMESPACE_PREFIX = "lisa-";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Run a git command in the repository and return stdout as latin1 text.
|
|
34
|
+
*
|
|
35
|
+
* Blobs are read as `binary` so arbitrary bytes survive the round trip into the
|
|
36
|
+
* Buffer that gets hashed; decoding as UTF-8 would corrupt any non-text file.
|
|
37
|
+
* @param {readonly string[]} args - Arguments passed to git
|
|
38
|
+
* @returns {string} Command stdout, or an empty string when git fails
|
|
39
|
+
*/
|
|
40
|
+
function git(args) {
|
|
41
|
+
try {
|
|
42
|
+
return execFileSync("git", args, {
|
|
43
|
+
cwd: repoRoot,
|
|
44
|
+
encoding: "buffer",
|
|
45
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
46
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
47
|
+
}).toString("binary");
|
|
48
|
+
} catch {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether a destination path sits in the namespace Lisa owns outright.
|
|
55
|
+
*
|
|
56
|
+
* Mirrors `isLisaOwnedTemplate` in `src/core/lisa-owned-templates.ts`. The
|
|
57
|
+
* duplication is deliberate — this script runs before the TypeScript build, so
|
|
58
|
+
* it cannot import the compiled module — and a test asserts the two agree.
|
|
59
|
+
*
|
|
60
|
+
* This is also the ledger's boundary, and it is inherited rather than chosen.
|
|
61
|
+
* The `lisa-` path segment is what decides which files refresh at all, so a file
|
|
62
|
+
* outside it is never refreshed after its initial create and has nothing for
|
|
63
|
+
* provenance to decide — recording hashes for it would add a regeneration gate
|
|
64
|
+
* to ~120 more templates and buy nothing today. If that predicate is ever
|
|
65
|
+
* replaced (CodySwannGT/lisa#2491 tracks it freezing non-`lisa-` guards at
|
|
66
|
+
* whatever version first landed), widening enrolment is this one function: the
|
|
67
|
+
* ledger answers "has Lisa ever shipped these bytes here?" for any managed path.
|
|
68
|
+
* @param {string} destination - Repo-relative destination path
|
|
69
|
+
* @returns {boolean} True when Lisa owns the file outright
|
|
70
|
+
*/
|
|
71
|
+
function isLisaOwned(destination) {
|
|
72
|
+
return destination
|
|
73
|
+
.split("/")
|
|
74
|
+
.some(segment => segment.startsWith(LISA_NAMESPACE_PREFIX));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Every tracked copy-overwrite source that installs a Lisa-owned file.
|
|
79
|
+
* @returns {Map<string, string[]>} Destination path to the source paths producing it
|
|
80
|
+
*/
|
|
81
|
+
function lisaOwnedSources() {
|
|
82
|
+
const byDestination = new Map();
|
|
83
|
+
const marker = `/${COPY_OVERWRITE}/`;
|
|
84
|
+
for (const tracked of git(["ls-files"]).split("\n")) {
|
|
85
|
+
const at = tracked.indexOf(marker);
|
|
86
|
+
if (at === -1) continue;
|
|
87
|
+
const destination = tracked.slice(at + marker.length);
|
|
88
|
+
if (!isLisaOwned(destination)) continue;
|
|
89
|
+
const sources = byDestination.get(destination) ?? [];
|
|
90
|
+
sources.push(tracked);
|
|
91
|
+
byDestination.set(destination, sources);
|
|
92
|
+
}
|
|
93
|
+
return byDestination;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Hex sha256 of a blob's bytes.
|
|
98
|
+
* @param {Buffer} bytes - Blob contents
|
|
99
|
+
* @returns {string} Lower-case hex digest
|
|
100
|
+
*/
|
|
101
|
+
function digest(bytes) {
|
|
102
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Every hash this source path has ever had, across all of its history.
|
|
107
|
+
*
|
|
108
|
+
* `--follow` keeps a renamed guard's earlier hashes, which is exactly the case
|
|
109
|
+
* where a host is most likely to be running an older copy.
|
|
110
|
+
* @param {string} source - Repo-relative source path
|
|
111
|
+
* @returns {string[]} Hashes of every historical revision plus the checked-in copy
|
|
112
|
+
*/
|
|
113
|
+
function historicalHashes(source) {
|
|
114
|
+
const hashes = [];
|
|
115
|
+
const current = git(["show", `HEAD:${source}`]);
|
|
116
|
+
if (current !== "") hashes.push(digest(Buffer.from(current, "binary")));
|
|
117
|
+
|
|
118
|
+
// `--name-only` is what makes rename-following actually work. Asking for the
|
|
119
|
+
// commit list alone and then reading `<rev>:<source>` fails for every revision
|
|
120
|
+
// before a rename, because the file did not live at that path yet — the very
|
|
121
|
+
// revisions a long-installed host is most likely to be running.
|
|
122
|
+
const log = git([
|
|
123
|
+
"log",
|
|
124
|
+
"--format=%H",
|
|
125
|
+
"--follow",
|
|
126
|
+
"--name-only",
|
|
127
|
+
"--",
|
|
128
|
+
source,
|
|
129
|
+
]).split("\n");
|
|
130
|
+
let revision = "";
|
|
131
|
+
for (const line of log) {
|
|
132
|
+
if (/^[0-9a-f]{40}$/.test(line)) {
|
|
133
|
+
revision = line;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (line === "" || revision === "") continue;
|
|
137
|
+
const blob = git(["show", `${revision}:${line}`]);
|
|
138
|
+
if (blob !== "") hashes.push(digest(Buffer.from(blob, "binary")));
|
|
139
|
+
}
|
|
140
|
+
return hashes;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Hashes already recorded in the checked-in ledger, so none is ever lost.
|
|
145
|
+
* @returns {Map<string, string[]>} Destination path to previously recorded hashes
|
|
146
|
+
*/
|
|
147
|
+
function existingLedger() {
|
|
148
|
+
const previous = new Map();
|
|
149
|
+
let source = "";
|
|
150
|
+
try {
|
|
151
|
+
source = readFileSync(outputPath, "utf8");
|
|
152
|
+
} catch {
|
|
153
|
+
return previous;
|
|
154
|
+
}
|
|
155
|
+
const entry = /"([^"]+)": Object\.freeze\(\[([^\]]*)\]\)/g;
|
|
156
|
+
for (const match of source.matchAll(entry)) {
|
|
157
|
+
previous.set(
|
|
158
|
+
match[1],
|
|
159
|
+
[...match[2].matchAll(/"([0-9a-f]{64})"/g)].map(hash => hash[1])
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
return previous;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Render the ledger module.
|
|
167
|
+
* @param {Map<string, string[]>} ledger - Destination path to known-good hashes
|
|
168
|
+
* @returns {string} TypeScript module source
|
|
169
|
+
*/
|
|
170
|
+
function render(ledger) {
|
|
171
|
+
const entries = [...ledger.entries()]
|
|
172
|
+
.sort(([left], [right]) => (left < right ? -1 : 1))
|
|
173
|
+
// Indentation matches what Prettier produces. `lint-staged` reformats every
|
|
174
|
+
// staged file, so a generator whose output Prettier would reindent leaves
|
|
175
|
+
// `--check` failing immediately after the commit that regenerated it.
|
|
176
|
+
.map(([destination, hashes]) => {
|
|
177
|
+
const rendered = hashes.map(hash => ` "${hash}",`).join("\n");
|
|
178
|
+
return ` "${destination}": Object.freeze([\n${rendered}\n ]),`;
|
|
179
|
+
})
|
|
180
|
+
.join("\n");
|
|
181
|
+
return `/** Generated by scripts/generate-lisa-owned-hash-ledger.mjs. Do not edit. */
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Every content hash Lisa has published at each Lisa-owned destination path.
|
|
185
|
+
*
|
|
186
|
+
* A host copy whose hash appears here is provably an older Lisa artifact and may
|
|
187
|
+
* be refreshed. One that appears nowhere here was edited downstream, and refresh
|
|
188
|
+
* refuses rather than risk deleting a stronger guard. See
|
|
189
|
+
* \`src/core/lisa-owned-provenance.ts\`.
|
|
190
|
+
*/
|
|
191
|
+
export const LISA_OWNED_HASH_LEDGER: Readonly<
|
|
192
|
+
Record<string, readonly string[]>
|
|
193
|
+
> = Object.freeze({
|
|
194
|
+
${entries}
|
|
195
|
+
});
|
|
196
|
+
`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const ledger = new Map(existingLedger());
|
|
200
|
+
for (const [destination, sources] of lisaOwnedSources()) {
|
|
201
|
+
const known = new Set(ledger.get(destination) ?? []);
|
|
202
|
+
for (const source of sources) {
|
|
203
|
+
for (const hash of historicalHashes(source)) known.add(hash);
|
|
204
|
+
}
|
|
205
|
+
ledger.set(destination, [...known].sort());
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const rendered = render(ledger);
|
|
209
|
+
if (process.argv.includes("--check")) {
|
|
210
|
+
let actual = "";
|
|
211
|
+
try {
|
|
212
|
+
actual = readFileSync(outputPath, "utf8");
|
|
213
|
+
} catch {
|
|
214
|
+
actual = "";
|
|
215
|
+
}
|
|
216
|
+
if (actual !== rendered) {
|
|
217
|
+
process.stderr.write(
|
|
218
|
+
"Lisa-owned hash ledger is out of date. Run `bun run build:lisa-owned-hash-ledger` and commit the result.\n"
|
|
219
|
+
);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
process.stdout.write("Lisa-owned hash ledger is current.\n");
|
|
223
|
+
} else {
|
|
224
|
+
writeFileSync(outputPath, rendered);
|
|
225
|
+
process.stdout.write(`Wrote ${ledger.size} ledger entries.\n`);
|
|
226
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seeded by Lisa on first setup — this file is YOURS.
|
|
2
|
+
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
2
3
|
# You can customize this file — Lisa will not overwrite it.
|
|
3
4
|
|
|
4
5
|
name: Auto-update PR branches (Dispatch Handler)
|