create-kudzu 0.1.157 → 0.1.159

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 (3) hide show
  1. package/README.md +5 -4
  2. package/ai.mjs +29 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,7 +16,7 @@ Use `--no-install` to create the files without installing dependencies.
16
16
 
17
17
  ## Optional AI authoring guidance
18
18
 
19
- `create-kudzu@0.1.157` provides optional local AI developer tools with managed check cancellation:
19
+ `create-kudzu@0.1.159` provides optional local AI developer tools with managed check cancellation:
20
20
 
21
21
  ```bash
22
22
  npm create kudzu@latest my-app -- --ai
@@ -59,9 +59,10 @@ npm run ai -- check --timeout-ms 600000
59
59
  `ps` PID/parent-PID inspection to stop only that check's descendant snapshot,
60
60
  preserving sibling work; inspection errors are reported as failures.
61
61
  - Full stdout/stderr are retained in a unique `.kudzu-ai/check-*/output.log`.
62
- Large logs show the first/last 2,048 bytes with an omission marker; an error may
63
- be in the omitted middle. Read the full log when needed. Old logs are not deleted
64
- automatically; remove `.kudzu-ai/` when no longer needed.
62
+ Long successful checks return only the last 512 bytes. Failed checks return
63
+ bounded head/tail excerpts and the first recognized diagnostic in the first
64
+ MiB when present. Read the full log if the excerpt is insufficient. Old logs
65
+ are not deleted automatically; remove `.kudzu-ai/` when no longer needed.
65
66
  - The tool does not cache checks or certify browser behavior. Later edits require
66
67
  a new check. Scripts that intentionally detach background jobs or custom
67
68
  deployment rules need their own lifecycle/asset handling. Use
package/ai.mjs CHANGED
@@ -58,7 +58,7 @@ export async function check(root, timeoutMs = 300000) {
58
58
  const pids = [managed ? child.pid : -child.pid]
59
59
  if (managed) {
60
60
  // Keep the outer group alive on this tool's timeout; stop only its ordinary descendants.
61
- const table = spawnSync("ps", ["-A", "-o", "pid=", "-o", "ppid="], { encoding: "utf8", timeout: 1000, maxBuffer: 1024 * 1024 })
61
+ const table = spawnSync("ps", ["-A", "-o", "pid=", "-o", "ppid="], { encoding: "utf8", timeout: 5000, maxBuffer: 1024 * 1024 })
62
62
  if (table.status !== 0) error = table.error?.message || table.stderr?.trim() || "Could not inspect managed check descendants"
63
63
  else {
64
64
  const parents = table.stdout.trim().split("\n").map(line => line.trim().split(/\s+/).map(Number))
@@ -85,21 +85,41 @@ export async function check(root, timeoutMs = 300000) {
85
85
  })
86
86
  })
87
87
  const { size } = await log.stat()
88
- const buffer = Buffer.alloc(Math.min(size, 4096))
89
- // ponytail: bounded head/tail is an excerpt, never a substitute for the retained full log.
88
+ const passed = result.exitCode === 0 && !result.timedOut && !result.interrupted && !result.error
89
+ const limit = passed ? 512 : 4096
90
+ const buffer = Buffer.alloc(Math.min(size, limit))
91
+ // ponytail: a bounded excerpt never substitutes for the retained full log.
90
92
  let excerpt
91
- if (size <= 4096) {
93
+ if (size <= limit) {
92
94
  const { bytesRead } = await log.read(buffer, 0, buffer.length, 0)
93
95
  excerpt = buffer.subarray(0, bytesRead).toString("utf8")
96
+ } else if (passed) {
97
+ await log.read(buffer, 0, limit, size - limit)
98
+ excerpt = buffer.toString("utf8")
94
99
  } else {
95
- await log.read(buffer, 0, 2048, 0)
96
- await log.read(buffer, 2048, 2048, size - 2048)
97
- excerpt = buffer.subarray(0, 2048).toString("utf8") + "\n[... omitted; read full output.log ...]\n" + buffer.subarray(2048).toString("utf8")
100
+ const middle = Buffer.alloc(8192)
101
+ let diagnostic = ""
102
+ // ponytail: scan at most the first MiB for a useful error; the full log stays on disk.
103
+ for (let offset = 2048; offset < Math.min(size - 2048, 1 << 20); offset += middle.length - 128) {
104
+ const length = Math.min(middle.length, size - 2048 - offset)
105
+ await log.read(middle, 0, length, offset)
106
+ const text = middle.subarray(0, length).toString("utf8")
107
+ const match = /(?:\berror(?: TS\d{4})?|\bfailed)\s*:|\bTS\d{4}\s*:|✖/i.exec(text)
108
+ if (!match) continue
109
+ const start = Math.max(text.lastIndexOf("\n", match.index) + 1, match.index - 120)
110
+ const end = text.indexOf("\n", match.index)
111
+ diagnostic = text.slice(start, end < 0 ? match.index + 240 : Math.min(end, match.index + 240)).trim()
112
+ break
113
+ }
114
+ const edge = diagnostic ? 1536 : 2048
115
+ await log.read(buffer, 0, edge, 0)
116
+ await log.read(buffer, edge, edge, size - edge)
117
+ excerpt = buffer.subarray(0, edge).toString("utf8") + "\n[... omitted; read full output.log ...]\n" + (diagnostic ? diagnostic.slice(0, 400) + "\n[... omitted ...]\n" : "") + buffer.subarray(edge, edge * 2).toString("utf8")
98
118
  }
99
119
  return {
100
- command: "npm run check", passed: result.exitCode === 0 && !result.timedOut && !result.interrupted && !result.error,
120
+ command: "npm run check", passed,
101
121
  ...result, elapsedMs: Math.round(performance.now() - start),
102
- log: { path, bytes: size, excerpt, truncated: size > 4096 },
122
+ log: { path, bytes: size, excerpt, truncated: size > limit },
103
123
  browserVerified: false, note: "Fresh check result only; later edits invalidate it. Compiler success is not browser or accessibility proof.",
104
124
  }
105
125
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kudzu",
3
- "version": "0.1.157",
3
+ "version": "0.1.159",
4
4
  "description": "Create a Kudzu project",
5
5
  "type": "module",
6
6
  "license": "MIT",