create-kudzu 0.1.156 → 0.1.158
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 +11 -5
- package/ai.mjs +45 -11
- package/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,13 +10,13 @@ cd my-app
|
|
|
10
10
|
npm run dev
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The generated project is a working showcase on `@kudzujs/core@^0.16.
|
|
13
|
+
The generated project is a working showcase on `@kudzujs/core@^0.16.36` with reusable components, an interactive state example, a zero-JavaScript static route, page metadata, responsive CSS, and `npm run check`. Configuration remains optional until the app needs custom assets, document defaults, navigation, or build hooks.
|
|
14
14
|
|
|
15
15
|
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.
|
|
19
|
+
`create-kudzu@0.1.158` provides optional local AI developer tools with managed check cancellation:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npm create kudzu@latest my-app -- --ai
|
|
@@ -53,10 +53,16 @@ npm run ai -- check --timeout-ms 600000
|
|
|
53
53
|
log excerpt. Failure stays nonzero. The default timeout is five minutes; choose
|
|
54
54
|
an integer from 1 to 1,200,000 ms. Timeout/cancellation terminates the spawned
|
|
55
55
|
process group on Unix or process tree on Windows and reports failure.
|
|
56
|
+
Under the existing `KUDZU_AI_DELIVERY_GROUP=1` managed-runner contract, Unix
|
|
57
|
+
checks inherit their outer group so a hard group cancellation also reaches
|
|
58
|
+
their ordinary descendants. A check-local timeout or interruption uses native
|
|
59
|
+
`ps` PID/parent-PID inspection to stop only that check's descendant snapshot,
|
|
60
|
+
preserving sibling work; inspection errors are reported as failures.
|
|
56
61
|
- Full stdout/stderr are retained in a unique `.kudzu-ai/check-*/output.log`.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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.
|
|
60
66
|
- The tool does not cache checks or certify browser behavior. Later edits require
|
|
61
67
|
a new check. Scripts that intentionally detach background jobs or custom
|
|
62
68
|
deployment rules need their own lifecycle/asset handling. Use
|
package/ai.mjs
CHANGED
|
@@ -41,20 +41,34 @@ export async function check(root, timeoutMs = 300000) {
|
|
|
41
41
|
const directory = await mkdtemp(join(logs, "check-")), path = join(directory, "output.log")
|
|
42
42
|
const log = await open(path, "wx+")
|
|
43
43
|
const start = performance.now()
|
|
44
|
+
const managed = process.env.KUDZU_AI_DELIVERY_GROUP === "1"
|
|
44
45
|
try {
|
|
45
46
|
const result = await new Promise(resolveRun => {
|
|
46
47
|
const child = spawn(process.platform === "win32" ? "npm.cmd" : "npm", ["run", "check"], {
|
|
47
|
-
cwd: root, detached: process.platform !== "win32", shell: process.platform === "win32",
|
|
48
|
+
cwd: root, detached: process.platform !== "win32" && !managed, shell: process.platform === "win32",
|
|
48
49
|
stdio: ["ignore", log.fd, log.fd], env: { ...process.env, FORCE_COLOR: "0", KUDZU_AI_CHECK_ROOT: root },
|
|
49
50
|
})
|
|
50
51
|
let timedOut = false, interrupted = null, error = null
|
|
51
52
|
const terminate = () => {
|
|
52
|
-
if (!child.pid) return
|
|
53
|
+
if (!child.pid || child.exitCode !== null || child.signalCode !== null) return
|
|
53
54
|
if (process.platform === "win32") {
|
|
54
55
|
const killed = spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], { encoding: "utf8" })
|
|
55
56
|
if (killed.status !== 0) error = killed.error?.message ?? killed.stderr ?? "Could not terminate check tree"
|
|
56
57
|
} else {
|
|
57
|
-
|
|
58
|
+
const pids = [managed ? child.pid : -child.pid]
|
|
59
|
+
if (managed) {
|
|
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 })
|
|
62
|
+
if (table.status !== 0) error = table.error?.message || table.stderr?.trim() || "Could not inspect managed check descendants"
|
|
63
|
+
else {
|
|
64
|
+
const parents = table.stdout.trim().split("\n").map(line => line.trim().split(/\s+/).map(Number))
|
|
65
|
+
if (parents.some(row => row.length !== 2 || row.some(id => !Number.isSafeInteger(id) || id < 0))) error = "Invalid managed check process table"
|
|
66
|
+
else for (let index = 0; index < pids.length; index++) for (const [pid, parent] of parents) {
|
|
67
|
+
if (parent === pids[index] && pid > 1 && pid !== process.pid && !pids.includes(pid)) pids.push(pid)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const pid of pids.reverse()) try { process.kill(pid, "SIGKILL") } catch (failure) { if (failure.code !== "ESRCH") error = failure.message }
|
|
58
72
|
}
|
|
59
73
|
}
|
|
60
74
|
const onInterrupt = signal => { interrupted = signal; terminate() }
|
|
@@ -71,21 +85,41 @@ export async function check(root, timeoutMs = 300000) {
|
|
|
71
85
|
})
|
|
72
86
|
})
|
|
73
87
|
const { size } = await log.stat()
|
|
74
|
-
const
|
|
75
|
-
|
|
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.
|
|
76
92
|
let excerpt
|
|
77
|
-
if (size <=
|
|
93
|
+
if (size <= limit) {
|
|
78
94
|
const { bytesRead } = await log.read(buffer, 0, buffer.length, 0)
|
|
79
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")
|
|
80
99
|
} else {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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")
|
|
84
118
|
}
|
|
85
119
|
return {
|
|
86
|
-
command: "npm run check", passed
|
|
120
|
+
command: "npm run check", passed,
|
|
87
121
|
...result, elapsedMs: Math.round(performance.now() - start),
|
|
88
|
-
log: { path, bytes: size, excerpt, truncated: size >
|
|
122
|
+
log: { path, bytes: size, excerpt, truncated: size > limit },
|
|
89
123
|
browserVerified: false, note: "Fresh check result only; later edits invalidate it. Compiler success is not browser or accessibility proof.",
|
|
90
124
|
}
|
|
91
125
|
} finally {
|
package/index.mjs
CHANGED