@tarcisiopgs/lisa 1.26.2 → 1.28.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.
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ checkoutBaseBranches,
4
+ runDemoLoop,
5
+ runLoop
6
+ } from "./chunk-W73XGHD4.js";
7
+ import {
8
+ WATCH_POLL_INTERVAL_MS
9
+ } from "./chunk-UXVSQQID.js";
10
+ import "./chunk-5N4BWHIT.js";
11
+ import "./chunk-3EOEDL3T.js";
12
+ import "./chunk-7OCDGYDM.js";
13
+ import "./chunk-72CYGBT4.js";
14
+ import "./chunk-2TW2MJXF.js";
15
+ import "./chunk-DGL33SDZ.js";
16
+ export {
17
+ WATCH_POLL_INTERVAL_MS,
18
+ checkoutBaseBranches,
19
+ runDemoLoop,
20
+ runLoop
21
+ };
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ buildPlanningPrompt,
4
+ createPlanIssues,
5
+ issueToMarkdown,
6
+ markdownToIssue,
7
+ parsePlanResponse,
8
+ savePlan
9
+ } from "./chunk-FCEUJ7VK.js";
10
+ import {
11
+ createSource,
12
+ resolveModels,
13
+ runWithFallback
14
+ } from "./chunk-UXVSQQID.js";
15
+ import {
16
+ error,
17
+ kanbanEmitter,
18
+ log,
19
+ ok,
20
+ warn
21
+ } from "./chunk-5N4BWHIT.js";
22
+ import "./chunk-3EOEDL3T.js";
23
+ import "./chunk-7OCDGYDM.js";
24
+ import "./chunk-72CYGBT4.js";
25
+ import "./chunk-2TW2MJXF.js";
26
+
27
+ // src/plan/tui-bridge.ts
28
+ import { execSync } from "child_process";
29
+ import { mkdtempSync, readFileSync, writeFileSync } from "fs";
30
+ import { tmpdir } from "os";
31
+ import { join, resolve } from "path";
32
+ function registerPlanBridge(config) {
33
+ const chatHistory = [];
34
+ let goal = "";
35
+ const onUserMessage = (message) => {
36
+ chatHistory.push({ role: "user", content: message });
37
+ if (!goal) goal = message;
38
+ handleUserMessage(config, goal, chatHistory).catch((err) => {
39
+ kanbanEmitter.emit(
40
+ "plan:ai-message",
41
+ `Error: ${err instanceof Error ? err.message : String(err)}`
42
+ );
43
+ });
44
+ };
45
+ const onApproved = (issues, approvedGoal) => {
46
+ handleApproval(config, issues, approvedGoal).catch((err) => {
47
+ error(`Plan approval failed: ${err instanceof Error ? err.message : String(err)}`);
48
+ });
49
+ };
50
+ const onEditIssue = (index, issue) => {
51
+ if (!issue) return;
52
+ const tmpDir = mkdtempSync(join(tmpdir(), "lisa-edit-"));
53
+ const tmpFile = join(tmpDir, "issue.md");
54
+ writeFileSync(tmpFile, issueToMarkdown(issue));
55
+ const editor = process.env.EDITOR || process.env.VISUAL || "vi";
56
+ try {
57
+ execSync(`${editor} "${tmpFile}"`, { stdio: "inherit" });
58
+ } catch {
59
+ return;
60
+ }
61
+ const content = readFileSync(tmpFile, "utf-8");
62
+ const updated = markdownToIssue(content, issue);
63
+ kanbanEmitter.emit("plan:edit-result", index, updated);
64
+ };
65
+ kanbanEmitter.on("plan:user-message", onUserMessage);
66
+ kanbanEmitter.on("plan:approved", onApproved);
67
+ kanbanEmitter.on("plan:edit-issue", onEditIssue);
68
+ return () => {
69
+ kanbanEmitter.off("plan:user-message", onUserMessage);
70
+ kanbanEmitter.off("plan:approved", onApproved);
71
+ kanbanEmitter.off("plan:edit-issue", onEditIssue);
72
+ };
73
+ }
74
+ async function handleUserMessage(config, goal, chatHistory) {
75
+ kanbanEmitter.emit("plan:thinking");
76
+ const prompt = buildChatPrompt(goal, config, chatHistory);
77
+ const models = resolveModels(config);
78
+ const logDir = mkdtempSync(join(tmpdir(), "lisa-plan-"));
79
+ const logFile = join(logDir, "plan.log");
80
+ const result = await runWithFallback(models, prompt, {
81
+ logFile,
82
+ cwd: resolve(config.workspace),
83
+ sessionTimeout: 120
84
+ });
85
+ if (!result.success) {
86
+ kanbanEmitter.emit("plan:ai-message", "Failed to get AI response. Try again.");
87
+ return;
88
+ }
89
+ try {
90
+ const issues = parsePlanResponse(result.output);
91
+ chatHistory.push({ role: "ai", content: `Decomposed into ${issues.length} issues.` });
92
+ kanbanEmitter.emit("plan:issues-ready", issues);
93
+ return;
94
+ } catch {
95
+ }
96
+ const response = extractChatResponse(result.output);
97
+ chatHistory.push({ role: "ai", content: response });
98
+ kanbanEmitter.emit("plan:ai-message", response);
99
+ }
100
+ async function handleApproval(config, issues, goal) {
101
+ const source = createSource(config.source);
102
+ if (!source.createIssue) {
103
+ error(`Source "${config.source}" does not support issue creation.`);
104
+ return;
105
+ }
106
+ const plan = {
107
+ goal,
108
+ issues,
109
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
110
+ status: "approved"
111
+ };
112
+ log("Creating issues in source...");
113
+ const createdIds = await createPlanIssues(source, config.source_config, plan);
114
+ plan.status = "created";
115
+ plan.createdIssueIds = createdIds;
116
+ savePlan(resolve(config.workspace), plan);
117
+ ok(`${createdIds.length} issue${createdIds.length !== 1 ? "s" : ""} created via plan.`);
118
+ try {
119
+ const allIssues = await source.listIssues(config.source_config);
120
+ for (const issue of allIssues) {
121
+ kanbanEmitter.emit("issue:queued", issue);
122
+ }
123
+ } catch (err) {
124
+ warn(`Could not refresh kanban: ${err instanceof Error ? err.message : String(err)}`);
125
+ }
126
+ }
127
+ function buildChatPrompt(goal, config, chatHistory) {
128
+ const basePrompt = buildPlanningPrompt(goal, config);
129
+ if (chatHistory.length <= 1) {
130
+ return `${basePrompt}
131
+
132
+ ## Chat Mode
133
+
134
+ The user has just described their goal. You have two options:
135
+
136
+ 1. **If the goal is clear and specific enough** to decompose into issues, output the JSON immediately.
137
+ 2. **If you need clarification** (which endpoints? what technology? what scope?), respond with a SHORT question (1-2 sentences). Do NOT output JSON \u2014 just plain text.
138
+
139
+ Prefer decomposing directly when possible. Only ask if the goal is genuinely ambiguous.`;
140
+ }
141
+ const historyBlock = chatHistory.map((m) => `${m.role === "user" ? "User" : "AI"}: ${m.content}`).join("\n\n");
142
+ return `${basePrompt}
143
+
144
+ ## Conversation History
145
+
146
+ ${historyBlock}
147
+
148
+ ## Instructions
149
+
150
+ Based on the conversation above, either:
151
+ 1. **Decompose into issues** \u2014 output the JSON structure defined above.
152
+ 2. **Ask one more question** \u2014 respond with SHORT plain text (1-2 sentences). No JSON.
153
+
154
+ If you have enough context, decompose now. Do not ask more than 3 questions total.`;
155
+ }
156
+ function extractChatResponse(output) {
157
+ const cleaned = output.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "").trim();
158
+ if (cleaned.length < 500) return cleaned;
159
+ const lines = cleaned.split("\n").filter((l) => l.trim().length > 0);
160
+ return lines.slice(-10).join("\n");
161
+ }
162
+ export {
163
+ registerPlanBridge
164
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.26.2",
3
+ "version": "1.28.0",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",
@@ -34,7 +34,8 @@
34
34
  "ink-spinner": "^5.0.0",
35
35
  "picocolors": "^1.1.1",
36
36
  "react": "^19.2.4",
37
- "yaml": "^2.8.2"
37
+ "yaml": "^2.8.2",
38
+ "zod": "^4.3.6"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@biomejs/biome": "^2.4.5",
@@ -43,12 +44,12 @@
43
44
  "@vitest/coverage-v8": "^4.0.18",
44
45
  "concurrently": "^9.2.1",
45
46
  "husky": "^9.1.7",
47
+ "ink-testing-library": "^4.0.0",
46
48
  "lint-staged": "^16.3.2",
47
49
  "tsup": "^8.5.1",
48
50
  "tsx": "^4.21.0",
49
51
  "typescript": "^5.9.3",
50
- "vitest": "^4.0.18",
51
- "ink-testing-library": "^4.0.0"
52
+ "vitest": "^4.0.18"
52
53
  },
53
54
  "engines": {
54
55
  "node": ">=20"