@codyswann/lisa 2.163.3 → 2.163.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +35 -11
- package/dist/cli/apply.js.map +1 -1
- package/dist/codex/scripts/block-no-verify.sh +63 -9
- package/dist/core/bootstrap-environment.d.ts +32 -0
- package/dist/core/bootstrap-environment.d.ts.map +1 -0
- package/dist/core/bootstrap-environment.js +51 -0
- package/dist/core/bootstrap-environment.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/package.json +1 -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.agy.sh +62 -13
- package/plugins/lisa/hooks/block-no-verify.sh +62 -10
- package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +62 -13
- 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 +62 -10
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +62 -10
- 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.agy.sh +62 -13
- package/plugins/src/base/hooks/block-no-verify.sh +62 -10
package/dist/cli/apply.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/cli/apply.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/cli/apply.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,UAAU,EAAsB,MAAM,qBAAqB,CAAC;AAgG1E;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAiEf"}
|
package/dist/cli/apply.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { getBootstrapApplySkipNotice } from "../core/bootstrap-environment.js";
|
|
3
4
|
import { ACCEPTED_HARNESS_INPUTS } from "../core/config.js";
|
|
4
5
|
import { Lisa } from "../core/lisa.js";
|
|
5
6
|
import { projectConfigExists, readProjectConfig, resolveHarness, shouldPersistProjectConfig, writeProjectConfig, } from "../core/project-config.js";
|
|
@@ -42,6 +43,28 @@ function printUsageAndExit() {
|
|
|
42
43
|
console.log(" lisa --harness=all . # Emit for every agent (alias for fleet)");
|
|
43
44
|
process.exit(1);
|
|
44
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the required destination argument or print the legacy usage error.
|
|
48
|
+
* @param destination - Destination argument from the CLI
|
|
49
|
+
* @returns Absolute destination path
|
|
50
|
+
*/
|
|
51
|
+
function resolveDestinationOrExit(destination) {
|
|
52
|
+
if (!destination) {
|
|
53
|
+
printUsageAndExit();
|
|
54
|
+
}
|
|
55
|
+
return toAbsolutePath(destination);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Persist the resolved harness when a real apply needs to backfill or update
|
|
59
|
+
* `.lisa.config.json`.
|
|
60
|
+
* @param destDir - Destination project directory
|
|
61
|
+
* @param input - Existing config state and resolved harness values
|
|
62
|
+
*/
|
|
63
|
+
async function persistProjectConfigIfNeeded(destDir, input) {
|
|
64
|
+
if (!shouldPersistProjectConfig(input))
|
|
65
|
+
return;
|
|
66
|
+
await writeProjectConfig(destDir, { harness: input.resolvedHarness });
|
|
67
|
+
}
|
|
45
68
|
/**
|
|
46
69
|
* Apply Lisa to the given destination with the given options.
|
|
47
70
|
*
|
|
@@ -54,12 +77,16 @@ function printUsageAndExit() {
|
|
|
54
77
|
* @returns Promise that completes when Lisa finishes
|
|
55
78
|
*/
|
|
56
79
|
export async function runApply(destination, options) {
|
|
57
|
-
if (!destination) {
|
|
58
|
-
printUsageAndExit();
|
|
59
|
-
}
|
|
60
80
|
const dryRun = options.dryRun ?? options.validate ?? false;
|
|
61
81
|
const yesMode = options.yes ?? false;
|
|
62
|
-
const
|
|
82
|
+
const validateOnly = options.validate ?? false;
|
|
83
|
+
const destDir = resolveDestinationOrExit(destination);
|
|
84
|
+
const logger = new ConsoleLogger();
|
|
85
|
+
const skipNotice = getBootstrapApplySkipNotice({ validateOnly });
|
|
86
|
+
if (skipNotice !== undefined) {
|
|
87
|
+
console.log(skipNotice);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
63
90
|
// Resolve harness with precedence: CLI flag > .lisa.config.json > default
|
|
64
91
|
const projectConfig = await readProjectConfig(destDir);
|
|
65
92
|
const configFileExists = await projectConfigExists(destDir);
|
|
@@ -69,11 +96,10 @@ export async function runApply(destination, options) {
|
|
|
69
96
|
destDir,
|
|
70
97
|
dryRun,
|
|
71
98
|
yesMode,
|
|
72
|
-
validateOnly
|
|
99
|
+
validateOnly,
|
|
73
100
|
skipGitCheck: options.skipGitCheck ?? false,
|
|
74
101
|
harness,
|
|
75
102
|
};
|
|
76
|
-
const logger = new ConsoleLogger();
|
|
77
103
|
const deps = createDependencies(dryRun, yesMode, logger);
|
|
78
104
|
const lisa = new Lisa(config, deps);
|
|
79
105
|
try {
|
|
@@ -89,15 +115,13 @@ export async function runApply(destination, options) {
|
|
|
89
115
|
// resolved harness (the default when no --harness was passed) so no
|
|
90
116
|
// project is left config-less; an existing file is only rewritten when
|
|
91
117
|
// --harness actually changes the persisted value, avoiding churn.
|
|
92
|
-
if (!options.validate &&
|
|
93
|
-
|
|
94
|
-
shouldPersistProjectConfig({
|
|
118
|
+
if (!options.validate && !dryRun) {
|
|
119
|
+
await persistProjectConfigIfNeeded(destDir, {
|
|
95
120
|
fileExists: configFileExists,
|
|
96
121
|
flagHarness: options.harness,
|
|
97
122
|
existingHarness: projectConfig.harness,
|
|
98
123
|
resolvedHarness: harness,
|
|
99
|
-
})
|
|
100
|
-
await writeProjectConfig(destDir, { harness });
|
|
124
|
+
});
|
|
101
125
|
}
|
|
102
126
|
// After a real apply, surface (read-only) whether any locally-authored
|
|
103
127
|
// agent definitions need cross-pollinating to the project's other agents.
|
package/dist/cli/apply.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/cli/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAmB,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE1E,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/cli/apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
# Lisa-managed Codex hook script (PreToolUse Bash).
|
|
3
3
|
# Blocks git commands that bypass verification hooks: the --no-verify long flag,
|
|
4
4
|
# HUSKY=0 / HUSKY_SKIP_HOOKS= (disables husky hooks), and core.hooksPath pointed
|
|
5
|
-
# at /dev/null or set empty (disables all git hooks).
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
5
|
+
# at /dev/null or set empty (disables all git hooks). Shell-token matching
|
|
6
|
+
# avoids false positives from issue bodies, heredocs, and commit-message prose
|
|
7
|
+
# while still catching quoted real argv values such as
|
|
8
|
+
# `git -c "core.hooksPath=/dev/null"`.
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
|
|
11
11
|
input="$(cat 2>/dev/null || true)"
|
|
@@ -18,11 +18,65 @@ tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null || t
|
|
|
18
18
|
command_str="$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null || true)"
|
|
19
19
|
[ -n "$command_str" ] || exit 0
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
command -v python3 >/dev/null 2>&1 || exit 0
|
|
22
|
+
|
|
23
|
+
if ! BLOCK_NO_VERIFY_COMMAND="$command_str" python3 - <<'PY'
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
import shlex
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
command = os.environ.get("BLOCK_NO_VERIFY_COMMAND", "")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def strip_heredocs(text: str) -> str:
|
|
33
|
+
lines = text.splitlines()
|
|
34
|
+
output = []
|
|
35
|
+
pending = []
|
|
36
|
+
marker_pattern = re.compile(
|
|
37
|
+
r"<<-?\s*(?:'([^']+)'|\"([^\"]+)\"|([A-Za-z_][A-Za-z0-9_]*))"
|
|
38
|
+
)
|
|
39
|
+
index = 0
|
|
40
|
+
while index < len(lines):
|
|
41
|
+
line = lines[index]
|
|
42
|
+
output.append(line)
|
|
43
|
+
pending.extend(
|
|
44
|
+
next(group for group in match.groups() if group)
|
|
45
|
+
for match in marker_pattern.finditer(line)
|
|
46
|
+
)
|
|
47
|
+
index += 1
|
|
48
|
+
while pending and index < len(lines):
|
|
49
|
+
if lines[index].strip() == pending[0]:
|
|
50
|
+
output.append(lines[index])
|
|
51
|
+
pending.pop(0)
|
|
52
|
+
index += 1
|
|
53
|
+
break
|
|
54
|
+
index += 1
|
|
55
|
+
return "\n".join(output)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
tokens = shlex.split(strip_heredocs(command), posix=True)
|
|
60
|
+
except ValueError:
|
|
61
|
+
sys.exit(0)
|
|
62
|
+
|
|
63
|
+
normalized_tokens = [token.strip("();|&") for token in tokens]
|
|
64
|
+
|
|
65
|
+
for i, token in enumerate(normalized_tokens):
|
|
66
|
+
if token == "--no-verify":
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
if token.startswith("core.hooksPath="):
|
|
71
|
+
value = token.split("=", 1)[1]
|
|
72
|
+
if value in ("", "/dev/null"):
|
|
73
|
+
sys.exit(1)
|
|
74
|
+
if token == "core.hooksPath" and i + 1 < len(normalized_tokens) and normalized_tokens[i + 1] in ("", "/dev/null"):
|
|
75
|
+
sys.exit(1)
|
|
76
|
+
|
|
77
|
+
sys.exit(0)
|
|
78
|
+
PY
|
|
79
|
+
then
|
|
26
80
|
jq -n '{
|
|
27
81
|
"hookSpecificOutput": {
|
|
28
82
|
"hookEventName": "PreToolUse",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const LISA_BOOTSTRAP_ENV = "LISA_BOOTSTRAP";
|
|
2
|
+
export declare const BUILD_ENV_FINGERPRINTS: readonly string[];
|
|
3
|
+
export declare const BOOTSTRAP_SKIP_NOTICE = "lisa: skipped (non-interactive environment; set LISA_BOOTSTRAP=1 to force)";
|
|
4
|
+
/**
|
|
5
|
+
* Runtime state used by the bootstrap guard.
|
|
6
|
+
*/
|
|
7
|
+
export interface BootstrapEnvironment {
|
|
8
|
+
/** Environment variables visible to the current process. */
|
|
9
|
+
readonly env: NodeJS.ProcessEnv;
|
|
10
|
+
/** Whether stdin is attached to an interactive TTY. */
|
|
11
|
+
readonly stdinIsTTY: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Options for deciding whether the apply entry point should be skipped.
|
|
15
|
+
*/
|
|
16
|
+
export interface BootstrapGuardOptions {
|
|
17
|
+
/** Whether the caller requested validate-only mode. */
|
|
18
|
+
readonly validateOnly: boolean;
|
|
19
|
+
/** Injectable runtime state for tests. Defaults to the current process. */
|
|
20
|
+
readonly environment?: BootstrapEnvironment;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Decide whether the Lisa apply entry point should refuse to run.
|
|
24
|
+
*
|
|
25
|
+
* Validate mode is intentionally exempt because it never writes project files.
|
|
26
|
+
* Real apply must be explicitly opted in when running without a TTY or under a
|
|
27
|
+
* known build-system fingerprint.
|
|
28
|
+
* @param options - Guard options and injectable runtime state
|
|
29
|
+
* @returns The skip notice when apply should be skipped, otherwise undefined
|
|
30
|
+
*/
|
|
31
|
+
export declare function getBootstrapApplySkipNotice(options: BootstrapGuardOptions): string | undefined;
|
|
32
|
+
//# sourceMappingURL=bootstrap-environment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap-environment.d.ts","sourceRoot":"","sources":["../../src/core/bootstrap-environment.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAEnD,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAUnD,CAAC;AAEF,eAAO,MAAM,qBAAqB,+EAC4C,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uDAAuD;IACvD,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAaD;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,qBAAqB,GAC7B,MAAM,GAAG,SAAS,CAoBpB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const LISA_BOOTSTRAP_ENV = "LISA_BOOTSTRAP";
|
|
2
|
+
export const BUILD_ENV_FINGERPRINTS = [
|
|
3
|
+
"CI",
|
|
4
|
+
"CODEBUILD_BUILD_ID",
|
|
5
|
+
"GITHUB_ACTIONS",
|
|
6
|
+
"EAS_BUILD",
|
|
7
|
+
"VERCEL",
|
|
8
|
+
"BUILDKITE",
|
|
9
|
+
"JENKINS_URL",
|
|
10
|
+
"AMPLIFY_APP_ID",
|
|
11
|
+
"AWS_BRANCH",
|
|
12
|
+
];
|
|
13
|
+
export const BOOTSTRAP_SKIP_NOTICE = "lisa: skipped (non-interactive environment; set LISA_BOOTSTRAP=1 to force)";
|
|
14
|
+
/**
|
|
15
|
+
* Read process.env through one explicit, reviewable exception to the app-template
|
|
16
|
+
* config access ban. Lisa's CLI bootstrap guard must inspect externally supplied
|
|
17
|
+
* build-system fingerprints before any project config is available.
|
|
18
|
+
* @returns The current process environment
|
|
19
|
+
*/
|
|
20
|
+
function readProcessEnv() {
|
|
21
|
+
// eslint-disable-next-line no-restricted-syntax -- CLI bootstrap guard must read externally supplied build env vars once
|
|
22
|
+
return process.env;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Decide whether the Lisa apply entry point should refuse to run.
|
|
26
|
+
*
|
|
27
|
+
* Validate mode is intentionally exempt because it never writes project files.
|
|
28
|
+
* Real apply must be explicitly opted in when running without a TTY or under a
|
|
29
|
+
* known build-system fingerprint.
|
|
30
|
+
* @param options - Guard options and injectable runtime state
|
|
31
|
+
* @returns The skip notice when apply should be skipped, otherwise undefined
|
|
32
|
+
*/
|
|
33
|
+
export function getBootstrapApplySkipNotice(options) {
|
|
34
|
+
if (options.validateOnly)
|
|
35
|
+
return undefined;
|
|
36
|
+
const environment = options.environment ?? {
|
|
37
|
+
env: readProcessEnv(),
|
|
38
|
+
stdinIsTTY: process.stdin.isTTY === true,
|
|
39
|
+
};
|
|
40
|
+
if (environment.env[LISA_BOOTSTRAP_ENV] === "1")
|
|
41
|
+
return undefined;
|
|
42
|
+
const hasBuildEnvFingerprint = BUILD_ENV_FINGERPRINTS.some(name => {
|
|
43
|
+
const value = environment.env[name];
|
|
44
|
+
return value !== undefined && value !== "";
|
|
45
|
+
});
|
|
46
|
+
if (!environment.stdinIsTTY || hasBuildEnvFingerprint) {
|
|
47
|
+
return BOOTSTRAP_SKIP_NOTICE;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=bootstrap-environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap-environment.js","sourceRoot":"","sources":["../../src/core/bootstrap-environment.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAEnD,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,IAAI;IACJ,oBAAoB;IACpB,gBAAgB;IAChB,WAAW;IACX,QAAQ;IACR,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,YAAY;CACb,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAChC,4EAA4E,CAAC;AAsB/E;;;;;GAKG;AACH,SAAS,cAAc;IACrB,yHAAyH;IACzH,OAAO,OAAO,CAAC,GAAG,CAAC;AACrB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,2BAA2B,CACzC,OAA8B;IAE9B,IAAI,OAAO,CAAC,YAAY;QAAE,OAAO,SAAS,CAAC;IAE3C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI;QACzC,GAAG,EAAE,cAAc,EAAE;QACrB,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI;KACzC,CAAC;IAEF,IAAI,WAAW,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,GAAG;QAAE,OAAO,SAAS,CAAC;IAElE,MAAM,sBAAsB,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAChE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,WAAW,CAAC,UAAU,IAAI,sBAAsB,EAAE,CAAC;QACtD,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/core/index.js
CHANGED
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAyB,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAyB,MAAM,WAAW,CAAC"}
|
package/package.json
CHANGED
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"lodash": ">=4.18.1"
|
|
86
86
|
},
|
|
87
87
|
"name": "@codyswann/lisa",
|
|
88
|
-
"version": "2.163.
|
|
88
|
+
"version": "2.163.5",
|
|
89
89
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
90
90
|
"main": "dist/index.js",
|
|
91
91
|
"exports": {
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
# git quality gates (exact parity with the Claude block-no-verify.sh): the
|
|
4
4
|
# `--no-verify` long flag, `HUSKY=0`/`HUSKY_SKIP_HOOKS=` (disables husky hooks),
|
|
5
5
|
# and `core.hooksPath` pointed at /dev/null or set empty (disables all git
|
|
6
|
-
# hooks).
|
|
7
|
-
#
|
|
6
|
+
# hooks). Shell-token matching avoids false positives from issue bodies,
|
|
7
|
+
# heredocs, and commit-message prose while still catching quoted real argv
|
|
8
|
+
# values such as `git -c "core.hooksPath=/dev/null"`.
|
|
8
9
|
#
|
|
9
10
|
# agy protocol (distinct from the Claude block-no-verify.sh exit-code protocol):
|
|
10
11
|
# - stdin = JSON: { "toolCall": { "name": "run_command",
|
|
@@ -34,17 +35,65 @@ input="$(cat 2>/dev/null || true)"
|
|
|
34
35
|
command_str="$(printf '%s' "$input" | jq -r '.toolCall.args.CommandLine // empty' 2>/dev/null || true)"
|
|
35
36
|
[ -z "$command_str" ] && allow
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
command -v python3 >/dev/null 2>&1 || allow
|
|
39
|
+
|
|
40
|
+
if ! BLOCK_NO_VERIFY_COMMAND="$command_str" python3 - <<'PY'
|
|
41
|
+
import os
|
|
42
|
+
import re
|
|
43
|
+
import shlex
|
|
44
|
+
import sys
|
|
45
|
+
|
|
46
|
+
command = os.environ.get("BLOCK_NO_VERIFY_COMMAND", "")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def strip_heredocs(text: str) -> str:
|
|
50
|
+
lines = text.splitlines()
|
|
51
|
+
output = []
|
|
52
|
+
pending = []
|
|
53
|
+
marker_pattern = re.compile(
|
|
54
|
+
r"<<-?\s*(?:'([^']+)'|\"([^\"]+)\"|([A-Za-z_][A-Za-z0-9_]*))"
|
|
55
|
+
)
|
|
56
|
+
index = 0
|
|
57
|
+
while index < len(lines):
|
|
58
|
+
line = lines[index]
|
|
59
|
+
output.append(line)
|
|
60
|
+
pending.extend(
|
|
61
|
+
next(group for group in match.groups() if group)
|
|
62
|
+
for match in marker_pattern.finditer(line)
|
|
63
|
+
)
|
|
64
|
+
index += 1
|
|
65
|
+
while pending and index < len(lines):
|
|
66
|
+
if lines[index].strip() == pending[0]:
|
|
67
|
+
output.append(lines[index])
|
|
68
|
+
pending.pop(0)
|
|
69
|
+
index += 1
|
|
70
|
+
break
|
|
71
|
+
index += 1
|
|
72
|
+
return "\n".join(output)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
tokens = shlex.split(strip_heredocs(command), posix=True)
|
|
77
|
+
except ValueError:
|
|
78
|
+
sys.exit(0)
|
|
79
|
+
|
|
80
|
+
normalized_tokens = [token.strip("();|&") for token in tokens]
|
|
81
|
+
|
|
82
|
+
for i, token in enumerate(normalized_tokens):
|
|
83
|
+
if token == "--no-verify":
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
if token.startswith("core.hooksPath="):
|
|
88
|
+
value = token.split("=", 1)[1]
|
|
89
|
+
if value in ("", "/dev/null"):
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
if token == "core.hooksPath" and i + 1 < len(normalized_tokens) and normalized_tokens[i + 1] in ("", "/dev/null"):
|
|
92
|
+
sys.exit(1)
|
|
93
|
+
|
|
94
|
+
sys.exit(0)
|
|
95
|
+
PY
|
|
96
|
+
then
|
|
48
97
|
deny
|
|
49
98
|
fi
|
|
50
99
|
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
# 2. HUSKY=0 / HUSKY_SKIP_HOOKS=... — disables husky-managed git hooks;
|
|
10
10
|
# 3. core.hooksPath pointed at /dev/null or set empty — disables ALL git hooks.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# Shell-token matching avoids false positives from issue bodies, heredocs, and
|
|
13
|
+
# commit-message prose while still catching quoted real argv values such as
|
|
14
|
+
# `git -c "core.hooksPath=/dev/null"`.
|
|
14
15
|
#
|
|
15
16
|
# The short `-n` form is intentionally NOT matched (see block-no-verify.agy.sh):
|
|
16
17
|
# grep cannot distinguish a real -n option from -n in commit-message prose or an
|
|
@@ -29,14 +30,65 @@ if [ -z "$command_str" ]; then
|
|
|
29
30
|
exit 0
|
|
30
31
|
fi
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
command -v python3 >/dev/null 2>&1 || exit 0
|
|
34
|
+
|
|
35
|
+
if ! BLOCK_NO_VERIFY_COMMAND="$command_str" python3 - <<'PY'
|
|
36
|
+
import os
|
|
37
|
+
import re
|
|
38
|
+
import shlex
|
|
39
|
+
import sys
|
|
40
|
+
|
|
41
|
+
command = os.environ.get("BLOCK_NO_VERIFY_COMMAND", "")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def strip_heredocs(text: str) -> str:
|
|
45
|
+
lines = text.splitlines()
|
|
46
|
+
output = []
|
|
47
|
+
pending = []
|
|
48
|
+
marker_pattern = re.compile(
|
|
49
|
+
r"<<-?\s*(?:'([^']+)'|\"([^\"]+)\"|([A-Za-z_][A-Za-z0-9_]*))"
|
|
50
|
+
)
|
|
51
|
+
index = 0
|
|
52
|
+
while index < len(lines):
|
|
53
|
+
line = lines[index]
|
|
54
|
+
output.append(line)
|
|
55
|
+
pending.extend(
|
|
56
|
+
next(group for group in match.groups() if group)
|
|
57
|
+
for match in marker_pattern.finditer(line)
|
|
58
|
+
)
|
|
59
|
+
index += 1
|
|
60
|
+
while pending and index < len(lines):
|
|
61
|
+
if lines[index].strip() == pending[0]:
|
|
62
|
+
output.append(lines[index])
|
|
63
|
+
pending.pop(0)
|
|
64
|
+
index += 1
|
|
65
|
+
break
|
|
66
|
+
index += 1
|
|
67
|
+
return "\n".join(output)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
tokens = shlex.split(strip_heredocs(command), posix=True)
|
|
72
|
+
except ValueError:
|
|
73
|
+
sys.exit(0)
|
|
74
|
+
|
|
75
|
+
normalized_tokens = [token.strip("();|&") for token in tokens]
|
|
76
|
+
|
|
77
|
+
for i, token in enumerate(normalized_tokens):
|
|
78
|
+
if token == "--no-verify":
|
|
79
|
+
sys.exit(1)
|
|
80
|
+
if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
if token.startswith("core.hooksPath="):
|
|
83
|
+
value = token.split("=", 1)[1]
|
|
84
|
+
if value in ("", "/dev/null"):
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
if token == "core.hooksPath" and i + 1 < len(normalized_tokens) and normalized_tokens[i + 1] in ("", "/dev/null"):
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
|
|
89
|
+
sys.exit(0)
|
|
90
|
+
PY
|
|
91
|
+
then
|
|
40
92
|
cat >&2 <<'EOF'
|
|
41
93
|
Blocked: this command bypasses pre-commit/pre-push hooks (--no-verify, HUSKY=0,
|
|
42
94
|
or core.hooksPath disabling). Fix the underlying issue (lint error, failing
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
# git quality gates (exact parity with the Claude block-no-verify.sh): the
|
|
4
4
|
# `--no-verify` long flag, `HUSKY=0`/`HUSKY_SKIP_HOOKS=` (disables husky hooks),
|
|
5
5
|
# and `core.hooksPath` pointed at /dev/null or set empty (disables all git
|
|
6
|
-
# hooks).
|
|
7
|
-
#
|
|
6
|
+
# hooks). Shell-token matching avoids false positives from issue bodies,
|
|
7
|
+
# heredocs, and commit-message prose while still catching quoted real argv
|
|
8
|
+
# values such as `git -c "core.hooksPath=/dev/null"`.
|
|
8
9
|
#
|
|
9
10
|
# agy protocol (distinct from the Claude block-no-verify.sh exit-code protocol):
|
|
10
11
|
# - stdin = JSON: { "toolCall": { "name": "run_command",
|
|
@@ -34,17 +35,65 @@ input="$(cat 2>/dev/null || true)"
|
|
|
34
35
|
command_str="$(printf '%s' "$input" | jq -r '.toolCall.args.CommandLine // empty' 2>/dev/null || true)"
|
|
35
36
|
[ -z "$command_str" ] && allow
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
command -v python3 >/dev/null 2>&1 || allow
|
|
39
|
+
|
|
40
|
+
if ! BLOCK_NO_VERIFY_COMMAND="$command_str" python3 - <<'PY'
|
|
41
|
+
import os
|
|
42
|
+
import re
|
|
43
|
+
import shlex
|
|
44
|
+
import sys
|
|
45
|
+
|
|
46
|
+
command = os.environ.get("BLOCK_NO_VERIFY_COMMAND", "")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def strip_heredocs(text: str) -> str:
|
|
50
|
+
lines = text.splitlines()
|
|
51
|
+
output = []
|
|
52
|
+
pending = []
|
|
53
|
+
marker_pattern = re.compile(
|
|
54
|
+
r"<<-?\s*(?:'([^']+)'|\"([^\"]+)\"|([A-Za-z_][A-Za-z0-9_]*))"
|
|
55
|
+
)
|
|
56
|
+
index = 0
|
|
57
|
+
while index < len(lines):
|
|
58
|
+
line = lines[index]
|
|
59
|
+
output.append(line)
|
|
60
|
+
pending.extend(
|
|
61
|
+
next(group for group in match.groups() if group)
|
|
62
|
+
for match in marker_pattern.finditer(line)
|
|
63
|
+
)
|
|
64
|
+
index += 1
|
|
65
|
+
while pending and index < len(lines):
|
|
66
|
+
if lines[index].strip() == pending[0]:
|
|
67
|
+
output.append(lines[index])
|
|
68
|
+
pending.pop(0)
|
|
69
|
+
index += 1
|
|
70
|
+
break
|
|
71
|
+
index += 1
|
|
72
|
+
return "\n".join(output)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
tokens = shlex.split(strip_heredocs(command), posix=True)
|
|
77
|
+
except ValueError:
|
|
78
|
+
sys.exit(0)
|
|
79
|
+
|
|
80
|
+
normalized_tokens = [token.strip("();|&") for token in tokens]
|
|
81
|
+
|
|
82
|
+
for i, token in enumerate(normalized_tokens):
|
|
83
|
+
if token == "--no-verify":
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
if token.startswith("core.hooksPath="):
|
|
88
|
+
value = token.split("=", 1)[1]
|
|
89
|
+
if value in ("", "/dev/null"):
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
if token == "core.hooksPath" and i + 1 < len(normalized_tokens) and normalized_tokens[i + 1] in ("", "/dev/null"):
|
|
92
|
+
sys.exit(1)
|
|
93
|
+
|
|
94
|
+
sys.exit(0)
|
|
95
|
+
PY
|
|
96
|
+
then
|
|
48
97
|
deny
|
|
49
98
|
fi
|
|
50
99
|
|