im-pickle-rick 0.1.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 +242 -0
- package/bin.js +3 -0
- package/dist/pickle +0 -0
- package/dist/worker-executor.js +207 -0
- package/package.json +53 -0
- package/src/games/GameSidebarManager.test.ts +64 -0
- package/src/games/GameSidebarManager.ts +78 -0
- package/src/games/gameboy/GameboyView.test.ts +25 -0
- package/src/games/gameboy/GameboyView.ts +100 -0
- package/src/games/gameboy/gameboy-polyfills.ts +313 -0
- package/src/games/index.test.ts +9 -0
- package/src/games/index.ts +4 -0
- package/src/games/snake/SnakeGame.test.ts +35 -0
- package/src/games/snake/SnakeGame.ts +145 -0
- package/src/games/snake/SnakeView.test.ts +25 -0
- package/src/games/snake/SnakeView.ts +290 -0
- package/src/index.test.ts +24 -0
- package/src/index.ts +141 -0
- package/src/services/commands/worker.test.ts +14 -0
- package/src/services/commands/worker.ts +262 -0
- package/src/services/config/index.ts +2 -0
- package/src/services/config/settings.test.ts +42 -0
- package/src/services/config/settings.ts +220 -0
- package/src/services/config/state.test.ts +88 -0
- package/src/services/config/state.ts +130 -0
- package/src/services/config/types.ts +39 -0
- package/src/services/execution/index.ts +1 -0
- package/src/services/execution/pickle-source.test.ts +88 -0
- package/src/services/execution/pickle-source.ts +264 -0
- package/src/services/execution/prompt.test.ts +93 -0
- package/src/services/execution/prompt.ts +322 -0
- package/src/services/execution/sequential.test.ts +91 -0
- package/src/services/execution/sequential.ts +422 -0
- package/src/services/execution/worker-client.ts +94 -0
- package/src/services/execution/worker-executor.ts +41 -0
- package/src/services/execution/worker.test.ts +73 -0
- package/src/services/git/branch.test.ts +147 -0
- package/src/services/git/branch.ts +128 -0
- package/src/services/git/diff.test.ts +113 -0
- package/src/services/git/diff.ts +323 -0
- package/src/services/git/index.ts +4 -0
- package/src/services/git/pr.test.ts +104 -0
- package/src/services/git/pr.ts +192 -0
- package/src/services/git/worktree.test.ts +99 -0
- package/src/services/git/worktree.ts +141 -0
- package/src/services/providers/base.test.ts +86 -0
- package/src/services/providers/base.ts +438 -0
- package/src/services/providers/codex.test.ts +39 -0
- package/src/services/providers/codex.ts +208 -0
- package/src/services/providers/gemini.test.ts +40 -0
- package/src/services/providers/gemini.ts +169 -0
- package/src/services/providers/index.test.ts +28 -0
- package/src/services/providers/index.ts +41 -0
- package/src/services/providers/opencode.test.ts +64 -0
- package/src/services/providers/opencode.ts +228 -0
- package/src/services/providers/types.ts +44 -0
- package/src/skills/code-implementer.md +105 -0
- package/src/skills/code-researcher.md +78 -0
- package/src/skills/implementation-planner.md +105 -0
- package/src/skills/plan-reviewer.md +100 -0
- package/src/skills/prd-drafter.md +123 -0
- package/src/skills/research-reviewer.md +79 -0
- package/src/skills/ruthless-refactorer.md +52 -0
- package/src/skills/ticket-manager.md +135 -0
- package/src/types/index.ts +2 -0
- package/src/types/rpc.ts +14 -0
- package/src/types/tasks.ts +50 -0
- package/src/types.d.ts +9 -0
- package/src/ui/common.ts +28 -0
- package/src/ui/components/FilePickerView.test.ts +79 -0
- package/src/ui/components/FilePickerView.ts +161 -0
- package/src/ui/components/MultiLineInput.test.ts +27 -0
- package/src/ui/components/MultiLineInput.ts +233 -0
- package/src/ui/components/SessionChip.test.ts +69 -0
- package/src/ui/components/SessionChip.ts +481 -0
- package/src/ui/components/ToyboxSidebar.test.ts +36 -0
- package/src/ui/components/ToyboxSidebar.ts +329 -0
- package/src/ui/components/refactor_plan.md +35 -0
- package/src/ui/controllers/DashboardController.integration.test.ts +43 -0
- package/src/ui/controllers/DashboardController.ts +650 -0
- package/src/ui/dashboard.test.ts +43 -0
- package/src/ui/dashboard.ts +309 -0
- package/src/ui/dialogs/DashboardDialog.test.ts +146 -0
- package/src/ui/dialogs/DashboardDialog.ts +399 -0
- package/src/ui/dialogs/Dialog.test.ts +50 -0
- package/src/ui/dialogs/Dialog.ts +241 -0
- package/src/ui/dialogs/DialogSidebar.test.ts +60 -0
- package/src/ui/dialogs/DialogSidebar.ts +71 -0
- package/src/ui/dialogs/DiffViewDialog.test.ts +57 -0
- package/src/ui/dialogs/DiffViewDialog.ts +510 -0
- package/src/ui/dialogs/PRPreviewDialog.test.ts +50 -0
- package/src/ui/dialogs/PRPreviewDialog.ts +346 -0
- package/src/ui/dialogs/test-utils.ts +232 -0
- package/src/ui/file-picker-utils.test.ts +71 -0
- package/src/ui/file-picker-utils.ts +200 -0
- package/src/ui/input-chrome.test.ts +62 -0
- package/src/ui/input-chrome.ts +172 -0
- package/src/ui/logger.test.ts +68 -0
- package/src/ui/logger.ts +45 -0
- package/src/ui/mock-factory.ts +6 -0
- package/src/ui/spinner.test.ts +65 -0
- package/src/ui/spinner.ts +41 -0
- package/src/ui/test-setup.ts +300 -0
- package/src/ui/theme.test.ts +23 -0
- package/src/ui/theme.ts +16 -0
- package/src/ui/views/LandingView.integration.test.ts +21 -0
- package/src/ui/views/LandingView.test.ts +24 -0
- package/src/ui/views/LandingView.ts +221 -0
- package/src/ui/views/LogView.test.ts +24 -0
- package/src/ui/views/LogView.ts +277 -0
- package/src/ui/views/ToyboxView.test.ts +46 -0
- package/src/ui/views/ToyboxView.ts +323 -0
- package/src/utils/clipboard.test.ts +86 -0
- package/src/utils/clipboard.ts +100 -0
- package/src/utils/index.test.ts +68 -0
- package/src/utils/index.ts +95 -0
- package/src/utils/persona.test.ts +12 -0
- package/src/utils/persona.ts +8 -0
- package/src/utils/project-root.test.ts +38 -0
- package/src/utils/project-root.ts +22 -0
- package/src/utils/resources.test.ts +64 -0
- package/src/utils/resources.ts +92 -0
- package/src/utils/search.test.ts +48 -0
- package/src/utils/search.ts +103 -0
- package/src/utils/session-tracker.test.ts +46 -0
- package/src/utils/session-tracker.ts +67 -0
- package/src/utils/spinner.test.ts +54 -0
- package/src/utils/spinner.ts +87 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InputRenderable,
|
|
3
|
+
type InputRenderableOptions,
|
|
4
|
+
InputRenderableEvents,
|
|
5
|
+
type PasteEvent,
|
|
6
|
+
type KeyEvent,
|
|
7
|
+
RGBA,
|
|
8
|
+
parseColor,
|
|
9
|
+
type ColorInput,
|
|
10
|
+
type RenderContext,
|
|
11
|
+
} from "@opentui/core";
|
|
12
|
+
import { fg } from "@opentui/core";
|
|
13
|
+
|
|
14
|
+
export enum MultiLineInputEvents {
|
|
15
|
+
INPUT = "input",
|
|
16
|
+
CHANGE = "change",
|
|
17
|
+
SUBMIT = "submit",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface MultiLineInputOptions extends Omit<InputRenderableOptions, "value"> {
|
|
21
|
+
/** Initial text value (can include newlines) */
|
|
22
|
+
value?: string;
|
|
23
|
+
/** Minimum height in lines (default: 1) */
|
|
24
|
+
minHeight?: number;
|
|
25
|
+
/** Maximum height in lines (default: 10) */
|
|
26
|
+
maxHeight?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* MultiLineInputRenderable - A multi-line text input component that auto-expands.
|
|
31
|
+
*
|
|
32
|
+
* Features:
|
|
33
|
+
* - Auto-expands height based on content (word wrapping)
|
|
34
|
+
* - Shift+Enter inserts a newline
|
|
35
|
+
* - Enter alone submits the input
|
|
36
|
+
* - Respects minHeight and maxHeight constraints
|
|
37
|
+
* - Emits events: input, change, submit
|
|
38
|
+
*
|
|
39
|
+
* Extends InputRenderable for compatibility with file picker and other utilities.
|
|
40
|
+
*/
|
|
41
|
+
export class MultiLineInputRenderable extends InputRenderable {
|
|
42
|
+
private _minHeight: number;
|
|
43
|
+
private _maxHeight: number;
|
|
44
|
+
private _customPlaceholderColor: RGBA | undefined;
|
|
45
|
+
|
|
46
|
+
constructor(ctx: RenderContext, options: MultiLineInputOptions) {
|
|
47
|
+
// Convert newlines to empty string for initial parent call
|
|
48
|
+
// We'll restore them after initialization
|
|
49
|
+
const sanitizedValue = (options.value ?? "").replace(/[\n\r]/g, "");
|
|
50
|
+
|
|
51
|
+
super(ctx, {
|
|
52
|
+
...options,
|
|
53
|
+
value: sanitizedValue,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this._minHeight = options.minHeight ?? 1;
|
|
57
|
+
this._maxHeight = options.maxHeight ?? 10;
|
|
58
|
+
|
|
59
|
+
// Restore the original value with newlines if provided
|
|
60
|
+
if (options.value && options.value.includes("\n")) {
|
|
61
|
+
this.setText(options.value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Setup content change listener to adjust height
|
|
65
|
+
this.onContentChange = () => {
|
|
66
|
+
this.adjustHeight();
|
|
67
|
+
this.emit(MultiLineInputEvents.INPUT, this.plainText);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Initial height adjustment
|
|
71
|
+
this.adjustHeight();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Apply placeholder with custom color
|
|
76
|
+
*/
|
|
77
|
+
public setPlaceholderWithColor(placeholder: string, color: RGBA): void {
|
|
78
|
+
this._customPlaceholderColor = color;
|
|
79
|
+
if (!placeholder) {
|
|
80
|
+
this.placeholder = "";
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Create a styled text with the placeholder color
|
|
84
|
+
const styledPlaceholder = fg(color)(placeholder);
|
|
85
|
+
// Use parent setter with styled text
|
|
86
|
+
(this as any)._placeholder = styledPlaceholder;
|
|
87
|
+
this.requestRender();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Adjust height based on content line count
|
|
92
|
+
*/
|
|
93
|
+
private adjustHeight(): void {
|
|
94
|
+
const lineCount = this.virtualLineCount;
|
|
95
|
+
const newHeight = Math.max(this._minHeight, Math.min(lineCount, this._maxHeight));
|
|
96
|
+
|
|
97
|
+
// Only update if height actually changed
|
|
98
|
+
if (this.height !== newHeight) {
|
|
99
|
+
this.height = newHeight;
|
|
100
|
+
this.yogaNode.markDirty();
|
|
101
|
+
this.requestRender();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Override handleKeyPress to handle Shift+Enter for newlines
|
|
107
|
+
* and Enter for submit
|
|
108
|
+
*/
|
|
109
|
+
public override handleKeyPress(key: KeyEvent): boolean {
|
|
110
|
+
// Handle Shift+Enter to insert newline
|
|
111
|
+
if ((key.name === "return" || key.name === "linefeed") && key.shift) {
|
|
112
|
+
this.newLine();
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Let parent handle other keys (Enter without shift will submit)
|
|
117
|
+
const handled = super.handleKeyPress(key);
|
|
118
|
+
|
|
119
|
+
// After any text modification, adjust height
|
|
120
|
+
if (handled) {
|
|
121
|
+
this.adjustHeight();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return handled;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Override newLine to allow newlines (parent returns false for single-line)
|
|
129
|
+
*/
|
|
130
|
+
public override newLine(): boolean {
|
|
131
|
+
super.newLine();
|
|
132
|
+
this.adjustHeight();
|
|
133
|
+
this.emit(MultiLineInputEvents.INPUT, this.plainText);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Handle paste - allow newlines in multi-line mode
|
|
139
|
+
*/
|
|
140
|
+
public override handlePaste(event: PasteEvent): void {
|
|
141
|
+
const currentLength = this.plainText.length;
|
|
142
|
+
const remaining = this.maxLength - currentLength;
|
|
143
|
+
|
|
144
|
+
if (remaining <= 0) return;
|
|
145
|
+
|
|
146
|
+
const toInsert = event.text.substring(0, remaining);
|
|
147
|
+
if (toInsert) {
|
|
148
|
+
super.insertText(toInsert);
|
|
149
|
+
this.adjustHeight();
|
|
150
|
+
this.emit(MultiLineInputEvents.INPUT, this.plainText);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Override insertText to allow newlines
|
|
156
|
+
*/
|
|
157
|
+
public override insertText(text: string): void {
|
|
158
|
+
const currentLength = this.plainText.length;
|
|
159
|
+
const remaining = this.maxLength - currentLength;
|
|
160
|
+
|
|
161
|
+
if (remaining <= 0) return;
|
|
162
|
+
|
|
163
|
+
const toInsert = text.substring(0, remaining);
|
|
164
|
+
if (toInsert) {
|
|
165
|
+
super.insertText(toInsert);
|
|
166
|
+
this.adjustHeight();
|
|
167
|
+
this.emit(MultiLineInputEvents.INPUT, this.plainText);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Override deleteCharBackward to adjust height
|
|
173
|
+
*/
|
|
174
|
+
public override deleteCharBackward(): boolean {
|
|
175
|
+
const result = super.deleteCharBackward();
|
|
176
|
+
if (result) {
|
|
177
|
+
this.adjustHeight();
|
|
178
|
+
this.emit(MultiLineInputEvents.INPUT, this.plainText);
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Override deleteChar to adjust height
|
|
185
|
+
*/
|
|
186
|
+
public override deleteChar(): boolean {
|
|
187
|
+
const result = super.deleteChar();
|
|
188
|
+
if (result) {
|
|
189
|
+
this.adjustHeight();
|
|
190
|
+
this.emit(MultiLineInputEvents.INPUT, this.plainText);
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get min height
|
|
197
|
+
*/
|
|
198
|
+
public get minHeight(): number {
|
|
199
|
+
return this._minHeight;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Set min height
|
|
204
|
+
*/
|
|
205
|
+
public set minHeight(value: number) {
|
|
206
|
+
this._minHeight = value;
|
|
207
|
+
this.adjustHeight();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get max height
|
|
212
|
+
*/
|
|
213
|
+
public get maxHeight(): number {
|
|
214
|
+
return this._maxHeight;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Set max height
|
|
219
|
+
*/
|
|
220
|
+
public set maxHeight(value: number) {
|
|
221
|
+
this._maxHeight = value;
|
|
222
|
+
this.adjustHeight();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Override submit to emit both events
|
|
227
|
+
*/
|
|
228
|
+
public override submit(): boolean {
|
|
229
|
+
// Emit submit event with current value
|
|
230
|
+
this.emit(MultiLineInputEvents.SUBMIT, this.plainText);
|
|
231
|
+
return super.submit();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { mock, expect, test, describe, beforeEach, afterEach, spyOn } from "bun:test";
|
|
2
|
+
import "../test-setup.js";
|
|
3
|
+
import { createMockRenderer } from "../mock-factory.js";
|
|
4
|
+
import { SessionData } from "../../types/tasks.js";
|
|
5
|
+
import * as utils from "../../utils/index.js";
|
|
6
|
+
import { SessionChip } from "./SessionChip.ts";
|
|
7
|
+
|
|
8
|
+
describe("SessionChip", () => {
|
|
9
|
+
let mockRenderer: any;
|
|
10
|
+
let mockSession: SessionData;
|
|
11
|
+
let onSelect: (session: SessionData) => void;
|
|
12
|
+
let spies: any[] = [];
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
mockRenderer = createMockRenderer();
|
|
16
|
+
mockSession = {
|
|
17
|
+
id: "test-id",
|
|
18
|
+
startTime: Date.now(),
|
|
19
|
+
prompt: "test prompt",
|
|
20
|
+
status: "Running",
|
|
21
|
+
workingDir: "/test/path",
|
|
22
|
+
gitStatus: { branch: "main", ahead: 0, behind: 0, modified: 0, isClean: true },
|
|
23
|
+
} as SessionData;
|
|
24
|
+
onSelect = mock(() => {});
|
|
25
|
+
|
|
26
|
+
spies = [
|
|
27
|
+
spyOn(utils, "formatDuration").mockReturnValue("10s"),
|
|
28
|
+
spyOn(utils, "isSessionActive").mockReturnValue(true),
|
|
29
|
+
];
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
spies.forEach(s => s.mockRestore());
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("should initialize", () => {
|
|
37
|
+
const chip = new SessionChip(mockRenderer, mockSession, onSelect);
|
|
38
|
+
expect(chip).toBeDefined();
|
|
39
|
+
// Verify chip has an id
|
|
40
|
+
expect(chip.id).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("should have update method", () => {
|
|
44
|
+
const chip = new SessionChip(mockRenderer, mockSession, onSelect);
|
|
45
|
+
expect(typeof chip.update).toBe("function");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("should trigger onSelect when clicked", () => {
|
|
49
|
+
const chip = new SessionChip(mockRenderer, mockSession, onSelect);
|
|
50
|
+
|
|
51
|
+
// Simulate mouse up on the chip
|
|
52
|
+
if (chip.onMouse) {
|
|
53
|
+
chip.onMouse({ type: "up", target: chip } as any);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
expect(onSelect).toHaveBeenCalledWith(mockSession);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("should not trigger onSelect when clicking cancel button", () => {
|
|
60
|
+
const chip = new SessionChip(mockRenderer, mockSession, onSelect);
|
|
61
|
+
|
|
62
|
+
// Simulate mouse up on the cancel button (if it exists)
|
|
63
|
+
if (chip.onMouse && (chip as any).cancelButton) {
|
|
64
|
+
chip.onMouse({ type: "up", target: (chip as any).cancelButton } as any);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
expect(onSelect).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
});
|