@tarcisiopgs/lisa 1.30.0 → 1.32.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/README.md +53 -4
- package/dist/{chunk-PBPVDKID.js → chunk-AAWXKEV7.js} +172 -2
- package/dist/{chunk-5GZPXARF.js → chunk-K2ILRYXS.js} +3 -164
- package/dist/{chunk-IMIDFVMI.js → chunk-NHC3JG2C.js} +387 -4
- package/dist/{chunk-IQDRQXFK.js → chunk-R6D5VH65.js} +1 -1
- package/dist/{chunk-72DVXSHO.js → chunk-YMV4CBQE.js} +94 -1
- package/dist/{detection-PC7EMUHY.js → detection-H5QJR5XI.js} +2 -2
- package/dist/index.js +270 -57
- package/dist/{kanban-OHFQGK3R.js → kanban-F27DQLKJ.js} +129 -34
- package/dist/{loop-TTRI7MFZ.js → loop-PXUNTCQS.js} +3 -3
- package/dist/{tui-bridge-LIQLGTZ2.js → tui-bridge-WKKIHWJT.js} +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,8 @@ If something fails — pre-push hooks, quota limits, stuck processes — Lisa ha
|
|
|
40
40
|
- **7 issue trackers** — Linear, GitHub Issues, GitLab Issues, Jira, Trello, Plane, Shortcut
|
|
41
41
|
- **8 AI agents** — Claude Code, Gemini CLI, GitHub Copilot CLI, Cursor Agent, Aider, Goose, OpenCode, Codex
|
|
42
42
|
- **AI planning** — describe a goal, the AI brainstorms with you, decomposes it into issues with dependencies, created in your tracker
|
|
43
|
-
- **Language-aware** —
|
|
43
|
+
- **Language-aware** — responds in the same language you write your goal in
|
|
44
|
+
- **Spec compliance** — LLM-verified acceptance criteria check before PR creation, with auto-retry
|
|
44
45
|
- **Concurrent execution** — process multiple issues in parallel, each in its own worktree
|
|
45
46
|
- **Multi-repo** — plans across repos, creates one PR per repo in the correct order
|
|
46
47
|
- **Model fallback** — chain models; transient errors (429, quota, timeout) auto-switch to the next
|
|
@@ -48,6 +49,7 @@ If something fails — pre-push hooks, quota limits, stuck processes — Lisa ha
|
|
|
48
49
|
- **CI monitoring** — polls CI after PR creation, re-invokes the agent to fix failures automatically
|
|
49
50
|
- **Progress comments** — posts real-time status updates on issues as Lisa works through stages
|
|
50
51
|
- **Context enrichment** — greps for issue-related files and surfaces them in the agent prompt
|
|
52
|
+
- **PR reviewers & assignees** — auto-request reviews and assign PRs via config; `self` keyword resolves to the authenticated user
|
|
51
53
|
- **Self-healing** — orphan recovery on startup, push failure retry, stuck process detection
|
|
52
54
|
- **Guardrails** — past failures are injected into future prompts to avoid repeating mistakes
|
|
53
55
|
- **Project context** — auto-generates `.lisa/context.md` with your stack, conventions, and constraints
|
|
@@ -101,6 +103,9 @@ lisa plan --no-brainstorm "goal" # skip brainstorming, decompose directly
|
|
|
101
103
|
lisa plan --yes "goal" # skip confirmations (CI/scripts)
|
|
102
104
|
lisa init # create .lisa/config.yaml interactively
|
|
103
105
|
lisa status # show session stats
|
|
106
|
+
lisa config --show # print current config
|
|
107
|
+
lisa config --get loop.cooldown # query a specific value
|
|
108
|
+
lisa config --set loop.cooldown=5 # set nested config values
|
|
104
109
|
lisa doctor # diagnose setup issues (config, provider, env, git)
|
|
105
110
|
lisa context refresh # regenerate project context
|
|
106
111
|
lisa feedback --pr URL # inject PR review feedback into guardrails
|
|
@@ -235,6 +240,11 @@ proof_of_work:
|
|
|
235
240
|
validation:
|
|
236
241
|
require_acceptance_criteria: true
|
|
237
242
|
|
|
243
|
+
spec_compliance:
|
|
244
|
+
enabled: true
|
|
245
|
+
max_retries: 1 # retry agent to fix unmet criteria (default: 1)
|
|
246
|
+
block_on_failure: true # skip PR when criteria aren't met (default: false)
|
|
247
|
+
|
|
238
248
|
ci_monitor:
|
|
239
249
|
enabled: true
|
|
240
250
|
max_retries: 3 # fix attempts on CI failure
|
|
@@ -244,10 +254,41 @@ ci_monitor:
|
|
|
244
254
|
|
|
245
255
|
progress_comments:
|
|
246
256
|
enabled: true # post real-time status on issues
|
|
257
|
+
|
|
258
|
+
pr:
|
|
259
|
+
reviewers: # auto-request reviews on every PR
|
|
260
|
+
- octocat
|
|
261
|
+
- hubot
|
|
262
|
+
assignees: # auto-assign PRs ("self" = authenticated user)
|
|
263
|
+
- self
|
|
264
|
+
|
|
265
|
+
hooks:
|
|
266
|
+
before_run: "./scripts/setup.sh"
|
|
267
|
+
after_run: "./scripts/cleanup.sh"
|
|
268
|
+
timeout: 60000 # ms, default 60000
|
|
247
269
|
```
|
|
248
270
|
|
|
249
271
|
</details>
|
|
250
272
|
|
|
273
|
+
## Validation Pipeline
|
|
274
|
+
|
|
275
|
+
After the agent implements an issue, Lisa runs a multi-stage validation pipeline before creating a PR:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
Agent implements → Proof of Work (lint/test/typecheck) → Spec Compliance → PR
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Proof of Work** runs configured shell commands (lint, typecheck, test). If any fail, the agent is re-invoked with the error output to fix the issue.
|
|
282
|
+
|
|
283
|
+
**Spec Compliance** extracts acceptance criteria from the issue description (`- [ ]` checklists) and asks the LLM to verify each one against the git diff. The result is a structured JSON with met/not-met verdicts and evidence. If criteria are unmet, the agent is re-invoked to fix them. Results are appended to the PR body as a Markdown table:
|
|
284
|
+
|
|
285
|
+
| Criterion | Status | Evidence |
|
|
286
|
+
|-----------|--------|----------|
|
|
287
|
+
| Returns 429 on rate limit | Met | Rate limit middleware returns 429 |
|
|
288
|
+
| Headers include X-RateLimit | Not Met | No header injection found |
|
|
289
|
+
|
|
290
|
+
Both stages support `max_retries` and `block_on_failure` — when blocking is enabled, the PR is skipped entirely on failure.
|
|
291
|
+
|
|
251
292
|
## Writing Good Issues
|
|
252
293
|
|
|
253
294
|
Issue quality = PR quality. Lisa validates issues and skips vague ones (labeling them `needs-spec`).
|
|
@@ -290,15 +331,23 @@ The real-time Kanban board shows issue progress, streams provider output, and de
|
|
|
290
331
|
| `m` | Merge PR (warns if CI not passed) |
|
|
291
332
|
| `Esc` | Back to board |
|
|
292
333
|
|
|
293
|
-
**Plan
|
|
334
|
+
**Plan chat**
|
|
335
|
+
|
|
336
|
+
| Key | Action |
|
|
337
|
+
|-----|--------|
|
|
338
|
+
| `↵` | Send message |
|
|
339
|
+
| `↑` `↓` | Scroll chat history |
|
|
340
|
+
| `Esc` | Cancel |
|
|
341
|
+
|
|
342
|
+
**Plan review**
|
|
294
343
|
|
|
295
344
|
| Key | Action |
|
|
296
345
|
|-----|--------|
|
|
297
|
-
| `↵` |
|
|
346
|
+
| `↵` | View issue detail |
|
|
298
347
|
| `e` | Edit issue in $EDITOR |
|
|
299
348
|
| `d` | Delete issue |
|
|
300
349
|
| `a` | Approve and create issues |
|
|
301
|
-
| `Esc` |
|
|
350
|
+
| `Esc` | Back |
|
|
302
351
|
|
|
303
352
|
In CLI mode, the plan wizard also offers **Regenerate with feedback** — describe what to change and the AI regenerates the entire plan incorporating your feedback.
|
|
304
353
|
|
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
analyzeProject,
|
|
5
5
|
appendPlatformAttribution,
|
|
6
6
|
appendPlatformProofOfWork,
|
|
7
|
+
appendPlatformSpecCompliance,
|
|
8
|
+
applyPrReviewersAndAssignees,
|
|
9
|
+
buildCompliancePrompt,
|
|
10
|
+
buildComplianceRecoveryPrompt,
|
|
7
11
|
buildContinuationPrompt,
|
|
8
12
|
buildImplementPrompt,
|
|
9
13
|
buildNativeWorktreePrompt,
|
|
@@ -15,14 +19,18 @@ import {
|
|
|
15
19
|
createSource,
|
|
16
20
|
detectPackageManager,
|
|
17
21
|
detectTestRunner,
|
|
22
|
+
extractAcceptanceCriteria,
|
|
18
23
|
getContextPath,
|
|
24
|
+
getFullDiff,
|
|
19
25
|
isCompleteProviderExhaustion,
|
|
20
26
|
isProofOfWorkEnabled,
|
|
27
|
+
isSpecComplianceEnabled,
|
|
28
|
+
parseComplianceResponse,
|
|
21
29
|
readContext,
|
|
22
30
|
resolveModels,
|
|
23
31
|
runValidationCommands,
|
|
24
32
|
runWithFallback
|
|
25
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-NHC3JG2C.js";
|
|
26
34
|
import {
|
|
27
35
|
divider,
|
|
28
36
|
error,
|
|
@@ -185,6 +193,23 @@ function findConfigDir(startDir = process.cwd()) {
|
|
|
185
193
|
dir = parent;
|
|
186
194
|
}
|
|
187
195
|
}
|
|
196
|
+
var USERNAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,38}$/;
|
|
197
|
+
function isValidPrUsername(s) {
|
|
198
|
+
return s === "self" || USERNAME_RE.test(s);
|
|
199
|
+
}
|
|
200
|
+
function isStringArray(v) {
|
|
201
|
+
return Array.isArray(v) && v.every((item) => typeof item === "string");
|
|
202
|
+
}
|
|
203
|
+
function parsePrConfig(raw) {
|
|
204
|
+
if (!raw) return void 0;
|
|
205
|
+
const reviewers = isStringArray(raw.reviewers) ? raw.reviewers.filter(isValidPrUsername) : void 0;
|
|
206
|
+
const assignees = isStringArray(raw.assignees) ? raw.assignees.filter(isValidPrUsername) : void 0;
|
|
207
|
+
if (!reviewers?.length && !assignees?.length) return void 0;
|
|
208
|
+
return {
|
|
209
|
+
reviewers: reviewers?.length ? reviewers : void 0,
|
|
210
|
+
assignees: assignees?.length ? assignees : void 0
|
|
211
|
+
};
|
|
212
|
+
}
|
|
188
213
|
function loadConfig(cwd = process.cwd()) {
|
|
189
214
|
const configPath = getConfigPath(cwd);
|
|
190
215
|
if (!existsSync(configPath)) {
|
|
@@ -225,7 +250,9 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
225
250
|
const rawProofOfWork = parsed.proof_of_work;
|
|
226
251
|
const rawReconciliation = parsed.reconciliation;
|
|
227
252
|
const rawCiMonitor = parsed.ci_monitor;
|
|
253
|
+
const rawSpecCompliance = parsed.spec_compliance;
|
|
228
254
|
const rawProgress = parsed.progress_comments;
|
|
255
|
+
const rawPr = parsed.pr;
|
|
229
256
|
const config = {
|
|
230
257
|
...DEFAULT_CONFIG,
|
|
231
258
|
...parsedWithoutLogs,
|
|
@@ -265,7 +292,13 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
265
292
|
poll_timeout: rawCiMonitor.poll_timeout,
|
|
266
293
|
block_on_failure: rawCiMonitor.block_on_failure
|
|
267
294
|
} : void 0,
|
|
295
|
+
spec_compliance: rawSpecCompliance ? {
|
|
296
|
+
enabled: rawSpecCompliance.enabled ?? false,
|
|
297
|
+
max_retries: rawSpecCompliance.max_retries,
|
|
298
|
+
block_on_failure: rawSpecCompliance.block_on_failure
|
|
299
|
+
} : void 0,
|
|
268
300
|
progress_comments: rawProgress ? { enabled: rawProgress.enabled ?? false } : void 0,
|
|
301
|
+
pr: parsePrConfig(rawPr),
|
|
269
302
|
provider_options: {
|
|
270
303
|
...DEFAULT_CONFIG.provider_options || {},
|
|
271
304
|
...parsed.provider_options ?? {}
|
|
@@ -1175,7 +1208,7 @@ async function startInfra(issueId, cwd, config) {
|
|
|
1175
1208
|
function startReconciliationMonitor(source, issueId, config) {
|
|
1176
1209
|
return source && config.reconciliation?.enabled ? startReconciliation(source, issueId, config.reconciliation, config.source_config) : null;
|
|
1177
1210
|
}
|
|
1178
|
-
async function runProofOfWork(config, issue, models, cwd, logFile, workspace, lifecycleEnv,
|
|
1211
|
+
async function runProofOfWork(config, issue, models, cwd, logFile, workspace, lifecycleEnv, _result) {
|
|
1179
1212
|
if (!isProofOfWorkEnabled(config.proof_of_work)) return {};
|
|
1180
1213
|
const pow = config.proof_of_work;
|
|
1181
1214
|
let retriesLeft = pow?.max_retries ?? 2;
|
|
@@ -1229,6 +1262,90 @@ async function runProofOfWork(config, issue, models, cwd, logFile, workspace, li
|
|
|
1229
1262
|
}
|
|
1230
1263
|
}
|
|
1231
1264
|
}
|
|
1265
|
+
async function runSpecCompliance(config, issue, models, cwd, baseBranch, logFile, workspace, lifecycleEnv) {
|
|
1266
|
+
if (!isSpecComplianceEnabled(config.spec_compliance)) return {};
|
|
1267
|
+
const criteria = extractAcceptanceCriteria(issue.description);
|
|
1268
|
+
if (criteria.length === 0) {
|
|
1269
|
+
warn(`No extractable acceptance criteria for ${issue.id}. Skipping spec compliance.`);
|
|
1270
|
+
return {};
|
|
1271
|
+
}
|
|
1272
|
+
const sc = config.spec_compliance;
|
|
1273
|
+
let retriesLeft = sc?.max_retries ?? 1;
|
|
1274
|
+
while (true) {
|
|
1275
|
+
if (reconciliationSet.has(issue.id)) {
|
|
1276
|
+
reconciliationSet.delete(issue.id);
|
|
1277
|
+
warn(`Issue ${issue.id} was closed/cancelled during spec compliance. Skipping.`);
|
|
1278
|
+
return { reconciled: true };
|
|
1279
|
+
}
|
|
1280
|
+
startSpinner(`${issue.id} \u2014 checking spec compliance...`);
|
|
1281
|
+
const diff = await getFullDiff(cwd, baseBranch);
|
|
1282
|
+
if (!diff) {
|
|
1283
|
+
stopSpinner();
|
|
1284
|
+
warn(`No diff available for spec compliance on ${issue.id}. Skipping.`);
|
|
1285
|
+
return {};
|
|
1286
|
+
}
|
|
1287
|
+
const compliancePrompt = buildCompliancePrompt(issue, criteria, diff);
|
|
1288
|
+
const checkResult = await runWithFallback(
|
|
1289
|
+
models,
|
|
1290
|
+
compliancePrompt,
|
|
1291
|
+
buildRunOptions(config, issue, cwd, logFile, workspace, lifecycleEnv)
|
|
1292
|
+
);
|
|
1293
|
+
stopSpinner();
|
|
1294
|
+
if (!checkResult.success) {
|
|
1295
|
+
warn(`Spec compliance check failed to run for ${issue.id}. Proceeding without it.`);
|
|
1296
|
+
return {};
|
|
1297
|
+
}
|
|
1298
|
+
const compliance = parseComplianceResponse(checkResult.output);
|
|
1299
|
+
if (!compliance) {
|
|
1300
|
+
warn(
|
|
1301
|
+
`Could not parse spec compliance response for ${issue.id}. Proceeding without it.`
|
|
1302
|
+
);
|
|
1303
|
+
return {};
|
|
1304
|
+
}
|
|
1305
|
+
if (compliance.passed) {
|
|
1306
|
+
ok(`Spec compliance passed for ${issue.id}: ${compliance.summary}`);
|
|
1307
|
+
return { result: compliance };
|
|
1308
|
+
}
|
|
1309
|
+
const unmet = compliance.criteria.filter((c) => !c.met);
|
|
1310
|
+
warn(
|
|
1311
|
+
`Spec compliance failed for ${issue.id}: ${compliance.summary} (${unmet.length} unmet)`
|
|
1312
|
+
);
|
|
1313
|
+
if (retriesLeft <= 0) {
|
|
1314
|
+
if (sc?.block_on_failure) {
|
|
1315
|
+
error(
|
|
1316
|
+
`Spec compliance failed after max retries for ${issue.id}. Blocking PR creation.`
|
|
1317
|
+
);
|
|
1318
|
+
return { result: compliance, blocked: true };
|
|
1319
|
+
}
|
|
1320
|
+
warn(
|
|
1321
|
+
`Spec compliance failed after max retries for ${issue.id}. Creating PR with failures noted.`
|
|
1322
|
+
);
|
|
1323
|
+
return { result: compliance };
|
|
1324
|
+
}
|
|
1325
|
+
retriesLeft--;
|
|
1326
|
+
warn(
|
|
1327
|
+
`Re-invoking agent to fix unmet criteria for ${issue.id} (${retriesLeft} retries left)...`
|
|
1328
|
+
);
|
|
1329
|
+
const recoveryPrompt = buildComplianceRecoveryPrompt(issue, unmet);
|
|
1330
|
+
startSpinner(`${issue.id} \u2014 fixing unmet criteria...`);
|
|
1331
|
+
const recoveryResult = await runWithFallback(
|
|
1332
|
+
models,
|
|
1333
|
+
recoveryPrompt,
|
|
1334
|
+
buildRunOptions(config, issue, cwd, logFile, workspace, lifecycleEnv)
|
|
1335
|
+
);
|
|
1336
|
+
stopSpinner();
|
|
1337
|
+
if (!recoveryResult.success) {
|
|
1338
|
+
if (sc?.block_on_failure) {
|
|
1339
|
+
error(`Spec compliance recovery failed for ${issue.id}. Blocking PR creation.`);
|
|
1340
|
+
return { result: compliance, blocked: true };
|
|
1341
|
+
}
|
|
1342
|
+
warn(
|
|
1343
|
+
`Spec compliance recovery failed for ${issue.id}. Creating PR with failures noted.`
|
|
1344
|
+
);
|
|
1345
|
+
return { result: compliance };
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1232
1349
|
|
|
1233
1350
|
// src/loop/recovery.ts
|
|
1234
1351
|
async function injectRejectedPrFeedback(workspace, issueId, prUrls) {
|
|
@@ -2480,6 +2597,7 @@ ${contResult.output}
|
|
|
2480
2597
|
await executeHook("before_remove", config.hooks, worktreePath, hookEnv);
|
|
2481
2598
|
await cleanupWorktree(repoPath, worktreePath);
|
|
2482
2599
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
2600
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
2483
2601
|
ok(`Step ${stepNum} complete: ${repoPath} \u2014 PR: ${prUrl}`);
|
|
2484
2602
|
return {
|
|
2485
2603
|
success: true,
|
|
@@ -2649,6 +2767,7 @@ async function runNativeWorktreeSession(config, issue, logFile, session, models,
|
|
|
2649
2767
|
}
|
|
2650
2768
|
ok(`PR created by provider: ${prUrl}`);
|
|
2651
2769
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
2770
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
2652
2771
|
if (isCiMonitorEnabled(config.ci_monitor)) {
|
|
2653
2772
|
const manifestBranch = manifest?.branch;
|
|
2654
2773
|
if (manifestBranch) {
|
|
@@ -2794,6 +2913,29 @@ async function runManualWorktreeSession(config, issue, logFile, session, models,
|
|
|
2794
2913
|
if (validationResults) {
|
|
2795
2914
|
await reporter.update("validating", "Validation passed");
|
|
2796
2915
|
}
|
|
2916
|
+
const complianceResult = await runSpecCompliance(
|
|
2917
|
+
config,
|
|
2918
|
+
issue,
|
|
2919
|
+
models,
|
|
2920
|
+
worktreePath,
|
|
2921
|
+
baseBranch,
|
|
2922
|
+
logFile,
|
|
2923
|
+
workspace,
|
|
2924
|
+
lifecycleEnv
|
|
2925
|
+
);
|
|
2926
|
+
if (complianceResult.reconciled) {
|
|
2927
|
+
await cleanupWorktree(repoPath, worktreePath);
|
|
2928
|
+
return failureResult(result.providerUsed, result);
|
|
2929
|
+
}
|
|
2930
|
+
if (complianceResult.blocked) {
|
|
2931
|
+
error(
|
|
2932
|
+
`Skipping PR for ${issue.id} \u2014 spec compliance failed with block_on_failure enabled.`
|
|
2933
|
+
);
|
|
2934
|
+
await reporter.fail("Spec compliance failed");
|
|
2935
|
+
await executeHook("before_remove", config.hooks, worktreePath, hookEnv);
|
|
2936
|
+
await cleanupWorktree(repoPath, worktreePath);
|
|
2937
|
+
return failureResult(result.providerUsed, result);
|
|
2938
|
+
}
|
|
2797
2939
|
const manifest = readManifestFile(manifestPath);
|
|
2798
2940
|
let prUrl = manifest?.prUrl;
|
|
2799
2941
|
if (!prUrl) {
|
|
@@ -2847,9 +2989,13 @@ async function runManualWorktreeSession(config, issue, logFile, session, models,
|
|
|
2847
2989
|
}
|
|
2848
2990
|
ok(`PR created by provider: ${prUrl}`);
|
|
2849
2991
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
2992
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
2850
2993
|
if (validationResults) {
|
|
2851
2994
|
await appendPlatformProofOfWork(prUrl, validationResults, config.platform);
|
|
2852
2995
|
}
|
|
2996
|
+
if (complianceResult.result) {
|
|
2997
|
+
await appendPlatformSpecCompliance(prUrl, complianceResult.result, config.platform);
|
|
2998
|
+
}
|
|
2853
2999
|
if (isCiMonitorEnabled(config.ci_monitor)) {
|
|
2854
3000
|
const manifestBranch = manifest?.branch ?? branchName;
|
|
2855
3001
|
const ciResult = await monitorCi(
|
|
@@ -3308,6 +3454,26 @@ async function runBranchSession(config, issue, logFile, session, models, source,
|
|
|
3308
3454
|
if (validationResults) {
|
|
3309
3455
|
await reporter.update("validating", "Validation passed");
|
|
3310
3456
|
}
|
|
3457
|
+
const complianceResult = await runSpecCompliance(
|
|
3458
|
+
config,
|
|
3459
|
+
issue,
|
|
3460
|
+
models,
|
|
3461
|
+
workspace,
|
|
3462
|
+
config.base_branch,
|
|
3463
|
+
logFile,
|
|
3464
|
+
workspace,
|
|
3465
|
+
lifecycleEnv
|
|
3466
|
+
);
|
|
3467
|
+
if (complianceResult.reconciled) {
|
|
3468
|
+
return failureResult(result.providerUsed, result);
|
|
3469
|
+
}
|
|
3470
|
+
if (complianceResult.blocked) {
|
|
3471
|
+
error(
|
|
3472
|
+
`Skipping PR for ${issue.id} \u2014 spec compliance failed with block_on_failure enabled.`
|
|
3473
|
+
);
|
|
3474
|
+
await reporter.fail("Spec compliance failed");
|
|
3475
|
+
return failureResult(result.providerUsed, result);
|
|
3476
|
+
}
|
|
3311
3477
|
const manifest = readManifestFile(manifestPath);
|
|
3312
3478
|
try {
|
|
3313
3479
|
unlinkSync3(manifestPath);
|
|
@@ -3327,9 +3493,13 @@ async function runBranchSession(config, issue, logFile, session, models, source,
|
|
|
3327
3493
|
}
|
|
3328
3494
|
ok(`PR created by provider: ${prUrl}`);
|
|
3329
3495
|
await appendPlatformAttribution(prUrl, result.providerUsed, config.platform);
|
|
3496
|
+
await applyPrReviewersAndAssignees(prUrl, config.pr, config.platform);
|
|
3330
3497
|
if (validationResults) {
|
|
3331
3498
|
await appendPlatformProofOfWork(prUrl, validationResults, config.platform);
|
|
3332
3499
|
}
|
|
3500
|
+
if (complianceResult.result) {
|
|
3501
|
+
await appendPlatformSpecCompliance(prUrl, complianceResult.result, config.platform);
|
|
3502
|
+
}
|
|
3333
3503
|
if (isCiMonitorEnabled(config.ci_monitor)) {
|
|
3334
3504
|
const manifestBranch = manifest?.branch;
|
|
3335
3505
|
if (manifestBranch) {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
readContext,
|
|
5
5
|
resolveModels,
|
|
6
6
|
runWithFallback
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-NHC3JG2C.js";
|
|
8
8
|
import {
|
|
9
9
|
error,
|
|
10
10
|
log,
|
|
@@ -208,164 +208,7 @@ _Depends on: ${depRefs}_`;
|
|
|
208
208
|
|
|
209
209
|
// src/plan/prompt.ts
|
|
210
210
|
import { resolve } from "path";
|
|
211
|
-
|
|
212
|
-
// src/plan/language.ts
|
|
213
|
-
var STOP_WORDS = {
|
|
214
|
-
pt: /* @__PURE__ */ new Set([
|
|
215
|
-
"n\xE3o",
|
|
216
|
-
"tamb\xE9m",
|
|
217
|
-
"j\xE1",
|
|
218
|
-
"est\xE1",
|
|
219
|
-
"s\xE3o",
|
|
220
|
-
"nos",
|
|
221
|
-
"das",
|
|
222
|
-
"dos",
|
|
223
|
-
"pelo",
|
|
224
|
-
"pela",
|
|
225
|
-
"uma",
|
|
226
|
-
"nas",
|
|
227
|
-
"aos",
|
|
228
|
-
"essa",
|
|
229
|
-
"esse",
|
|
230
|
-
"isso",
|
|
231
|
-
"aqui",
|
|
232
|
-
"muito",
|
|
233
|
-
"quando",
|
|
234
|
-
"como",
|
|
235
|
-
"mais",
|
|
236
|
-
"ainda",
|
|
237
|
-
"fazer",
|
|
238
|
-
"deve",
|
|
239
|
-
"pode",
|
|
240
|
-
"cada",
|
|
241
|
-
"todos",
|
|
242
|
-
"todas",
|
|
243
|
-
"entre",
|
|
244
|
-
"ap\xF3s",
|
|
245
|
-
"sobre",
|
|
246
|
-
"seus",
|
|
247
|
-
"suas",
|
|
248
|
-
"desta",
|
|
249
|
-
"deste",
|
|
250
|
-
"onde",
|
|
251
|
-
"apenas",
|
|
252
|
-
// Common contractions (preposition + article)
|
|
253
|
-
"na",
|
|
254
|
-
"no",
|
|
255
|
-
"da",
|
|
256
|
-
"do",
|
|
257
|
-
"ao",
|
|
258
|
-
"num",
|
|
259
|
-
"numa",
|
|
260
|
-
"para",
|
|
261
|
-
"com",
|
|
262
|
-
"sem",
|
|
263
|
-
"ou"
|
|
264
|
-
]),
|
|
265
|
-
es: /* @__PURE__ */ new Set([
|
|
266
|
-
"tambi\xE9n",
|
|
267
|
-
"m\xE1s",
|
|
268
|
-
"pero",
|
|
269
|
-
"muy",
|
|
270
|
-
"est\xE1",
|
|
271
|
-
"est\xE1n",
|
|
272
|
-
"puede",
|
|
273
|
-
"todo",
|
|
274
|
-
"esta",
|
|
275
|
-
"este",
|
|
276
|
-
"como",
|
|
277
|
-
"cuando",
|
|
278
|
-
"donde",
|
|
279
|
-
"cada",
|
|
280
|
-
"entre",
|
|
281
|
-
"sobre",
|
|
282
|
-
"despu\xE9s",
|
|
283
|
-
"antes",
|
|
284
|
-
"desde",
|
|
285
|
-
"hasta",
|
|
286
|
-
"seg\xFAn",
|
|
287
|
-
"durante",
|
|
288
|
-
"todos",
|
|
289
|
-
"todas",
|
|
290
|
-
"otro",
|
|
291
|
-
"otra",
|
|
292
|
-
"otros",
|
|
293
|
-
"otras",
|
|
294
|
-
"hacer",
|
|
295
|
-
"debe",
|
|
296
|
-
"aqu\xED",
|
|
297
|
-
"ahora",
|
|
298
|
-
"siempre",
|
|
299
|
-
"nunca"
|
|
300
|
-
]),
|
|
301
|
-
en: /* @__PURE__ */ new Set([
|
|
302
|
-
"the",
|
|
303
|
-
"is",
|
|
304
|
-
"are",
|
|
305
|
-
"was",
|
|
306
|
-
"were",
|
|
307
|
-
"been",
|
|
308
|
-
"being",
|
|
309
|
-
"have",
|
|
310
|
-
"has",
|
|
311
|
-
"had",
|
|
312
|
-
"having",
|
|
313
|
-
"does",
|
|
314
|
-
"did",
|
|
315
|
-
"will",
|
|
316
|
-
"would",
|
|
317
|
-
"could",
|
|
318
|
-
"should",
|
|
319
|
-
"might",
|
|
320
|
-
"shall",
|
|
321
|
-
"this",
|
|
322
|
-
"that",
|
|
323
|
-
"these",
|
|
324
|
-
"those",
|
|
325
|
-
"with",
|
|
326
|
-
"from",
|
|
327
|
-
"into",
|
|
328
|
-
"through",
|
|
329
|
-
"during",
|
|
330
|
-
"before",
|
|
331
|
-
"after",
|
|
332
|
-
"above",
|
|
333
|
-
"below",
|
|
334
|
-
"between",
|
|
335
|
-
"each",
|
|
336
|
-
"every",
|
|
337
|
-
"which",
|
|
338
|
-
"when",
|
|
339
|
-
"where",
|
|
340
|
-
"while"
|
|
341
|
-
])
|
|
342
|
-
};
|
|
343
|
-
var LANGUAGE_NAMES = {
|
|
344
|
-
pt: "Portuguese",
|
|
345
|
-
es: "Spanish",
|
|
346
|
-
en: "English"
|
|
347
|
-
};
|
|
348
|
-
function detectLanguage(text2) {
|
|
349
|
-
const words = text2.toLowerCase().replace(/[^\p{L}\s]/gu, "").split(/\s+/).filter((w) => w.length > 1);
|
|
350
|
-
if (words.length === 0) return "en";
|
|
351
|
-
const scores = { pt: 0, es: 0, en: 0 };
|
|
352
|
-
for (const word of words) {
|
|
353
|
-
for (const [lang, stopWords] of Object.entries(STOP_WORDS)) {
|
|
354
|
-
if (stopWords.has(word)) {
|
|
355
|
-
scores[lang]++;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
const best = Object.entries(scores).sort((a, b) => b[1] - a[1])[0];
|
|
360
|
-
return best[1] > 0 ? best[0] : "en";
|
|
361
|
-
}
|
|
362
|
-
function languageName(code) {
|
|
363
|
-
return LANGUAGE_NAMES[code] ?? "English";
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// src/plan/prompt.ts
|
|
367
211
|
function buildPlanningPrompt(goal, config, parentIssueDescription) {
|
|
368
|
-
const language = detectLanguage(goal);
|
|
369
212
|
const workspace = resolve(config.workspace);
|
|
370
213
|
const contextMd = readContext(workspace);
|
|
371
214
|
const contextBlock = buildContextMdBlock(contextMd);
|
|
@@ -379,6 +222,8 @@ ${parentIssueDescription}
|
|
|
379
222
|
` : "";
|
|
380
223
|
return `You are a project planning agent. Your job is to decompose a high-level goal into atomic, implementable issues.
|
|
381
224
|
|
|
225
|
+
Always respond in the same language the user wrote their goal in.
|
|
226
|
+
|
|
382
227
|
## Goal
|
|
383
228
|
|
|
384
229
|
${goal}
|
|
@@ -399,10 +244,6 @@ For each issue, provide:
|
|
|
399
244
|
- **order**: Integer (1-based) \u2014 execution order based on dependencies
|
|
400
245
|
- **dependsOn**: Array of order numbers this issue depends on (empty if independent)
|
|
401
246
|
${config.repos.length > 1 ? "- **repo**: Name of the target repository from the list above (required for multi-repo)\n" : ""}
|
|
402
|
-
## Language
|
|
403
|
-
|
|
404
|
-
Respond in ${languageName(language)}. Generate all issue titles, descriptions, and acceptance criteria in ${languageName(language)}.
|
|
405
|
-
|
|
406
247
|
## Rules
|
|
407
248
|
|
|
408
249
|
1. Each issue MUST be self-contained and completable in a single session
|
|
@@ -751,8 +592,6 @@ function markdownToIssue(content, original) {
|
|
|
751
592
|
|
|
752
593
|
export {
|
|
753
594
|
CliError,
|
|
754
|
-
detectLanguage,
|
|
755
|
-
languageName,
|
|
756
595
|
parseStructuredOutput,
|
|
757
596
|
createPlanIssues,
|
|
758
597
|
buildPlanningPrompt,
|