@codyswann/lisa 2.85.2 → 2.88.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/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/commands/automation-status.md +5 -0
- package/plugins/lisa/scripts/automation-status-expected-fleet.mjs +444 -0
- package/plugins/lisa/scripts/automation-status-report.mjs +170 -0
- package/plugins/lisa/skills/automation-status/SKILL.md +75 -0
- package/plugins/lisa/skills/automation-status/agents/openai.yaml +4 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-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-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-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-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-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-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-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/src/base/commands/automation-status.md +5 -0
- package/plugins/src/base/scripts/automation-status-expected-fleet.mjs +444 -0
- package/plugins/src/base/scripts/automation-status-report.mjs +170 -0
- package/plugins/src/base/skills/automation-status/SKILL.md +75 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: automation-status
|
|
3
|
+
description: "Read-only operator surface for the current project's Lisa automation fleet. Resolves the expected recurring jobs from the same setup-automations contract Lisa uses to create them, inspects the active runtime scheduler (Codex automations or Claude /schedule), compares live command/cadence/queue arguments against the expected contract, and reports grouped fleet health such as healthy, missing, unsupported, drifted, stale, or failing with remediation guidance."
|
|
4
|
+
allowed-tools: ["Skill", "Bash", "Read"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Automation Status: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
`/lisa:automation-status` is the operator-facing inspection surface for Lisa's unattended job fleet. It answers, for the **current repo only**, whether the recurring Lisa automations that should exist actually exist, still match Lisa's setup contract, and appear healthy based on the runtime metadata available.
|
|
10
|
+
|
|
11
|
+
This command is **read-only** in v1. It does not create, update, resume, rerun, pause, or delete automations. It complements `/lisa:setup-automations`, `/lisa:tear-down-automations`, `/lisa:intake`, `/lisa:repair-intake`, `doctor`, and `monitor`; it does not replace them.
|
|
12
|
+
|
|
13
|
+
## Confirmation policy
|
|
14
|
+
|
|
15
|
+
Do **not** ask for confirmation once invoked. This skill inspects scheduler state and reports what it finds. There are no write-side effects in the v1 surface.
|
|
16
|
+
|
|
17
|
+
## Scope
|
|
18
|
+
|
|
19
|
+
Inspect only the Lisa automation fleet for the current project:
|
|
20
|
+
|
|
21
|
+
- `intake-repair`
|
|
22
|
+
- `intake-prd`
|
|
23
|
+
- `intake-tickets`
|
|
24
|
+
- `exploratory-bugs` when the current stack supports `exploratory-qa`
|
|
25
|
+
- `exploratory-prds`
|
|
26
|
+
|
|
27
|
+
Resolve the expected project identifier, fleet naming prefix, queue arguments, cadence, and stack-support rules from the same contract used by `setup-automations` and `tear-down-automations`. Do **not** invent a second source of truth for fleet naming or queue resolution.
|
|
28
|
+
|
|
29
|
+
## Runtime inspection
|
|
30
|
+
|
|
31
|
+
Branch on the active runtime and prefer the runtime's native automation listing surface:
|
|
32
|
+
|
|
33
|
+
- **Codex**: inspect Codex automations metadata first. Use backing-store files only as a fallback or to enrich timestamps when the runtime surface cannot provide enough status detail directly.
|
|
34
|
+
- **Claude**: inspect the `/schedule` listing surface and any exposed recency or failure metadata available there.
|
|
35
|
+
- **Other runtimes**: if no native recurring-task inspection exists, report that automation-status is unsupported in this runtime rather than guessing.
|
|
36
|
+
|
|
37
|
+
The report must stay repo-scoped: inspect only automations whose names belong to the current repo's Lisa fleet prefix, and do not absorb unrelated automations into the result.
|
|
38
|
+
|
|
39
|
+
## What to report
|
|
40
|
+
|
|
41
|
+
For each expected automation, report:
|
|
42
|
+
|
|
43
|
+
1. Whether it exists.
|
|
44
|
+
2. Whether it is expected or intentionally unsupported.
|
|
45
|
+
3. The configured command and cadence Lisa expects.
|
|
46
|
+
4. Any detected drift in name, cadence, command shape, or queue arguments.
|
|
47
|
+
5. Any available recent-run health signal such as stale last-run timing or repeated failure status.
|
|
48
|
+
6. A concise remediation hint when attention is needed.
|
|
49
|
+
|
|
50
|
+
Emit an overall grouped fleet verdict such as `HEALTHY`, `ATTENTION_NEEDED`, or `PARTIAL_SUPPORT`, plus the runtime surface inspected.
|
|
51
|
+
|
|
52
|
+
Render the report in grouped sections using the shared `scripts/automation-status-report.mjs` contract:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
Overall verdict: <VERDICT>
|
|
56
|
+
Counts: <n HEALTHY>, <n MISSING>, <n UNSUPPORTED>, <n DRIFTED>, <n STALE>, <n FAILING>
|
|
57
|
+
Runtime inspected: <runtime surface>
|
|
58
|
+
Generated at: <ISO timestamp>
|
|
59
|
+
|
|
60
|
+
1. <group title>
|
|
61
|
+
- <STATUS> <automation-id>: <summary>
|
|
62
|
+
Expected: <cadence> -> <command>
|
|
63
|
+
Observed: <what the runtime exposed>
|
|
64
|
+
Remediation: <next step when attention is needed>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Keep observable runtime facts separate from remediation guidance so operators can distinguish drift, unsupported jobs, and actual failures quickly.
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
|
|
71
|
+
- Stay **read-only**. Never create, update, delete, enable, disable, or rerun automations from this skill.
|
|
72
|
+
- Reuse `setup-automations` contract logic for expected fleet resolution, cadence, queue arguments, naming, and stack-specific support checks.
|
|
73
|
+
- Distinguish **unsupported** from **missing**. An exploratory job omitted because the current stack lacks `exploratory-qa` is not a failure.
|
|
74
|
+
- If the runtime cannot expose a field such as last-run timestamp or failure state, say that explicitly instead of implying health.
|
|
75
|
+
- Keep the output operational and repo-scoped so operators can tell whether Lisa's unattended surfaces are present, current, and healthy right now.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.88.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": "2.
|
|
3
|
+
"version": "2.88.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"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Inspect the current project's Lisa automation fleet and report whether the expected recurring jobs exist, match Lisa's setup contract, and show healthy recent activity. Read-only by default."
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Use the /lisa:automation-status skill to inspect the current project's expected Lisa automations, compare them with the runtime's scheduler metadata, and report fleet health, drift, staleness, unsupported jobs, and remediation hints. $ARGUMENTS
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Shared automation-status expected-fleet helpers.
|
|
4
|
+
*
|
|
5
|
+
* This module resolves the same naming, queue arguments, cadence, and
|
|
6
|
+
* exploratory support decisions documented by `/lisa:setup-automations`, so
|
|
7
|
+
* automation-status and later runtime adapters do not invent a second source of
|
|
8
|
+
* truth.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const AUTOMATION_EXPECTED_CADENCES = {
|
|
12
|
+
"intake-repair": {
|
|
13
|
+
human: "every 60 minutes",
|
|
14
|
+
rrule: "FREQ=HOURLY;INTERVAL=1",
|
|
15
|
+
},
|
|
16
|
+
"intake-prd": {
|
|
17
|
+
human: "every 60 minutes",
|
|
18
|
+
rrule: "FREQ=HOURLY;INTERVAL=1",
|
|
19
|
+
},
|
|
20
|
+
"intake-tickets": {
|
|
21
|
+
human: "every 10 minutes",
|
|
22
|
+
rrule: "FREQ=MINUTELY;INTERVAL=10",
|
|
23
|
+
},
|
|
24
|
+
"exploratory-bugs": {
|
|
25
|
+
human: "once a day",
|
|
26
|
+
rrule: "FREQ=DAILY;INTERVAL=1",
|
|
27
|
+
},
|
|
28
|
+
"exploratory-prds": {
|
|
29
|
+
human: "once a day",
|
|
30
|
+
rrule: "FREQ=DAILY;INTERVAL=1",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const EXPLORATORY_QA_STACK_PRIORITY = ["expo", "rails", "harper-fabric"];
|
|
35
|
+
|
|
36
|
+
const GITHUB_REMOTE_PATTERNS = [
|
|
37
|
+
/github\.com[:/](?<owner>[^/]+)\/(?<repo>[^/.]+?)(?:\.git)?$/,
|
|
38
|
+
/^git@github\.com:(?<owner>[^/]+)\/(?<repo>[^/.]+?)(?:\.git)?$/,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef {{
|
|
43
|
+
* readonly id: string
|
|
44
|
+
* readonly automationId: string
|
|
45
|
+
* readonly expectedCadence: string
|
|
46
|
+
* readonly expectedRRule: string
|
|
47
|
+
* readonly expectedCommand: string
|
|
48
|
+
* readonly group: "core" | "exploratory"
|
|
49
|
+
* }} ExpectedAutomationEntry
|
|
50
|
+
*
|
|
51
|
+
* @typedef {{
|
|
52
|
+
* readonly id: string
|
|
53
|
+
* readonly automationId: string
|
|
54
|
+
* readonly group: "core" | "exploratory"
|
|
55
|
+
* readonly reason: string
|
|
56
|
+
* readonly expectedCadence: string
|
|
57
|
+
* readonly expectedRRule: string
|
|
58
|
+
* }} UnsupportedAutomationEntry
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the stable project identifier and automation prefix used by
|
|
63
|
+
* `/lisa:setup-automations`.
|
|
64
|
+
*
|
|
65
|
+
* @param {{
|
|
66
|
+
* readonly config?: Record<string, any>
|
|
67
|
+
* readonly gitRemoteUrl?: string
|
|
68
|
+
* }} input
|
|
69
|
+
* @returns {{ readonly owner: string, readonly repo: string, readonly project: string, readonly automationPrefix: string }}
|
|
70
|
+
*/
|
|
71
|
+
export function resolveAutomationProjectIdentity(input = {}) {
|
|
72
|
+
const githubRef = resolveGithubRepoRef(
|
|
73
|
+
input.config ?? {},
|
|
74
|
+
input.gitRemoteUrl
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!githubRef) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"Unable to resolve repo identity for automation naming. Configure github.org/github.repo or provide a GitHub origin remote."
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const project = slugifyProjectToken(`${githubRef.owner}-${githubRef.repo}`);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
...githubRef,
|
|
87
|
+
project,
|
|
88
|
+
automationPrefix: `lisa-auto-${project}-`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the expected Lisa automation fleet for the current repo.
|
|
94
|
+
*
|
|
95
|
+
* @param {{
|
|
96
|
+
* readonly config?: Record<string, any>
|
|
97
|
+
* readonly gitRemoteUrl?: string
|
|
98
|
+
* readonly detectedTypes?: readonly string[]
|
|
99
|
+
* readonly autoStartPrds?: boolean | string
|
|
100
|
+
* readonly autoStartTickets?: boolean | string
|
|
101
|
+
* }} input
|
|
102
|
+
* @returns {{
|
|
103
|
+
* readonly owner: string
|
|
104
|
+
* readonly repo: string
|
|
105
|
+
* readonly project: string
|
|
106
|
+
* readonly automationPrefix: string
|
|
107
|
+
* readonly expected: readonly ExpectedAutomationEntry[]
|
|
108
|
+
* readonly unsupported: readonly UnsupportedAutomationEntry[]
|
|
109
|
+
* }}
|
|
110
|
+
*/
|
|
111
|
+
export function resolveExpectedAutomationFleet(input = {}) {
|
|
112
|
+
const config = input.config ?? {};
|
|
113
|
+
const identity = resolveAutomationProjectIdentity(input);
|
|
114
|
+
const autoStartPrds = normalizeBooleanFlag(input.autoStartPrds);
|
|
115
|
+
const autoStartTickets = normalizeBooleanFlag(input.autoStartTickets);
|
|
116
|
+
const detectedTypes = input.detectedTypes ?? [];
|
|
117
|
+
|
|
118
|
+
const tracker = config.tracker;
|
|
119
|
+
const source = resolvePrdSource(config);
|
|
120
|
+
const prdQueue = resolvePrdQueueArgument(config, source);
|
|
121
|
+
const buildQueue = resolveBuildQueueArgument(config, tracker);
|
|
122
|
+
const repairQueue = resolveRepairQueueArgument(config, source, tracker);
|
|
123
|
+
|
|
124
|
+
const expected = [
|
|
125
|
+
createExpectedEntry(
|
|
126
|
+
identity,
|
|
127
|
+
"intake-repair",
|
|
128
|
+
`/lisa:repair-intake ${repairQueue}`,
|
|
129
|
+
"core"
|
|
130
|
+
),
|
|
131
|
+
createExpectedEntry(
|
|
132
|
+
identity,
|
|
133
|
+
"intake-prd",
|
|
134
|
+
`/lisa:intake ${prdQueue}`,
|
|
135
|
+
"core"
|
|
136
|
+
),
|
|
137
|
+
createExpectedEntry(
|
|
138
|
+
identity,
|
|
139
|
+
"intake-tickets",
|
|
140
|
+
`/lisa:intake ${buildQueue}`,
|
|
141
|
+
"core"
|
|
142
|
+
),
|
|
143
|
+
createExpectedEntry(
|
|
144
|
+
identity,
|
|
145
|
+
"exploratory-prds",
|
|
146
|
+
`/lisa:project-ideation prd_ready=${String(autoStartPrds)}`,
|
|
147
|
+
"exploratory"
|
|
148
|
+
),
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
const exploratoryStack = resolveExploratoryQaStack(detectedTypes);
|
|
152
|
+
const unsupported = [];
|
|
153
|
+
|
|
154
|
+
if (exploratoryStack) {
|
|
155
|
+
expected.push(
|
|
156
|
+
createExpectedEntry(
|
|
157
|
+
identity,
|
|
158
|
+
"exploratory-bugs",
|
|
159
|
+
`/lisa-${exploratoryStack}:exploratory-qa ready=${String(autoStartTickets)}`,
|
|
160
|
+
"exploratory"
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
unsupported.push(
|
|
165
|
+
createUnsupportedEntry(
|
|
166
|
+
identity,
|
|
167
|
+
"exploratory-bugs",
|
|
168
|
+
"This repository does not ship an exploratory-qa command surface."
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
...identity,
|
|
175
|
+
expected,
|
|
176
|
+
unsupported,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Return the supported exploratory-qa surface for the detected host stacks.
|
|
182
|
+
*
|
|
183
|
+
* @param {readonly string[]} detectedTypes
|
|
184
|
+
* @returns {string | null}
|
|
185
|
+
*/
|
|
186
|
+
export function resolveExploratoryQaStack(detectedTypes = []) {
|
|
187
|
+
for (const stack of EXPLORATORY_QA_STACK_PRIORITY) {
|
|
188
|
+
if (detectedTypes.includes(stack)) {
|
|
189
|
+
return stack;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {{ readonly automationPrefix: string }} identity
|
|
197
|
+
* @param {string} id
|
|
198
|
+
* @param {string} expectedCommand
|
|
199
|
+
* @param {"core" | "exploratory"} group
|
|
200
|
+
* @returns {ExpectedAutomationEntry}
|
|
201
|
+
*/
|
|
202
|
+
function createExpectedEntry(identity, id, expectedCommand, group) {
|
|
203
|
+
const cadence = AUTOMATION_EXPECTED_CADENCES[id];
|
|
204
|
+
return {
|
|
205
|
+
id,
|
|
206
|
+
automationId: `${identity.automationPrefix}${id}`,
|
|
207
|
+
expectedCadence: cadence.human,
|
|
208
|
+
expectedRRule: cadence.rrule,
|
|
209
|
+
expectedCommand,
|
|
210
|
+
group,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {{ readonly automationPrefix: string }} identity
|
|
216
|
+
* @param {string} id
|
|
217
|
+
* @param {string} reason
|
|
218
|
+
* @returns {UnsupportedAutomationEntry}
|
|
219
|
+
*/
|
|
220
|
+
function createUnsupportedEntry(identity, id, reason) {
|
|
221
|
+
const cadence = AUTOMATION_EXPECTED_CADENCES[id];
|
|
222
|
+
return {
|
|
223
|
+
id,
|
|
224
|
+
automationId: `${identity.automationPrefix}${id}`,
|
|
225
|
+
expectedCadence: cadence.human,
|
|
226
|
+
expectedRRule: cadence.rrule,
|
|
227
|
+
group: "exploratory",
|
|
228
|
+
reason,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @param {Record<string, any>} config
|
|
234
|
+
* @param {string | undefined} source
|
|
235
|
+
* @returns {string}
|
|
236
|
+
*/
|
|
237
|
+
function resolvePrdQueueArgument(config, source) {
|
|
238
|
+
switch (source) {
|
|
239
|
+
case "github":
|
|
240
|
+
requireGithubRepo(config);
|
|
241
|
+
return "github intake_mode=prd";
|
|
242
|
+
case "linear":
|
|
243
|
+
requireLinearWorkspace(config);
|
|
244
|
+
return "linear";
|
|
245
|
+
case "notion": {
|
|
246
|
+
const databaseId = config.notion?.prdDatabaseId;
|
|
247
|
+
if (!databaseId) {
|
|
248
|
+
throw new Error(
|
|
249
|
+
"Unable to resolve the PRD queue: notion.prdDatabaseId is required when source=notion."
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return databaseId;
|
|
253
|
+
}
|
|
254
|
+
case "confluence": {
|
|
255
|
+
const parentPageId = config.confluence?.parentPageId;
|
|
256
|
+
const spaceKey = config.confluence?.spaceKey;
|
|
257
|
+
if (!parentPageId && !spaceKey) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
"Unable to resolve the PRD queue: confluence.parentPageId or confluence.spaceKey is required when source=confluence."
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
return parentPageId ?? spaceKey;
|
|
263
|
+
}
|
|
264
|
+
case "jira": {
|
|
265
|
+
const project = config.jira?.project;
|
|
266
|
+
if (!project) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
"Unable to resolve the PRD queue: jira.project is required when source=jira."
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return project;
|
|
272
|
+
}
|
|
273
|
+
default:
|
|
274
|
+
throw new Error(
|
|
275
|
+
"Unable to resolve the PRD queue from config. Set source or use tracker=github self-host with github.org/github.repo."
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @param {Record<string, any>} config
|
|
282
|
+
* @param {string | undefined} tracker
|
|
283
|
+
* @returns {string}
|
|
284
|
+
*/
|
|
285
|
+
function resolveBuildQueueArgument(config, tracker) {
|
|
286
|
+
switch (tracker) {
|
|
287
|
+
case "github":
|
|
288
|
+
requireGithubRepo(config);
|
|
289
|
+
return "github intake_mode=build";
|
|
290
|
+
case "linear":
|
|
291
|
+
requireLinearWorkspace(config);
|
|
292
|
+
return "linear";
|
|
293
|
+
case "jira": {
|
|
294
|
+
const project = config.jira?.project;
|
|
295
|
+
if (!project) {
|
|
296
|
+
throw new Error(
|
|
297
|
+
"Unable to resolve the build queue: jira.project is required when tracker=jira."
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
return project;
|
|
301
|
+
}
|
|
302
|
+
default:
|
|
303
|
+
throw new Error(
|
|
304
|
+
"Unable to resolve the build queue from config. tracker must be github, linear, or jira."
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* @param {Record<string, any>} config
|
|
311
|
+
* @param {string | undefined} source
|
|
312
|
+
* @param {string | undefined} tracker
|
|
313
|
+
* @returns {string}
|
|
314
|
+
*/
|
|
315
|
+
function resolveRepairQueueArgument(config, source, tracker) {
|
|
316
|
+
if (tracker === "github" && source === "github") {
|
|
317
|
+
requireGithubRepo(config);
|
|
318
|
+
return "github intake_mode=both";
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (tracker === "linear" && source === "linear") {
|
|
322
|
+
requireLinearWorkspace(config);
|
|
323
|
+
return "linear";
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (tracker === "jira" && source === "jira") {
|
|
327
|
+
const project = config.jira?.project;
|
|
328
|
+
if (!project) {
|
|
329
|
+
throw new Error(
|
|
330
|
+
"Unable to resolve the repair queue: jira.project is required when tracker=jira and source=jira."
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
return project;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (tracker === "github" && source === undefined) {
|
|
337
|
+
requireGithubRepo(config);
|
|
338
|
+
return "github intake_mode=both";
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Unable to resolve a single repair-intake queue for tracker=${String(tracker)} and source=${String(source)} without guessing.`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* @param {Record<string, any>} config
|
|
348
|
+
* @returns {string | undefined}
|
|
349
|
+
*/
|
|
350
|
+
function resolvePrdSource(config) {
|
|
351
|
+
if (typeof config.source === "string" && config.source.length > 0) {
|
|
352
|
+
return config.source;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (
|
|
356
|
+
config.tracker === "github" &&
|
|
357
|
+
config.github?.org &&
|
|
358
|
+
config.github?.repo
|
|
359
|
+
) {
|
|
360
|
+
return "github";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @param {Record<string, any>} config
|
|
368
|
+
* @param {string | undefined} gitRemoteUrl
|
|
369
|
+
* @returns {{ readonly owner: string, readonly repo: string } | null}
|
|
370
|
+
*/
|
|
371
|
+
function resolveGithubRepoRef(config, gitRemoteUrl) {
|
|
372
|
+
const owner = config.github?.org;
|
|
373
|
+
const repo = config.github?.repo;
|
|
374
|
+
|
|
375
|
+
if (owner && repo) {
|
|
376
|
+
return { owner, repo };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (!gitRemoteUrl) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
for (const pattern of GITHUB_REMOTE_PATTERNS) {
|
|
384
|
+
const match = gitRemoteUrl.match(pattern);
|
|
385
|
+
if (match?.groups?.owner && match.groups.repo) {
|
|
386
|
+
return {
|
|
387
|
+
owner: match.groups.owner,
|
|
388
|
+
repo: match.groups.repo,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* @param {Record<string, any>} config
|
|
398
|
+
*/
|
|
399
|
+
function requireGithubRepo(config) {
|
|
400
|
+
if (!config.github?.org || !config.github?.repo) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
"Unable to resolve the GitHub queue: github.org and github.repo are required."
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* @param {Record<string, any>} config
|
|
409
|
+
*/
|
|
410
|
+
function requireLinearWorkspace(config) {
|
|
411
|
+
if (!config.linear?.workspace) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
"Unable to resolve the Linear queue: linear.workspace is required."
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @param {boolean | string | undefined} value
|
|
420
|
+
* @returns {boolean}
|
|
421
|
+
*/
|
|
422
|
+
function normalizeBooleanFlag(value) {
|
|
423
|
+
if (typeof value === "boolean") {
|
|
424
|
+
return value;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (typeof value === "string") {
|
|
428
|
+
return value.toLowerCase() === "true";
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* @param {string} value
|
|
436
|
+
* @returns {string}
|
|
437
|
+
*/
|
|
438
|
+
function slugifyProjectToken(value) {
|
|
439
|
+
return value
|
|
440
|
+
.trim()
|
|
441
|
+
.toLowerCase()
|
|
442
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
443
|
+
.replace(/^-+|-+$/g, "");
|
|
444
|
+
}
|