@codyswann/lisa 2.342.2 → 2.342.4
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-work-item.mjs +130 -38
- package/dist/core/upstream-evidence-manifest.js +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-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-cursor/.claude-plugin/plugin.json +1 -1
- 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
|
@@ -29,21 +29,9 @@ const GUIDANCE = [
|
|
|
29
29
|
class TrackingError extends Error {}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* reader to three different places: a missing binary is an environment to
|
|
36
|
-
* provision, a refused credential is an access grant to obtain, and only the
|
|
37
|
-
* third is anything to do with the ticket.
|
|
38
|
-
*
|
|
39
|
-
* Collapsing them cost a real round trip. On Claude Code web, where `gh` is not
|
|
40
|
-
* pre-installed, every commit was refused with "issue #2202 does not exist or
|
|
41
|
-
* is inaccessible" — about an issue that was open and correct. The message
|
|
42
|
-
* accused the ticket, so that is where the reader went.
|
|
43
|
-
* @param {{status: number|null, error?: Error, stderr?: string}} result Spawn result.
|
|
44
|
-
* @param {string} ref Work item reference, for the message.
|
|
45
|
-
* @param {string} noun What the reference names.
|
|
46
|
-
* @returns {string} A message naming the actual fault.
|
|
32
|
+
* The tracker could not be ASKED — a missing binary, a refused credential, a
|
|
33
|
+
* call that never completed. Distinct from a TrackingError, which carries the
|
|
34
|
+
* tracker's own "no". Only this kind may be degraded.
|
|
47
35
|
*/
|
|
48
36
|
export class TrackerUnreachableError extends TrackingError {}
|
|
49
37
|
|
|
@@ -61,13 +49,35 @@ export class TrackerUnreachableError extends TrackingError {}
|
|
|
61
49
|
*/
|
|
62
50
|
export function githubFailure(result, ref) {
|
|
63
51
|
const reason = githubFailureReason(result, ref, "GitHub issue");
|
|
52
|
+
// ANY spawn-level error means the tracker could not be asked — the binary is
|
|
53
|
+
// missing (ENOENT), the call was killed at its deadline (ETIMEDOUT), the
|
|
54
|
+
// system could not fork (EAGAIN). Singling out ENOENT made a timed-out `gh`
|
|
55
|
+
// a non-degradable TrackingError, i.e. "this work item is invalid" because
|
|
56
|
+
// the network was slow.
|
|
64
57
|
const unreachable =
|
|
65
|
-
result.error
|
|
58
|
+
result.error !== undefined || /cannot authenticate/.test(reason);
|
|
66
59
|
return unreachable
|
|
67
60
|
? new TrackerUnreachableError(reason)
|
|
68
61
|
: new TrackingError(reason);
|
|
69
62
|
}
|
|
70
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Say which of three different failures a `gh` invocation actually hit.
|
|
66
|
+
*
|
|
67
|
+
* The three are indistinguishable from an exit status alone, and they send the
|
|
68
|
+
* reader to three different places: a missing binary is an environment to
|
|
69
|
+
* provision, a refused credential is an access grant to obtain, and only the
|
|
70
|
+
* third is anything to do with the ticket.
|
|
71
|
+
*
|
|
72
|
+
* Collapsing them cost a real round trip. On Claude Code web, where `gh` is not
|
|
73
|
+
* pre-installed, every commit was refused with "issue #2202 does not exist or
|
|
74
|
+
* is inaccessible" — about an issue that was open and correct. The message
|
|
75
|
+
* accused the ticket, so that is where the reader went.
|
|
76
|
+
* @param {{status: number|null, error?: Error, stderr?: string}} result Spawn result.
|
|
77
|
+
* @param {string} ref Work item reference, for the message.
|
|
78
|
+
* @param {string} noun What the reference names.
|
|
79
|
+
* @returns {string} A message naming the actual fault.
|
|
80
|
+
*/
|
|
71
81
|
export function githubFailureReason(result, ref, noun) {
|
|
72
82
|
if (result.error?.code === "ENOENT") {
|
|
73
83
|
return (
|
|
@@ -91,13 +101,38 @@ export function githubFailureReason(result, ref, noun) {
|
|
|
91
101
|
return `${noun} ${ref} does not exist or is inaccessible`;
|
|
92
102
|
}
|
|
93
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Wall-clock ceiling for any child process this script spawns.
|
|
106
|
+
*
|
|
107
|
+
* These run inside a git hook, so an unbounded call does not merely wait — it
|
|
108
|
+
* hangs the commit or push that invoked it, with no output explaining why. A
|
|
109
|
+
* tracker that has stopped answering must look like a tracker that is
|
|
110
|
+
* unreachable, which the degradation path already knows how to handle.
|
|
111
|
+
*/
|
|
112
|
+
const CHILD_TIMEOUT_MS = Number(
|
|
113
|
+
process.env.LISA_WORK_ITEM_TIMEOUT_MS || 30_000
|
|
114
|
+
);
|
|
115
|
+
|
|
94
116
|
function run(command, args, options = {}) {
|
|
95
117
|
const result = spawnSync(command, args, {
|
|
96
118
|
cwd: options.cwd ?? process.cwd(),
|
|
97
119
|
encoding: "utf8",
|
|
98
120
|
env: options.env ?? process.env,
|
|
99
121
|
input: options.input,
|
|
122
|
+
timeout: options.timeout ?? CHILD_TIMEOUT_MS,
|
|
123
|
+
// SIGKILL, not the default SIGTERM. `timeout` sends killSignal and then
|
|
124
|
+
// keeps waiting for the child to exit, so a child that traps or ignores
|
|
125
|
+
// SIGTERM hangs the hook exactly as long as it would have without any
|
|
126
|
+
// timeout at all. A deadline a child can decline is not a deadline.
|
|
127
|
+
killSignal: "SIGKILL",
|
|
100
128
|
});
|
|
129
|
+
// spawnSync reports a timeout via `error`, not a non-zero status, so the
|
|
130
|
+
// status check below would let a timed-out call through as success.
|
|
131
|
+
if (result.error && !options.allowFailure) {
|
|
132
|
+
throw new TrackerUnreachableError(
|
|
133
|
+
`${command} ${args.join(" ")} did not complete: ${result.error.message}`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
101
136
|
if (result.status !== 0 && !options.allowFailure) {
|
|
102
137
|
throw new TrackingError(
|
|
103
138
|
options.error ?? `${command} ${args.join(" ")} failed`
|
|
@@ -122,10 +157,24 @@ function secureCurl(args, entries, options = {}) {
|
|
|
122
157
|
const input = `${entries
|
|
123
158
|
.map(([name, value]) => `${name} = ${curlConfigValue(value)}`)
|
|
124
159
|
.join("\n")}\n`;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
160
|
+
// --max-time bounds curl itself, so it exits on its own rather than being
|
|
161
|
+
// killed by the spawnSync ceiling; --connect-timeout fails fast on a host
|
|
162
|
+
// that is not answering at all. Kept below CHILD_TIMEOUT_MS so curl's own
|
|
163
|
+
// error message is what surfaces.
|
|
164
|
+
return run(
|
|
165
|
+
"curl",
|
|
166
|
+
[
|
|
167
|
+
"-fsS",
|
|
168
|
+
"--connect-timeout",
|
|
169
|
+
"10",
|
|
170
|
+
"--max-time",
|
|
171
|
+
"25",
|
|
172
|
+
"--config",
|
|
173
|
+
"-",
|
|
174
|
+
...args,
|
|
175
|
+
],
|
|
176
|
+
{ ...options, input }
|
|
177
|
+
);
|
|
129
178
|
}
|
|
130
179
|
|
|
131
180
|
function projectRoot() {
|
|
@@ -557,13 +606,34 @@ function typeFromLabels(labels) {
|
|
|
557
606
|
return namesFrom(labels).find(name => name.startsWith("type:"));
|
|
558
607
|
}
|
|
559
608
|
|
|
609
|
+
/**
|
|
610
|
+
* Every sub-issue state for a GitHub issue, following pagination.
|
|
611
|
+
*
|
|
612
|
+
* Two things this gets right that the single-page version did not:
|
|
613
|
+
*
|
|
614
|
+
* A transport failure is classified through githubFailure, exactly as
|
|
615
|
+
* githubIssue does. Raising a bare TrackingError meant "gh is missing" and
|
|
616
|
+
* "the network is down" read as "this work item is invalid", so validateLive
|
|
617
|
+
* could not degrade and the hook hard-failed on a laptop with no connection.
|
|
618
|
+
*
|
|
619
|
+
* And it pages. `subIssues(first:100)` silently truncated, so an Epic with
|
|
620
|
+
* more than 100 children reported only the first page — assertLeaf then saw
|
|
621
|
+
* no open child beyond it and treated a container as a leaf, which is the
|
|
622
|
+
* precise condition leaf-only-lifecycle exists to prevent.
|
|
623
|
+
* @param {string} ref Canonical work-item ref, for error messages.
|
|
624
|
+
* @param {object} contract Resolved tracker contract.
|
|
625
|
+
* @param {string|number} number GitHub issue number.
|
|
626
|
+
* @returns {string[]} State of every sub-issue.
|
|
627
|
+
*/
|
|
560
628
|
function githubHierarchy(ref, contract, number) {
|
|
561
629
|
const [owner, repo] = contract.repository.split("/");
|
|
562
630
|
const query =
|
|
563
|
-
"query($owner:String!,$repo:String!,$number:Int
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
631
|
+
"query($owner:String!,$repo:String!,$number:Int!,$after:String){repository(owner:$owner,name:$repo){issue(number:$number){subIssues(first:100,after:$after){nodes{state}pageInfo{hasNextPage endCursor}}}}}";
|
|
632
|
+
const states = [];
|
|
633
|
+
const cursor = { after: null };
|
|
634
|
+
|
|
635
|
+
do {
|
|
636
|
+
const args = [
|
|
567
637
|
"api",
|
|
568
638
|
"graphql",
|
|
569
639
|
"-f",
|
|
@@ -574,19 +644,25 @@ function githubHierarchy(ref, contract, number) {
|
|
|
574
644
|
`repo=${repo}`,
|
|
575
645
|
"-F",
|
|
576
646
|
`number=${number}`,
|
|
577
|
-
]
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
647
|
+
];
|
|
648
|
+
if (cursor.after) args.push("-F", `after=${cursor.after}`);
|
|
649
|
+
const result = run("gh", args, { allowFailure: true });
|
|
650
|
+
if (result.status !== 0) throw githubFailure(result, ref);
|
|
651
|
+
|
|
652
|
+
const response = safeJson(result.stdout, `GitHub issue ${ref} hierarchy`);
|
|
653
|
+
const subIssues = response.data?.repository?.issue?.subIssues;
|
|
654
|
+
if (!Array.isArray(subIssues?.nodes)) {
|
|
655
|
+
throw new TrackingError(
|
|
656
|
+
`GitHub issue ${ref} did not expose native sub-issue hierarchy`
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
states.push(...subIssues.nodes.map(child => child.state));
|
|
660
|
+
cursor.after = subIssues.pageInfo?.hasNextPage
|
|
661
|
+
? subIssues.pageInfo.endCursor
|
|
662
|
+
: null;
|
|
663
|
+
} while (cursor.after);
|
|
664
|
+
|
|
665
|
+
return states;
|
|
590
666
|
}
|
|
591
667
|
|
|
592
668
|
function githubIssue(ref, contract) {
|
|
@@ -1152,9 +1228,25 @@ function currentPullRequest(number, repository = currentRepository()) {
|
|
|
1152
1228
|
: undefined;
|
|
1153
1229
|
}
|
|
1154
1230
|
|
|
1231
|
+
/**
|
|
1232
|
+
* Read a `--name <value>` option, falling back to an environment variable.
|
|
1233
|
+
*
|
|
1234
|
+
* A flag present but carrying no value — last on the line, or immediately
|
|
1235
|
+
* followed by another flag — used to yield `undefined` (or the next flag's
|
|
1236
|
+
* name) rather than falling back. `lisa … --ref` therefore silently discarded
|
|
1237
|
+
* a perfectly good value in the environment, and `--ref --json` bound the
|
|
1238
|
+
* literal string "--json". Both now behave as if the flag were absent.
|
|
1239
|
+
* @param {string[]} args Argument list.
|
|
1240
|
+
* @param {string} name Flag to read, including leading dashes.
|
|
1241
|
+
* @param {string} envName Environment variable to fall back to.
|
|
1242
|
+
* @returns {string | undefined} The resolved value, if any.
|
|
1243
|
+
*/
|
|
1155
1244
|
function option(args, name, envName) {
|
|
1156
1245
|
const index = args.indexOf(name);
|
|
1157
|
-
|
|
1246
|
+
if (index < 0) return process.env[envName];
|
|
1247
|
+
const value = args[index + 1];
|
|
1248
|
+
if (value === undefined || value.startsWith("-")) return process.env[envName];
|
|
1249
|
+
return value;
|
|
1158
1250
|
}
|
|
1159
1251
|
|
|
1160
1252
|
function bind(args) {
|
|
@@ -10,7 +10,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
10
10
|
"all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh": "1934a15e154fda3c2a40979a5cd6225661b14fe7bc77328a69ce499bea85dba7",
|
|
11
11
|
"all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh": "d9ea58150aa27e7f0aa9a4ef155ed446d30bdfc932d01c64e2b8ca211aa0de7b",
|
|
12
12
|
"all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh": "bf4132e49ba18e2f7e941520c299c15aeeacfd220e489f3d2eb8397f2048157b",
|
|
13
|
-
"all/copy-overwrite/scripts/lisa-work-item.mjs": "
|
|
13
|
+
"all/copy-overwrite/scripts/lisa-work-item.mjs": "fc0d92739e620d8fd70f7aa4a12d3e0b3a8f790d47c4a8322040f32c30b9445a",
|
|
14
14
|
"all/create-only/.claude/rules/PROJECT_RULES.md": "6e1c7923ad2a15356babc60c97e7f97be728d790af285b6a7cd7d1b4d39cd97f",
|
|
15
15
|
"all/create-only/.lisaignore": "3c9827e7e98daa46510b5357cd6dd9406da1821e64cc46d2eea9f311cd6d06b9",
|
|
16
16
|
"all/create-only/scripts/remote-agent-aws-setup.sh": "12d2204d34195ad4f81082e90a25dbbf7d2493debcc50c1cd37b32dc4648fc17",
|
package/package.json
CHANGED
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
122
|
"name": "@codyswann/lisa",
|
|
123
|
-
"version": "2.342.
|
|
123
|
+
"version": "2.342.4",
|
|
124
124
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
125
125
|
"main": "dist/index.js",
|
|
126
126
|
"exports": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.342.
|
|
3
|
+
"version": "2.342.4",
|
|
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": "2.342.
|
|
3
|
+
"version": "2.342.4",
|
|
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": "2.342.
|
|
3
|
+
"version": "2.342.4",
|
|
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": "2.342.
|
|
3
|
+
"version": "2.342.4",
|
|
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": "2.342.
|
|
3
|
+
"version": "2.342.4",
|
|
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"
|