pi-extensions 0.1.11 → 0.1.13
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 +1 -1
- package/arcade/CHANGELOG.md +3 -0
- package/arcade/README.md +2 -0
- package/arcade/assets/demo.mp4 +0 -0
- package/arcade/package.json +1 -1
- package/files-widget/CHANGELOG.md +8 -0
- package/files-widget/README.md +1 -1
- package/files-widget/file-tree.ts +41 -2
- package/files-widget/file-viewer.ts +178 -11
- package/files-widget/package.json +5 -1
- package/package.json +1 -1
- package/.ralph/import-cc-codex.md +0 -31
- package/.ralph/import-cc-codex.state.json +0 -14
- package/.ralph/mario-not-impl.md +0 -69
- package/.ralph/mario-not-impl.state.json +0 -14
- package/.ralph/mario-not-spec.md +0 -163
- package/.ralph/mario-not-spec.state.json +0 -14
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Personal extensions for the [Pi coding agent](https://github.com/badlogic/pi-mon
|
|
|
6
6
|
|
|
7
7
|
| Extension | Description |
|
|
8
8
|
|-----------|-------------|
|
|
9
|
-
| [/files](files-widget/) | In-terminal file browser and viewer. Navigate files, view diffs, select code, send comments to agent -
|
|
9
|
+
| [/files](files-widget/) | In-terminal file browser and viewer widget. Navigate files, view diffs, select code, send comments to agent - without leaving Pi, and without interrupting your agent |
|
|
10
10
|
| [tab-status](tab-status/) | Manage as many parallel sessions as your mind can handle. Terminal tab indicators for <br>✅ done / 🚧 stuck / 🛑 timed out |
|
|
11
11
|
| [ralph-wiggum](ralph-wiggum/) | Run arbitrarily-long tasks without diluting model attention. Flat version without subagents like [ralph-loop](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/ralph-loop) |
|
|
12
12
|
| [agent-guidance](agent-guidance/) | Switch between Claude/Codex/Gemini with model-specific guidance (CLAUDE.md, CODEX.md, GEMINI.md) |
|
package/arcade/CHANGELOG.md
CHANGED
package/arcade/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# arcade
|
|
2
2
|
|
|
3
|
+
[**▶️ Watch demo**](assets/demo.mp4)
|
|
4
|
+
|
|
3
5
|
[Snake](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/examples/extensions/snake.ts) is cool, but have you tried:
|
|
4
6
|
|
|
5
7
|
- **sPIce-invaders** (`/spice-invaders`) - type `clawd` for a special challenge that gets harder every level
|
|
Binary file
|
package/arcade/package.json
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this extension will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.1.10] - 2026-01-26
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Treat git-reported directory entries as directories to avoid viewer errors
|
|
9
|
+
- Guard the viewer against opening directories directly
|
|
10
|
+
- Wrap delta diff output without breaking gutters and avoid truncation
|
|
11
|
+
- Add a safe fallback when `bat` fails to render with wrapping
|
|
12
|
+
|
|
5
13
|
## [0.1.9] - 2026-01-26
|
|
6
14
|
|
|
7
15
|
### Changed
|
package/files-widget/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# files-widget
|
|
2
2
|
|
|
3
|
-
In-terminal file browser and viewer for Pi. Navigate files, view diffs, select code, and send comments to the agent without leaving the terminal.
|
|
3
|
+
In-terminal file browser and diff viewer widget for Pi. Navigate files, view diffs, select code, and send comments to the agent without leaving the terminal and without interrupting your agent.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
1
2
|
import { join } from "node:path";
|
|
2
3
|
|
|
3
4
|
import { MAX_TREE_DEPTH } from "./constants";
|
|
@@ -5,6 +6,14 @@ import type { DiffStats, FileNode, FlatNode } from "./types";
|
|
|
5
6
|
|
|
6
7
|
const collator = new Intl.Collator(undefined, { sensitivity: "base" });
|
|
7
8
|
|
|
9
|
+
function isDirectoryPath(path: string): boolean {
|
|
10
|
+
try {
|
|
11
|
+
return statSync(path).isDirectory();
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
function compareNodes(a: FileNode, b: FileNode): number {
|
|
9
18
|
if (a.isDirectory !== b.isDirectory) {
|
|
10
19
|
return a.isDirectory ? -1 : 1;
|
|
@@ -153,11 +162,41 @@ export function buildFileTreeFromPaths(
|
|
|
153
162
|
|
|
154
163
|
const fileRelPath = parts.join("/");
|
|
155
164
|
if (seenFiles.has(fileRelPath)) continue;
|
|
156
|
-
seenFiles.add(fileRelPath);
|
|
157
165
|
|
|
158
166
|
const filePath = join(cwd, fileRelPath);
|
|
159
|
-
const fileGitStatus = gitStatus.get(fileRelPath);
|
|
167
|
+
const fileGitStatus = gitStatus.get(fileRelPath) ?? gitStatus.get(`${fileRelPath}/`);
|
|
160
168
|
const fileDiffStats = diffStats.get(fileRelPath);
|
|
169
|
+
const existingDir = directoryMap.get(fileRelPath);
|
|
170
|
+
|
|
171
|
+
if (existingDir) {
|
|
172
|
+
if (fileGitStatus) {
|
|
173
|
+
existingDir.gitStatus = fileGitStatus;
|
|
174
|
+
}
|
|
175
|
+
if (fileDiffStats) {
|
|
176
|
+
existingDir.diffStats = fileDiffStats;
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const isDirEntry = normalized.endsWith("/") || isDirectoryPath(filePath);
|
|
182
|
+
if (isDirEntry) {
|
|
183
|
+
const depth = parts.length;
|
|
184
|
+
const dirNode: FileNode = {
|
|
185
|
+
name: fileName,
|
|
186
|
+
path: filePath,
|
|
187
|
+
isDirectory: true,
|
|
188
|
+
children: [],
|
|
189
|
+
expanded: depth < 1,
|
|
190
|
+
hasChangedChildren: false,
|
|
191
|
+
gitStatus: fileGitStatus,
|
|
192
|
+
diffStats: fileDiffStats,
|
|
193
|
+
};
|
|
194
|
+
directoryMap.set(fileRelPath, dirNode);
|
|
195
|
+
current.children?.push(dirNode);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
seenFiles.add(fileRelPath);
|
|
161
200
|
|
|
162
201
|
current.children?.push({
|
|
163
202
|
name: fileName,
|
|
@@ -1,9 +1,157 @@
|
|
|
1
|
+
import { visibleWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
|
|
1
2
|
import { execSync } from "node:child_process";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
+
import { readFileSync, statSync } from "node:fs";
|
|
3
4
|
|
|
4
5
|
import { isGitRepo } from "./git";
|
|
5
6
|
import { hasCommand, stripLeadingEmptyLines } from "./utils";
|
|
6
7
|
|
|
8
|
+
const DIFF_CONTENT_PREFIXES = new Set(["+", "-", " "]);
|
|
9
|
+
|
|
10
|
+
function isDiffContentLine(line: string): boolean {
|
|
11
|
+
if (!line) return false;
|
|
12
|
+
if (!DIFF_CONTENT_PREFIXES.has(line[0])) return false;
|
|
13
|
+
if (line.startsWith("+++ ") || line.startsWith("--- ")) return false;
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function wrapLine(line: string, width: number): string[] {
|
|
18
|
+
if (width <= 0 || line.length <= width) {
|
|
19
|
+
return [line];
|
|
20
|
+
}
|
|
21
|
+
const wrapped: string[] = [];
|
|
22
|
+
for (let i = 0; i < line.length; i += width) {
|
|
23
|
+
wrapped.push(line.slice(i, i + width));
|
|
24
|
+
}
|
|
25
|
+
return wrapped;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function wrapDiffLines(lines: string[], width: number): string[] {
|
|
29
|
+
if (width <= 0) return lines;
|
|
30
|
+
const wrapped: string[] = [];
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
if (line.length <= width) {
|
|
33
|
+
wrapped.push(line);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (isDiffContentLine(line)) {
|
|
37
|
+
const prefix = line[0];
|
|
38
|
+
const content = line.slice(1);
|
|
39
|
+
const contentWidth = Math.max(width - 1, 1);
|
|
40
|
+
for (const chunk of wrapLine(content, contentWidth)) {
|
|
41
|
+
wrapped.push(prefix + chunk);
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
wrapped.push(...wrapLine(line, width));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return wrapped;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function extractAnsiCode(str: string, pos: number): { length: number } | null {
|
|
51
|
+
if (pos >= str.length || str[pos] !== "\x1b") return null;
|
|
52
|
+
const next = str[pos + 1];
|
|
53
|
+
if (next === "[") {
|
|
54
|
+
let j = pos + 2;
|
|
55
|
+
while (j < str.length && !/[mGKHJ]/.test(str[j])) j++;
|
|
56
|
+
if (j < str.length) return { length: j + 1 - pos };
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
if (next === "]") {
|
|
60
|
+
let j = pos + 2;
|
|
61
|
+
while (j < str.length) {
|
|
62
|
+
if (str[j] === "\x07") return { length: j + 1 - pos };
|
|
63
|
+
if (str[j] === "\x1b" && str[j + 1] === "\\") return { length: j + 2 - pos };
|
|
64
|
+
j++;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (next === "_") {
|
|
69
|
+
let j = pos + 2;
|
|
70
|
+
while (j < str.length) {
|
|
71
|
+
if (str[j] === "\x07") return { length: j + 1 - pos };
|
|
72
|
+
if (str[j] === "\x1b" && str[j + 1] === "\\") return { length: j + 2 - pos };
|
|
73
|
+
j++;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function stripAnsiCodes(line: string): string {
|
|
81
|
+
let result = "";
|
|
82
|
+
for (let i = 0; i < line.length;) {
|
|
83
|
+
const ansi = extractAnsiCode(line, i);
|
|
84
|
+
if (ansi) {
|
|
85
|
+
i += ansi.length;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
result += line[i];
|
|
89
|
+
i += 1;
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function splitByVisibleWidth(line: string, width: number): { prefix: string; rest: string } {
|
|
95
|
+
if (width <= 0) return { prefix: "", rest: line };
|
|
96
|
+
let visible = 0;
|
|
97
|
+
let i = 0;
|
|
98
|
+
while (i < line.length) {
|
|
99
|
+
const ansi = extractAnsiCode(line, i);
|
|
100
|
+
if (ansi) {
|
|
101
|
+
i += ansi.length;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const charWidth = visibleWidth(line[i]);
|
|
105
|
+
if (visible + charWidth > width) break;
|
|
106
|
+
visible += charWidth;
|
|
107
|
+
i += 1;
|
|
108
|
+
}
|
|
109
|
+
return { prefix: line.slice(0, i), rest: line.slice(i) };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function maskDigits(line: string): string {
|
|
113
|
+
let result = "";
|
|
114
|
+
for (let i = 0; i < line.length;) {
|
|
115
|
+
const ansi = extractAnsiCode(line, i);
|
|
116
|
+
if (ansi) {
|
|
117
|
+
result += line.slice(i, i + ansi.length);
|
|
118
|
+
i += ansi.length;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const char = line[i];
|
|
122
|
+
result += char >= "0" && char <= "9" ? " " : char;
|
|
123
|
+
i += 1;
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function wrapDeltaLine(line: string, width: number): string[] {
|
|
129
|
+
const clean = stripAnsiCodes(line);
|
|
130
|
+
let separatorIndex = clean.indexOf("│");
|
|
131
|
+
if (separatorIndex === -1) separatorIndex = clean.indexOf("|");
|
|
132
|
+
if (separatorIndex === -1) return wrapTextWithAnsi(line, width);
|
|
133
|
+
|
|
134
|
+
let prefixWidth = visibleWidth(clean.slice(0, separatorIndex + 1));
|
|
135
|
+
if (clean[separatorIndex + 1] === " ") prefixWidth += 1;
|
|
136
|
+
if (prefixWidth >= width) return wrapTextWithAnsi(line, width);
|
|
137
|
+
|
|
138
|
+
const { prefix, rest } = splitByVisibleWidth(line, prefixWidth);
|
|
139
|
+
const contentWidth = Math.max(width - visibleWidth(prefix), 1);
|
|
140
|
+
const continuationPrefix = maskDigits(prefix);
|
|
141
|
+
const wrappedContent = wrapTextWithAnsi(rest, contentWidth);
|
|
142
|
+
|
|
143
|
+
return wrappedContent.map((chunk, index) => (index === 0 ? prefix : continuationPrefix) + chunk);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function wrapDeltaLines(lines: string[], width: number): string[] {
|
|
147
|
+
if (width <= 0) return lines;
|
|
148
|
+
const wrapped: string[] = [];
|
|
149
|
+
for (const line of lines) {
|
|
150
|
+
wrapped.push(...wrapDeltaLine(line, width));
|
|
151
|
+
}
|
|
152
|
+
return wrapped;
|
|
153
|
+
}
|
|
154
|
+
|
|
7
155
|
export function loadFileContent(
|
|
8
156
|
filePath: string,
|
|
9
157
|
cwd: string,
|
|
@@ -15,23 +163,31 @@ export function loadFileContent(
|
|
|
15
163
|
const termWidth = width || process.stdout.columns || 80;
|
|
16
164
|
|
|
17
165
|
try {
|
|
166
|
+
try {
|
|
167
|
+
if (statSync(filePath).isDirectory()) {
|
|
168
|
+
return ["Directory selected - expand it in the file tree instead of opening it."];
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
// Ignore stat errors and fall through to normal handling
|
|
172
|
+
}
|
|
173
|
+
|
|
18
174
|
if (diffMode && hasChanges && isGitRepo(cwd)) {
|
|
19
175
|
try {
|
|
20
176
|
// Try different diff strategies
|
|
21
177
|
let diffOutput = "";
|
|
22
178
|
|
|
23
179
|
// First try: unstaged changes
|
|
24
|
-
const unstaged = execSync(`git diff -- "${filePath}"`, { cwd, encoding: "utf-8", timeout: 10000, stdio: "pipe" });
|
|
180
|
+
const unstaged = execSync(`git diff --no-color -- "${filePath}"`, { cwd, encoding: "utf-8", timeout: 10000, stdio: "pipe" });
|
|
25
181
|
if (unstaged.trim()) {
|
|
26
182
|
diffOutput = unstaged;
|
|
27
183
|
} else {
|
|
28
184
|
// Second try: staged changes
|
|
29
|
-
const staged = execSync(`git diff --cached -- "${filePath}"`, { cwd, encoding: "utf-8", timeout: 10000, stdio: "pipe" });
|
|
185
|
+
const staged = execSync(`git diff --no-color --cached -- "${filePath}"`, { cwd, encoding: "utf-8", timeout: 10000, stdio: "pipe" });
|
|
30
186
|
if (staged.trim()) {
|
|
31
187
|
diffOutput = staged;
|
|
32
188
|
} else {
|
|
33
189
|
// Third try: diff against HEAD (for new files that are staged)
|
|
34
|
-
const headDiff = execSync(`git diff HEAD -- "${filePath}"`, { cwd, encoding: "utf-8", timeout: 10000, stdio: "pipe" });
|
|
190
|
+
const headDiff = execSync(`git diff --no-color HEAD -- "${filePath}"`, { cwd, encoding: "utf-8", timeout: 10000, stdio: "pipe" });
|
|
35
191
|
if (headDiff.trim()) {
|
|
36
192
|
diffOutput = headDiff;
|
|
37
193
|
}
|
|
@@ -46,7 +202,7 @@ export function loadFileContent(
|
|
|
46
202
|
// Pipe through delta with line numbers for better readability
|
|
47
203
|
try {
|
|
48
204
|
const deltaOutput = execSync(
|
|
49
|
-
`delta --no-gitconfig --width=${termWidth} --line-numbers`,
|
|
205
|
+
`delta --no-gitconfig --width=${termWidth} --line-numbers --wrap-max-lines=unlimited --max-line-length=0`,
|
|
50
206
|
{
|
|
51
207
|
cwd,
|
|
52
208
|
encoding: "utf-8",
|
|
@@ -55,13 +211,13 @@ export function loadFileContent(
|
|
|
55
211
|
stdio: ["pipe", "pipe", "pipe"],
|
|
56
212
|
}
|
|
57
213
|
);
|
|
58
|
-
return stripLeadingEmptyLines(deltaOutput.split("\n"));
|
|
214
|
+
return wrapDeltaLines(stripLeadingEmptyLines(deltaOutput.split("\n")), termWidth);
|
|
59
215
|
} catch {
|
|
60
216
|
// Fall back to raw diff
|
|
61
217
|
}
|
|
62
218
|
}
|
|
63
219
|
|
|
64
|
-
return diffOutput.split("\n");
|
|
220
|
+
return wrapDiffLines(stripLeadingEmptyLines(diffOutput.split("\n")), termWidth);
|
|
65
221
|
} catch (e: any) {
|
|
66
222
|
return [`Diff error: ${e.message}`];
|
|
67
223
|
}
|
|
@@ -79,10 +235,21 @@ export function loadFileContent(
|
|
|
79
235
|
}
|
|
80
236
|
|
|
81
237
|
if (hasCommand("bat")) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
238
|
+
try {
|
|
239
|
+
return execSync(
|
|
240
|
+
`bat --style=numbers --color=always --paging=never --wrap=auto --terminal-width=${termWidth} "${filePath}"`,
|
|
241
|
+
{ encoding: "utf-8", timeout: 10000 }
|
|
242
|
+
).split("\n");
|
|
243
|
+
} catch {
|
|
244
|
+
try {
|
|
245
|
+
return execSync(
|
|
246
|
+
`bat --style=numbers --color=always --paging=never --terminal-width=${termWidth} "${filePath}"`,
|
|
247
|
+
{ encoding: "utf-8", timeout: 10000 }
|
|
248
|
+
).split("\n");
|
|
249
|
+
} catch {
|
|
250
|
+
// Fall through to raw file read
|
|
251
|
+
}
|
|
252
|
+
}
|
|
86
253
|
}
|
|
87
254
|
|
|
88
255
|
const raw = readFileSync(filePath, "utf-8");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tmustier/pi-files-widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "In-terminal file browser and viewer for Pi.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Thomas Mustier",
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
},
|
|
15
15
|
"bugs": "https://github.com/tmustier/pi-extensions/issues",
|
|
16
16
|
"homepage": "https://github.com/tmustier/pi-extensions/tree/main/files-widget",
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@mariozechner/pi-coding-agent": "^0.50.0",
|
|
19
|
+
"@mariozechner/pi-tui": "^0.50.0"
|
|
20
|
+
},
|
|
17
21
|
"pi": {
|
|
18
22
|
"extensions": [
|
|
19
23
|
"index.ts"
|
package/package.json
CHANGED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# import-cc-codex implementation loop
|
|
2
|
-
|
|
3
|
-
## Goals
|
|
4
|
-
- Implement Phase 1 `pi --import <path>` CLI flow.
|
|
5
|
-
- Build Claude Code + Codex transcript parsing + mapping.
|
|
6
|
-
- Add usage exclusion + context estimate for imported ranges.
|
|
7
|
-
- Preserve metadata, ordering, and labels per spec.
|
|
8
|
-
- Prepare for Phase 2 `/import` selector (no UI changes yet unless required).
|
|
9
|
-
|
|
10
|
-
## Checklist (interleaved)
|
|
11
|
-
1. Wire CLI flags + import entrypoint (imp-001)
|
|
12
|
-
2. Implement parsers + mapping utilities (imp-002)
|
|
13
|
-
3. Add import metadata + labeling (imp-004)
|
|
14
|
-
4. Implement usage exclusion + context estimate (imp-003)
|
|
15
|
-
5. Add tool-output redaction (imp-005)
|
|
16
|
-
6. **Reflection**: summarize what works/doesn’t, adjust plan
|
|
17
|
-
7. **Cleanup**: refactor/simplify, remove redundancy, improve clarity
|
|
18
|
-
8. **Testing**: targeted tests for parsers/mapping
|
|
19
|
-
9. Repeat (1–5) as needed
|
|
20
|
-
10. **Reflection** (periodic)
|
|
21
|
-
11. **Cleanup** (periodic)
|
|
22
|
-
12. **Testing**: broaden scope; move toward E2E once CLI flow is stable
|
|
23
|
-
|
|
24
|
-
## Acceptance
|
|
25
|
-
- `pi --import <path>` creates a valid Pi session JSONL and opens it.
|
|
26
|
-
- Imported sessions are labeled, metadata preserved, and usage excluded.
|
|
27
|
-
- Deterministic context % works before first new assistant response.
|
|
28
|
-
- Tests cover key parsing and mapping behavior.
|
|
29
|
-
|
|
30
|
-
## Progress Log
|
|
31
|
-
- 2026-01-13: Wired `--import`/`--source` CLI flags, added core import module for CC/Codex parsing + mapping, updated README CLI docs, and added startup status banner plumbing. Pending: tests, usage exclusion/context estimate, redaction opt-out.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "import-cc-codex",
|
|
3
|
-
"taskFile": ".ralph/import-cc-codex.md",
|
|
4
|
-
"iteration": 1,
|
|
5
|
-
"maxIterations": 50,
|
|
6
|
-
"itemsPerIteration": 3,
|
|
7
|
-
"reflectEvery": 3,
|
|
8
|
-
"reflectInstructions": "REFLECTION CHECKPOINT\n\nPause and reflect on your progress:\n1. What has been accomplished so far?\n2. What's working well?\n3. What's not working or blocking progress?\n4. Should the approach be adjusted?\n5. What are the next priorities?\n\nUpdate the task file with your reflection, then continue working.",
|
|
9
|
-
"active": false,
|
|
10
|
-
"status": "completed",
|
|
11
|
-
"startedAt": "2026-01-13T13:32:49.696Z",
|
|
12
|
-
"lastReflectionAt": 0,
|
|
13
|
-
"completedAt": "2026-01-13T13:33:21.479Z"
|
|
14
|
-
}
|
package/.ralph/mario-not-impl.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Badlogic Game Implementation
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
Implement `/badlogic-game` per `badlogic-game/spec.md` with deterministic core logic, reliable tests (unit + integration + E2E), and incremental milestones.
|
|
5
|
-
|
|
6
|
-
## Loop Protocol
|
|
7
|
-
- Work on 1 feature per iteration (small, shippable, testable).
|
|
8
|
-
- Each iteration MUST:
|
|
9
|
-
- Add or extend at least 1 regression test (unit or integration).
|
|
10
|
-
- Add or extend at least 1 E2E scripted scenario (headless run with golden frame or state snapshot).
|
|
11
|
-
- Record verification evidence (commands + results) in Reflection.
|
|
12
|
-
- After each story, do a quick refactor pass to keep code concise and modular.
|
|
13
|
-
- Keep diffs small; commit once the feature passes verification.
|
|
14
|
-
- Self-reflect every iteration and adjust plan if needed.
|
|
15
|
-
|
|
16
|
-
## Refactor Principles
|
|
17
|
-
- Keep files under ~400 LOC; split by responsibility (engine, render, entities, input, levels).
|
|
18
|
-
- Prefer pure functions and deterministic state updates; avoid hidden globals.
|
|
19
|
-
- Consolidate constants (tiles, glyphs, physics) in one place.
|
|
20
|
-
- Eliminate duplication by extracting helpers and data tables.
|
|
21
|
-
|
|
22
|
-
## Typing Strategy
|
|
23
|
-
- Use `// @ts-check` + JSDoc types for strict checks without a build step.
|
|
24
|
-
- Add `badlogic-game/tsconfig.json` with `checkJs`, `strict`, `noEmit`.
|
|
25
|
-
- Typecheck command: `npx tsc --noEmit -p badlogic-game/tsconfig.json` (record results).
|
|
26
|
-
|
|
27
|
-
## Verifiability Standard
|
|
28
|
-
- Unit tests: physics tick, jump apex, collision resolution, serialization.
|
|
29
|
-
- Integration tests: deterministic input sequences -> expected positions/collisions/state transitions.
|
|
30
|
-
- E2E tests: scripted input -> render frames or HUD/state snapshots; compare to golden fixtures.
|
|
31
|
-
- Manual checks: milestone smoke test in a live TUI session (M1..M4).
|
|
32
|
-
|
|
33
|
-
## Feature Checklist
|
|
34
|
-
- [x] Story 0: Test harness + engine/core separation
|
|
35
|
-
- Decide runner (node:test vs custom) and structure
|
|
36
|
-
- Deterministic RNG + headless renderer
|
|
37
|
-
- [x] Quality: strict typing setup (tsconfig + @ts-check)
|
|
38
|
-
- [x] Story 1: Core loop (map, movement, gravity, collision, camera clamp, Level 1)
|
|
39
|
-
- [x] Story 2: Enemies + stomp + hazards
|
|
40
|
-
- [x] Story 3: Blocks + coins + scoring + HUD
|
|
41
|
-
- [x] Story 4: Power-ups (mushroom) + big state
|
|
42
|
-
- [x] Story 5: Save/resume + pause/quit
|
|
43
|
-
- [x] Story 6: Polish (particles, text cues, camera dead-zone)
|
|
44
|
-
- [x] Docs + wiring: `/badlogic-game` command registration and README update
|
|
45
|
-
- [ ] Final: full test run + manual E2E checks; update harness logs
|
|
46
|
-
|
|
47
|
-
## Milestone Gates
|
|
48
|
-
- [x] M1: Story 1 + test harness + E2E scenario
|
|
49
|
-
- [x] M2: Story 2 + tests + E2E scenario
|
|
50
|
-
- [x] M3: Story 3/4 + tests + E2E scenario
|
|
51
|
-
- [ ] M4: Story 5/6 + tests + manual run
|
|
52
|
-
|
|
53
|
-
## References
|
|
54
|
-
- Spec: `badlogic-game/spec.md`
|
|
55
|
-
- Existing WIP: `arcade/wip/badlogic-game.ts`
|
|
56
|
-
|
|
57
|
-
## Reflection (Iteration 35)
|
|
58
|
-
1. What has been accomplished so far?
|
|
59
|
-
- Added invuln save regression test and paused cue frame E2E fixture.
|
|
60
|
-
- Verification: `node --test badlogic-game/tests/*.test.js` -> 77 pass; `npx tsc --noEmit -p badlogic-game/tsconfig.json` -> ok.
|
|
61
|
-
2. What's working well?
|
|
62
|
-
- Invuln persistence and paused cue overlay are covered deterministically.
|
|
63
|
-
3. What's not working or blocking progress?
|
|
64
|
-
- Manual M4 run still pending (interactive UI required).
|
|
65
|
-
4. Should the approach be adjusted?
|
|
66
|
-
- No; keep manual run as final gate.
|
|
67
|
-
5. What are the next priorities?
|
|
68
|
-
- M4 manual run.
|
|
69
|
-
- Final: update harness logs.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "badlogic-game-impl",
|
|
3
|
-
"taskFile": ".ralph/badlogic-game-impl.md",
|
|
4
|
-
"iteration": 35,
|
|
5
|
-
"maxIterations": 80,
|
|
6
|
-
"itemsPerIteration": 1,
|
|
7
|
-
"reflectEvery": 1,
|
|
8
|
-
"reflectInstructions": "REFLECTION CHECKPOINT\n\nPause and reflect on your progress:\n1. What has been accomplished so far?\n2. What's working well?\n3. What's not working or blocking progress?\n4. Should the approach be adjusted?\n5. What are the next priorities?\n\nUpdate the task file with your reflection, then continue working.",
|
|
9
|
-
"active": false,
|
|
10
|
-
"status": "completed",
|
|
11
|
-
"startedAt": "2026-01-09T03:55:16.633Z",
|
|
12
|
-
"lastReflectionAt": 35,
|
|
13
|
-
"completedAt": "2026-01-09T09:09:36.947Z"
|
|
14
|
-
}
|
package/.ralph/mario-not-spec.md
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
# Badlogic Game Spec
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
Create a comprehensive Mario-style TUI extension spec in `badlogic-game/spec.md` with list-based outline and detailed, verifiable content. Each sub-subsection must include design intent + implementation guidance so it is directly actionable for engineering.
|
|
5
|
-
|
|
6
|
-
## Requirements
|
|
7
|
-
- Use list items + subitems for the outline and content.
|
|
8
|
-
- Draft section-by-section via ralph loop iterations.
|
|
9
|
-
- Iterate at the level of every sub-subsection (e.g., target size, scroll rules).
|
|
10
|
-
- After completing each sub-section (e.g., Player Experience & Feel), reflect on coherence and adjust if needed.
|
|
11
|
-
- Final coherence reflection at end of doc; adjust as needed.
|
|
12
|
-
- Commit after each major section: Core Gameplay, Presentation, Tech + Delivery.
|
|
13
|
-
|
|
14
|
-
## Planned Sections
|
|
15
|
-
1) Core Gameplay
|
|
16
|
-
- Player Experience & Feel
|
|
17
|
-
- Time-to-fun / quick boot
|
|
18
|
-
- Pace / difficulty ramp
|
|
19
|
-
- Failure/respawn cadence
|
|
20
|
-
- Feel targets vs SMB1
|
|
21
|
-
- Controls & Input
|
|
22
|
-
- Key mapping
|
|
23
|
-
- Input buffering
|
|
24
|
-
- Pausing/quitting/restart
|
|
25
|
-
- Game Loop & States
|
|
26
|
-
- State machine
|
|
27
|
-
- Transitions
|
|
28
|
-
- Level Design
|
|
29
|
-
- Tile palette
|
|
30
|
-
- Layout rules
|
|
31
|
-
- Level 1 (short)
|
|
32
|
-
- Entities & Interactions
|
|
33
|
-
- Player
|
|
34
|
-
- Blocks
|
|
35
|
-
- Items
|
|
36
|
-
- Enemies
|
|
37
|
-
- Hazards
|
|
38
|
-
- Physics & Movement
|
|
39
|
-
- Gravity / accel / friction
|
|
40
|
-
- Jump (variable)
|
|
41
|
-
- Collision resolution
|
|
42
|
-
|
|
43
|
-
2) Presentation
|
|
44
|
-
- Camera & Viewport
|
|
45
|
-
- Target size
|
|
46
|
-
- Scroll rules
|
|
47
|
-
- Bounds/edges
|
|
48
|
-
- Rendering & Palette
|
|
49
|
-
- 256-color plan
|
|
50
|
-
- Glyphs / tiles
|
|
51
|
-
- HUD & Scoring
|
|
52
|
-
- HUD layout
|
|
53
|
-
- Score/lives/time/coins
|
|
54
|
-
- FX & Feedback (optional)
|
|
55
|
-
- Particles
|
|
56
|
-
- Text cues
|
|
57
|
-
|
|
58
|
-
3) Tech + Delivery
|
|
59
|
-
- Save/Resume
|
|
60
|
-
- Autosave cadence
|
|
61
|
-
- Persisted fields
|
|
62
|
-
- Resume behavior
|
|
63
|
-
- Technical Architecture
|
|
64
|
-
- Data model
|
|
65
|
-
- Update order
|
|
66
|
-
- Collision handling
|
|
67
|
-
- Input handling
|
|
68
|
-
- Serialization
|
|
69
|
-
- Feature Stories & Sequencing
|
|
70
|
-
- Test Plan
|
|
71
|
-
- Milestones
|
|
72
|
-
- Open Questions
|
|
73
|
-
|
|
74
|
-
## Checklist
|
|
75
|
-
- [x] Create `badlogic-game/spec.md` with list-based scaffold
|
|
76
|
-
- [x] Draft Core Gameplay subsections (reflect + adjust after each)
|
|
77
|
-
- [x] Player Experience & Feel > Time-to-fun / quick boot
|
|
78
|
-
- [x] Player Experience & Feel > Pace / difficulty ramp
|
|
79
|
-
- [x] Player Experience & Feel > Failure/respawn cadence
|
|
80
|
-
- [x] Player Experience & Feel > Feel targets vs SMB1
|
|
81
|
-
- [x] Controls & Input > Key mapping
|
|
82
|
-
- [x] Controls & Input > Input buffering
|
|
83
|
-
- [x] Controls & Input > Pausing/quitting/restart
|
|
84
|
-
- [x] Game Loop & States > State machine
|
|
85
|
-
- [x] Game Loop & States > Transitions
|
|
86
|
-
- [x] Level Design > Tile palette
|
|
87
|
-
- [x] Level Design > Layout rules
|
|
88
|
-
- [x] Level Design > Level 1 (short)
|
|
89
|
-
- [x] Entities & Interactions > Player
|
|
90
|
-
- [x] Entities & Interactions > Blocks
|
|
91
|
-
- [x] Entities & Interactions > Items
|
|
92
|
-
- [x] Entities & Interactions > Enemies
|
|
93
|
-
- [x] Entities & Interactions > Hazards
|
|
94
|
-
- [x] Physics & Movement > Gravity / accel / friction
|
|
95
|
-
- [x] Physics & Movement > Jump (variable)
|
|
96
|
-
- [x] Physics & Movement > Collision resolution
|
|
97
|
-
- [x] Commit Core Gameplay section
|
|
98
|
-
- [x] Draft Presentation subsections (reflect + adjust after each)
|
|
99
|
-
- [x] Camera & Viewport > Target size
|
|
100
|
-
- [x] Camera & Viewport > Scroll rules
|
|
101
|
-
- [x] Camera & Viewport > Bounds/edges
|
|
102
|
-
- [x] Rendering & Palette > 256-color plan
|
|
103
|
-
- [x] Rendering & Palette > Glyphs / tiles
|
|
104
|
-
- [x] HUD & Scoring > HUD layout
|
|
105
|
-
- [x] HUD & Scoring > Score/lives/time/coins
|
|
106
|
-
- [x] FX & Feedback > Particles
|
|
107
|
-
- [x] FX & Feedback > Text cues
|
|
108
|
-
- [x] Commit Presentation section
|
|
109
|
-
- [x] Draft Tech + Delivery subsections (reflect + adjust after each)
|
|
110
|
-
- [x] Save/Resume > Autosave cadence
|
|
111
|
-
- [x] Save/Resume > Persisted fields
|
|
112
|
-
- [x] Save/Resume > Resume behavior
|
|
113
|
-
- [x] Technical Architecture > Data model
|
|
114
|
-
- [x] Technical Architecture > Update order
|
|
115
|
-
- [x] Technical Architecture > Collision handling
|
|
116
|
-
- [x] Technical Architecture > Input handling
|
|
117
|
-
- [x] Technical Architecture > Serialization
|
|
118
|
-
- [x] Feature Stories & Sequencing
|
|
119
|
-
- [x] Test Plan
|
|
120
|
-
- [x] Milestones
|
|
121
|
-
- [x] Open Questions
|
|
122
|
-
- [x] Commit Tech + Delivery section
|
|
123
|
-
- [x] Final coherence reflection; adjust doc as needed
|
|
124
|
-
|
|
125
|
-
## Reflection (Iteration 46)
|
|
126
|
-
1. What has been accomplished so far?
|
|
127
|
-
- Completed final coherence pass; clarified player glyph sizing.
|
|
128
|
-
2. What's working well?
|
|
129
|
-
- All sections align on sizes, timings, and rendering constraints.
|
|
130
|
-
3. What's not working or blocking progress?
|
|
131
|
-
- Nothing blocked.
|
|
132
|
-
4. Should the approach be adjusted?
|
|
133
|
-
- No; spec is complete.
|
|
134
|
-
5. What are the next priorities?
|
|
135
|
-
- None; ready to stop loop.
|
|
136
|
-
|
|
137
|
-
## Coherence Checks
|
|
138
|
-
- Physics & Movement: collision ordering aligns with stomp and head-bump rules.
|
|
139
|
-
|
|
140
|
-
## Tech + Delivery Coherence Check (Iteration 45)
|
|
141
|
-
- Save cadence aligns with resume behavior and pause/quit rules.
|
|
142
|
-
- Data model fields cover persisted fields list.
|
|
143
|
-
- Serialization versioning aligns with SaveState schema.
|
|
144
|
-
- Stories/milestones reflect the same delivery order.
|
|
145
|
-
|
|
146
|
-
## Final Coherence Check (Iteration 46)
|
|
147
|
-
- Glyph sizing clarified for player big state (1x3 tiles).
|
|
148
|
-
- Run modifier, jump buffer, and physics values are consistent.
|
|
149
|
-
- Save/resume flow matches input and state machine rules.
|
|
150
|
-
|
|
151
|
-
## Presentation Coherence Check (Iteration 32)
|
|
152
|
-
- Viewport size supports HUD lines and 2-char tile grid without wrap.
|
|
153
|
-
- Camera rules align with bounds clamp and no vertical scroll assumption.
|
|
154
|
-
- Glyphs/tiles use ASCII and 2-char width consistent with rendering plan.
|
|
155
|
-
- HUD fields (score/coins/lives/time) match scoring spec values.
|
|
156
|
-
- FX cues are lightweight and don't conflict with HUD overlays.
|
|
157
|
-
|
|
158
|
-
## Core Gameplay Coherence Check (Iteration 22)
|
|
159
|
-
- Consistency: run modifier defined in Controls; referenced in Physics and feel targets.
|
|
160
|
-
- Timing: coyote/jump buffer values consistent between Input and Jump sections.
|
|
161
|
-
- Level 1: pacing, layout rules, and hazards align with speed/jump targets.
|
|
162
|
-
- Death loop: hazards, enemies, and respawn cadence agree on invuln timing and lives.
|
|
163
|
-
- Tile palette: blocks/items/hazards align with symbols; ensure rendering uses same glyphs.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "badlogic-game-spec",
|
|
3
|
-
"taskFile": ".ralph/badlogic-game-spec.md",
|
|
4
|
-
"iteration": 46,
|
|
5
|
-
"maxIterations": 60,
|
|
6
|
-
"itemsPerIteration": 1,
|
|
7
|
-
"reflectEvery": 1,
|
|
8
|
-
"reflectInstructions": "REFLECTION CHECKPOINT\n\nPause and reflect on your progress:\n1. What has been accomplished so far?\n2. What's working well?\n3. What's not working or blocking progress?\n4. Should the approach be adjusted?\n5. What are the next priorities?\n\nUpdate the task file with your reflection, then continue working.",
|
|
9
|
-
"active": false,
|
|
10
|
-
"status": "completed",
|
|
11
|
-
"startedAt": "2026-01-08T22:35:44.127Z",
|
|
12
|
-
"lastReflectionAt": 46,
|
|
13
|
-
"completedAt": "2026-01-08T23:04:52.947Z"
|
|
14
|
-
}
|