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.
Files changed (128) hide show
  1. package/README.md +242 -0
  2. package/bin.js +3 -0
  3. package/dist/pickle +0 -0
  4. package/dist/worker-executor.js +207 -0
  5. package/package.json +53 -0
  6. package/src/games/GameSidebarManager.test.ts +64 -0
  7. package/src/games/GameSidebarManager.ts +78 -0
  8. package/src/games/gameboy/GameboyView.test.ts +25 -0
  9. package/src/games/gameboy/GameboyView.ts +100 -0
  10. package/src/games/gameboy/gameboy-polyfills.ts +313 -0
  11. package/src/games/index.test.ts +9 -0
  12. package/src/games/index.ts +4 -0
  13. package/src/games/snake/SnakeGame.test.ts +35 -0
  14. package/src/games/snake/SnakeGame.ts +145 -0
  15. package/src/games/snake/SnakeView.test.ts +25 -0
  16. package/src/games/snake/SnakeView.ts +290 -0
  17. package/src/index.test.ts +24 -0
  18. package/src/index.ts +141 -0
  19. package/src/services/commands/worker.test.ts +14 -0
  20. package/src/services/commands/worker.ts +262 -0
  21. package/src/services/config/index.ts +2 -0
  22. package/src/services/config/settings.test.ts +42 -0
  23. package/src/services/config/settings.ts +220 -0
  24. package/src/services/config/state.test.ts +88 -0
  25. package/src/services/config/state.ts +130 -0
  26. package/src/services/config/types.ts +39 -0
  27. package/src/services/execution/index.ts +1 -0
  28. package/src/services/execution/pickle-source.test.ts +88 -0
  29. package/src/services/execution/pickle-source.ts +264 -0
  30. package/src/services/execution/prompt.test.ts +93 -0
  31. package/src/services/execution/prompt.ts +322 -0
  32. package/src/services/execution/sequential.test.ts +91 -0
  33. package/src/services/execution/sequential.ts +422 -0
  34. package/src/services/execution/worker-client.ts +94 -0
  35. package/src/services/execution/worker-executor.ts +41 -0
  36. package/src/services/execution/worker.test.ts +73 -0
  37. package/src/services/git/branch.test.ts +147 -0
  38. package/src/services/git/branch.ts +128 -0
  39. package/src/services/git/diff.test.ts +113 -0
  40. package/src/services/git/diff.ts +323 -0
  41. package/src/services/git/index.ts +4 -0
  42. package/src/services/git/pr.test.ts +104 -0
  43. package/src/services/git/pr.ts +192 -0
  44. package/src/services/git/worktree.test.ts +99 -0
  45. package/src/services/git/worktree.ts +141 -0
  46. package/src/services/providers/base.test.ts +86 -0
  47. package/src/services/providers/base.ts +438 -0
  48. package/src/services/providers/codex.test.ts +39 -0
  49. package/src/services/providers/codex.ts +208 -0
  50. package/src/services/providers/gemini.test.ts +40 -0
  51. package/src/services/providers/gemini.ts +169 -0
  52. package/src/services/providers/index.test.ts +28 -0
  53. package/src/services/providers/index.ts +41 -0
  54. package/src/services/providers/opencode.test.ts +64 -0
  55. package/src/services/providers/opencode.ts +228 -0
  56. package/src/services/providers/types.ts +44 -0
  57. package/src/skills/code-implementer.md +105 -0
  58. package/src/skills/code-researcher.md +78 -0
  59. package/src/skills/implementation-planner.md +105 -0
  60. package/src/skills/plan-reviewer.md +100 -0
  61. package/src/skills/prd-drafter.md +123 -0
  62. package/src/skills/research-reviewer.md +79 -0
  63. package/src/skills/ruthless-refactorer.md +52 -0
  64. package/src/skills/ticket-manager.md +135 -0
  65. package/src/types/index.ts +2 -0
  66. package/src/types/rpc.ts +14 -0
  67. package/src/types/tasks.ts +50 -0
  68. package/src/types.d.ts +9 -0
  69. package/src/ui/common.ts +28 -0
  70. package/src/ui/components/FilePickerView.test.ts +79 -0
  71. package/src/ui/components/FilePickerView.ts +161 -0
  72. package/src/ui/components/MultiLineInput.test.ts +27 -0
  73. package/src/ui/components/MultiLineInput.ts +233 -0
  74. package/src/ui/components/SessionChip.test.ts +69 -0
  75. package/src/ui/components/SessionChip.ts +481 -0
  76. package/src/ui/components/ToyboxSidebar.test.ts +36 -0
  77. package/src/ui/components/ToyboxSidebar.ts +329 -0
  78. package/src/ui/components/refactor_plan.md +35 -0
  79. package/src/ui/controllers/DashboardController.integration.test.ts +43 -0
  80. package/src/ui/controllers/DashboardController.ts +650 -0
  81. package/src/ui/dashboard.test.ts +43 -0
  82. package/src/ui/dashboard.ts +309 -0
  83. package/src/ui/dialogs/DashboardDialog.test.ts +146 -0
  84. package/src/ui/dialogs/DashboardDialog.ts +399 -0
  85. package/src/ui/dialogs/Dialog.test.ts +50 -0
  86. package/src/ui/dialogs/Dialog.ts +241 -0
  87. package/src/ui/dialogs/DialogSidebar.test.ts +60 -0
  88. package/src/ui/dialogs/DialogSidebar.ts +71 -0
  89. package/src/ui/dialogs/DiffViewDialog.test.ts +57 -0
  90. package/src/ui/dialogs/DiffViewDialog.ts +510 -0
  91. package/src/ui/dialogs/PRPreviewDialog.test.ts +50 -0
  92. package/src/ui/dialogs/PRPreviewDialog.ts +346 -0
  93. package/src/ui/dialogs/test-utils.ts +232 -0
  94. package/src/ui/file-picker-utils.test.ts +71 -0
  95. package/src/ui/file-picker-utils.ts +200 -0
  96. package/src/ui/input-chrome.test.ts +62 -0
  97. package/src/ui/input-chrome.ts +172 -0
  98. package/src/ui/logger.test.ts +68 -0
  99. package/src/ui/logger.ts +45 -0
  100. package/src/ui/mock-factory.ts +6 -0
  101. package/src/ui/spinner.test.ts +65 -0
  102. package/src/ui/spinner.ts +41 -0
  103. package/src/ui/test-setup.ts +300 -0
  104. package/src/ui/theme.test.ts +23 -0
  105. package/src/ui/theme.ts +16 -0
  106. package/src/ui/views/LandingView.integration.test.ts +21 -0
  107. package/src/ui/views/LandingView.test.ts +24 -0
  108. package/src/ui/views/LandingView.ts +221 -0
  109. package/src/ui/views/LogView.test.ts +24 -0
  110. package/src/ui/views/LogView.ts +277 -0
  111. package/src/ui/views/ToyboxView.test.ts +46 -0
  112. package/src/ui/views/ToyboxView.ts +323 -0
  113. package/src/utils/clipboard.test.ts +86 -0
  114. package/src/utils/clipboard.ts +100 -0
  115. package/src/utils/index.test.ts +68 -0
  116. package/src/utils/index.ts +95 -0
  117. package/src/utils/persona.test.ts +12 -0
  118. package/src/utils/persona.ts +8 -0
  119. package/src/utils/project-root.test.ts +38 -0
  120. package/src/utils/project-root.ts +22 -0
  121. package/src/utils/resources.test.ts +64 -0
  122. package/src/utils/resources.ts +92 -0
  123. package/src/utils/search.test.ts +48 -0
  124. package/src/utils/search.ts +103 -0
  125. package/src/utils/session-tracker.test.ts +46 -0
  126. package/src/utils/session-tracker.ts +67 -0
  127. package/src/utils/spinner.test.ts +54 -0
  128. 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
+ });