@puzzmo/cli 1.0.17 → 1.0.19
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/lib/commands/game/create.js +3 -1
- package/lib/commands/game/create.js.map +1 -1
- package/lib/commands/migrate.d.ts +2 -0
- package/lib/commands/migrate.js +28 -0
- package/lib/commands/migrate.js.map +1 -0
- package/lib/download/page-downloader.js +68 -24
- package/lib/download/page-downloader.js.map +1 -1
- package/lib/index.js +8 -1
- package/lib/index.js.map +1 -1
- package/lib/skills/mcp-client.d.ts +9 -0
- package/lib/skills/mcp-client.js +92 -0
- package/lib/skills/mcp-client.js.map +1 -0
- package/lib/skills/registry.d.ts +0 -5
- package/lib/skills/registry.js +7 -41
- package/lib/skills/registry.js.map +1 -1
- package/lib/skills/runner.js +20 -13
- package/lib/skills/runner.js.map +1 -1
- package/lib/tui/pipeline.js +36 -19
- package/lib/tui/pipeline.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/game/create.ts +4 -1
- package/src/commands/migrate.ts +34 -0
- package/src/download/page-downloader.ts +77 -23
- package/src/index.ts +8 -1
- package/src/skills/mcp-client.ts +90 -0
- package/src/skills/registry.ts +7 -45
- package/src/skills/runner.ts +20 -13
- package/src/tui/pipeline.ts +38 -18
package/src/tui/pipeline.ts
CHANGED
|
@@ -5,6 +5,7 @@ import path from "node:path"
|
|
|
5
5
|
import { execSync } from "node:child_process"
|
|
6
6
|
|
|
7
7
|
import { skillsPipeline } from "../skills/registry.js"
|
|
8
|
+
import { getMcpUrl } from "../skills/mcp-client.js"
|
|
8
9
|
|
|
9
10
|
type StepState = "pending" | "running" | "success" | "failed" | "skipped"
|
|
10
11
|
type Phase = "agent" | "build" | "commit" | "idle"
|
|
@@ -57,24 +58,42 @@ const buildAgentCmd = (agent: string, prompt: string): { cmd: string; args: stri
|
|
|
57
58
|
return { cmd: agent, args: [prompt] }
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
const buildPrompt = (skillName: string): string => {
|
|
61
|
-
return `
|
|
61
|
+
const buildPrompt = (skillName: string, mcpUrl: string | null): string => {
|
|
62
|
+
if (!mcpUrl) return `Run the skill "${skillName}". The game source is in the current directory.`
|
|
63
|
+
|
|
64
|
+
return `First, fetch the skill instructions by using WebFetch to make a POST request to:
|
|
65
|
+
${mcpUrl}
|
|
66
|
+
|
|
67
|
+
With headers:
|
|
68
|
+
- Content-Type: application/json
|
|
69
|
+
- Accept: application/json, text/event-stream
|
|
70
|
+
|
|
71
|
+
And body:
|
|
72
|
+
{"jsonrpc":"2.0","id":1,"method":"prompts/get","params":{"name":"${skillName}"}}
|
|
73
|
+
|
|
74
|
+
The response will be in SSE format. Parse the "data:" line to get the JSON result, which contains the skill instructions in result.messages[0].content.text.
|
|
75
|
+
|
|
76
|
+
Then follow those instructions. The game source is in the current directory.`
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
class PipelineTUI {
|
|
65
80
|
private states: StepState[]
|
|
66
81
|
private currentStep = 0
|
|
67
82
|
private outputLines: string[] = []
|
|
83
|
+
private chatLines: string[] = []
|
|
68
84
|
private phase: Phase = "idle"
|
|
69
85
|
private done = false
|
|
70
86
|
private activeProc: ChildProcess | null = null
|
|
71
87
|
private sidebarWidth = 30
|
|
88
|
+
private logsDir: string
|
|
72
89
|
|
|
73
90
|
constructor(
|
|
74
91
|
private agent: string,
|
|
75
92
|
private gameDir: string,
|
|
76
93
|
) {
|
|
77
94
|
this.states = skillsPipeline.map(() => "pending")
|
|
95
|
+
this.logsDir = path.join(gameDir, ".puzzmo", "logs")
|
|
96
|
+
fs.mkdirSync(this.logsDir, { recursive: true })
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
private get rows(): number {
|
|
@@ -132,7 +151,7 @@ class PipelineTUI {
|
|
|
132
151
|
const isActive = skillIndex === this.currentStep && !this.done
|
|
133
152
|
const icon = stateIcon[state]
|
|
134
153
|
const colorFn = stateColor[state]
|
|
135
|
-
const label = `${icon} ${skill.name}
|
|
154
|
+
const label = `${icon} ${skill.name}`
|
|
136
155
|
const styled = isActive ? bold(colorFn(label)) : colorFn(label)
|
|
137
156
|
const rawLen = this.stripAnsi(styled).length
|
|
138
157
|
buf += "│ " + styled + " ".repeat(Math.max(0, sidebarWidth - rawLen - 3)) + "│"
|
|
@@ -147,8 +166,6 @@ class PipelineTUI {
|
|
|
147
166
|
: dim(`${completedCount}/${skillsPipeline.length} done`)
|
|
148
167
|
const rawLen = this.stripAnsi(status).length
|
|
149
168
|
buf += "│ " + status + " ".repeat(Math.max(0, sidebarWidth - rawLen - 3)) + "│"
|
|
150
|
-
} else if (skillIndex === skillsPipeline.length + 2) {
|
|
151
|
-
buf += "│ " + dim("* = optional") + " ".repeat(Math.max(0, sidebarWidth - 15)) + "│"
|
|
152
169
|
} else {
|
|
153
170
|
buf += "│" + " ".repeat(sidebarWidth - 2) + "│"
|
|
154
171
|
}
|
|
@@ -205,13 +222,13 @@ class PipelineTUI {
|
|
|
205
222
|
for (const line of rawLines) {
|
|
206
223
|
const parsed = this.parseStreamLine(line)
|
|
207
224
|
if (parsed) {
|
|
208
|
-
// Split multi-line parsed output into separate display lines
|
|
209
225
|
for (const displayLine of parsed.split("\n")) {
|
|
210
226
|
this.outputLines.push(displayLine)
|
|
227
|
+
this.chatLines.push(this.stripAnsi(displayLine))
|
|
211
228
|
}
|
|
212
229
|
}
|
|
213
230
|
}
|
|
214
|
-
// Keep buffer bounded
|
|
231
|
+
// Keep display buffer bounded, but chatLines grows unbounded per skill
|
|
215
232
|
if (this.outputLines.length > 500) {
|
|
216
233
|
this.outputLines = this.outputLines.slice(-this.maxOutputLines)
|
|
217
234
|
}
|
|
@@ -389,21 +406,19 @@ class PipelineTUI {
|
|
|
389
406
|
this.states[stepIndex] = "running"
|
|
390
407
|
this.currentStep = stepIndex
|
|
391
408
|
this.outputLines = []
|
|
409
|
+
this.chatLines = []
|
|
392
410
|
this.phase = "agent"
|
|
393
411
|
this.render()
|
|
394
412
|
|
|
395
|
-
const
|
|
413
|
+
const mcpUrl = getMcpUrl(this.gameDir)
|
|
414
|
+
const prompt = buildPrompt(skill.name, mcpUrl)
|
|
396
415
|
let success = await this.runAgent(prompt)
|
|
397
416
|
|
|
398
417
|
if (!success) {
|
|
399
|
-
if (skill.optional) {
|
|
400
|
-
this.states[stepIndex] = "skipped"
|
|
401
|
-
this.render()
|
|
402
|
-
return true
|
|
403
|
-
}
|
|
404
418
|
this.appendOutput("\n--- Retrying ---\n")
|
|
405
419
|
success = await this.runAgent(prompt)
|
|
406
420
|
if (!success) {
|
|
421
|
+
this.writeSkillLog(skill.name)
|
|
407
422
|
this.states[stepIndex] = "failed"
|
|
408
423
|
this.render()
|
|
409
424
|
return false
|
|
@@ -422,11 +437,7 @@ class PipelineTUI {
|
|
|
422
437
|
|
|
423
438
|
const retry = this.runCmd("npx vite build")
|
|
424
439
|
if (!retry.success) {
|
|
425
|
-
|
|
426
|
-
this.states[stepIndex] = "skipped"
|
|
427
|
-
this.render()
|
|
428
|
-
return true
|
|
429
|
-
}
|
|
440
|
+
this.writeSkillLog(skill.name)
|
|
430
441
|
this.states[stepIndex] = "failed"
|
|
431
442
|
this.render()
|
|
432
443
|
return false
|
|
@@ -441,11 +452,20 @@ class PipelineTUI {
|
|
|
441
452
|
if (commitResult.success) this.appendOutput("Committed.")
|
|
442
453
|
else this.appendOutput("No changes to commit.")
|
|
443
454
|
|
|
455
|
+
// Write chat log for this skill
|
|
456
|
+
this.writeSkillLog(skill.name)
|
|
457
|
+
|
|
444
458
|
this.states[stepIndex] = "success"
|
|
445
459
|
this.render()
|
|
446
460
|
return true
|
|
447
461
|
}
|
|
448
462
|
|
|
463
|
+
/** Writes the collected chat lines for a skill to a log file */
|
|
464
|
+
private writeSkillLog(skillName: string) {
|
|
465
|
+
const logPath = path.join(this.logsDir, `${skillName}.txt`)
|
|
466
|
+
fs.writeFileSync(logPath, this.chatLines.join("\n") + "\n")
|
|
467
|
+
}
|
|
468
|
+
|
|
449
469
|
/** Set up keyboard handling (Ctrl+C to quit) */
|
|
450
470
|
private setupInput() {
|
|
451
471
|
if (process.stdin.isTTY) {
|