@nanopm/cli 0.1.20 → 0.1.22
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/dist/index.js +29 -4
- package/package.json +1 -1
- package/skills/build/SKILL.md +6 -1
package/dist/index.js
CHANGED
|
@@ -5426,6 +5426,9 @@ function answerForAgent(answer) {
|
|
|
5426
5426
|
return isDecideForMe(answer) ? DECIDE_FOR_ME : answer;
|
|
5427
5427
|
}
|
|
5428
5428
|
var BUILD_HISTORY_KINDS = ["worktree", "plan", "step", "question", "answer", "paused", "pr", "handed", "throw", "thrown", "closed"];
|
|
5429
|
+
function checkNotesOf(json2) {
|
|
5430
|
+
return (Array.isArray(json2) ? json2 : []).flatMap((n) => typeof n?.ask === "string" && typeof n?.answer === "string" ? [{ ask: n.ask, answer: n.answer }] : []);
|
|
5431
|
+
}
|
|
5429
5432
|
function originOf(job) {
|
|
5430
5433
|
const named = (job.params ?? {}).origin;
|
|
5431
5434
|
return typeof named === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(named) ? named : job.id;
|
|
@@ -5476,6 +5479,9 @@ function buildHistory(events, at) {
|
|
|
5476
5479
|
const answered2 = last?.kind === "question" && answers.has(last.seq) ? answers.get(last.seq) : null;
|
|
5477
5480
|
if (answered2 !== null && last.payload.about === "usage") return found({ why: "limit" });
|
|
5478
5481
|
if (answered2 !== null && (last.payload.about === "conflict" || last.payload.about === "refused")) return found({ why: last.payload.about, answer: answered2 });
|
|
5482
|
+
if (answered2 !== null && last.payload.about === "check" && typeof last.payload.question === "string" && !isDecideForMe(answered2)) {
|
|
5483
|
+
return found({ why: "answer", answer: answered2, keep: last.payload.question });
|
|
5484
|
+
}
|
|
5479
5485
|
if (answered2 !== null) return found({ why: "answer", answer: answered2 });
|
|
5480
5486
|
if (last?.kind === "paused") return found({ why: "limit" });
|
|
5481
5487
|
return found({ why: "lost" });
|
|
@@ -11288,6 +11294,7 @@ async function prepareBuild(client, job, clone, adapter) {
|
|
|
11288
11294
|
made = true;
|
|
11289
11295
|
await appendJobEvent(client, job.id, "worktree", { dir, branch, base, fork }, "nanopm");
|
|
11290
11296
|
await handOverTheSpec(worktree, specForAgent(solution.agent_spec, solution.spec));
|
|
11297
|
+
await handOverNotes(client, job.project_id, worktree);
|
|
11291
11298
|
await startSolution(client, job.project_id, solution.ref, job.id);
|
|
11292
11299
|
return { ok: true, build: { solution, clone, dir, worktree, branch, base, fork, network: await network(dir), history } };
|
|
11293
11300
|
} catch (err) {
|
|
@@ -11311,7 +11318,8 @@ async function runBuildJob(input) {
|
|
|
11311
11318
|
};
|
|
11312
11319
|
const park = async (why) => {
|
|
11313
11320
|
if ("ask" in why) {
|
|
11314
|
-
|
|
11321
|
+
const atClose = why.ask.about === "conflict" || why.ask.about === "refused";
|
|
11322
|
+
await events.write("question", { ...why.ask, ...atClose ? { report: said } : why.ask.about || plan ? {} : { about: "plan" } });
|
|
11315
11323
|
await log("build.ask", `${solution.ref} waits on the founder`);
|
|
11316
11324
|
} else if (why.resetsAt) {
|
|
11317
11325
|
await events.write("paused", { why: "limit", resets_at: new Date(why.resetsAt * 1e3).toISOString() });
|
|
@@ -11401,6 +11409,10 @@ The project's own check is \`${verifyCommand2}\`. Run it before your last build_
|
|
|
11401
11409
|
} else {
|
|
11402
11410
|
await events.write("text", { text: AGAIN[resume2.why] });
|
|
11403
11411
|
if (resume2.why === "answer" && spec2.answerFile) await writeFile5(spec2.answerFile, answerForAgent(resume2.answer), { mode: 384 });
|
|
11412
|
+
if (resume2.why === "answer" && resume2.keep) {
|
|
11413
|
+
const { error } = await client.rpc("add_check_note", { p_project_id: job.project_id, p_ask: resume2.keep, p_answer: resume2.answer });
|
|
11414
|
+
if (error) await log("build.note", `the answer was not kept: ${error.message}`, "error");
|
|
11415
|
+
}
|
|
11404
11416
|
}
|
|
11405
11417
|
if (resume2?.why === "refused") return openPr();
|
|
11406
11418
|
let turn = null;
|
|
@@ -11462,9 +11474,10 @@ var AGAIN = {
|
|
|
11462
11474
|
refused: "Asking GitHub for the pull request again."
|
|
11463
11475
|
};
|
|
11464
11476
|
function askOf(input) {
|
|
11465
|
-
const { question, options, why } = input ?? {};
|
|
11477
|
+
const { question, options, why, about } = input ?? {};
|
|
11466
11478
|
return {
|
|
11467
11479
|
question: String(question ?? ""),
|
|
11480
|
+
...about === "check" ? { about: "check" } : {},
|
|
11468
11481
|
...Array.isArray(options) ? { options: options.map(String) } : {},
|
|
11469
11482
|
...typeof why === "string" ? { why } : {}
|
|
11470
11483
|
};
|
|
@@ -11497,6 +11510,16 @@ async function handOverTheSpec(worktree, agentSpec) {
|
|
|
11497
11510
|
`);
|
|
11498
11511
|
}
|
|
11499
11512
|
}
|
|
11513
|
+
async function handOverNotes(client, projectId, worktree) {
|
|
11514
|
+
const { data } = await client.from("project_preferences").select("check_notes").eq("project_id", projectId).maybeSingle();
|
|
11515
|
+
const lines = checkNotesOf(data?.check_notes).map((n) => `- ${n.ask.replace(/\s*\n\s*/g, " ")}
|
|
11516
|
+
${n.answer.replace(/\n/g, "\n ")}`);
|
|
11517
|
+
if (!lines.length) return;
|
|
11518
|
+
await writeFile5(join13(worktree.dir, ".nanopm", "CHECK.md"), `# What earlier builds asked the founder, to check the app
|
|
11519
|
+
|
|
11520
|
+
${lines.join("\n")}
|
|
11521
|
+
`);
|
|
11522
|
+
}
|
|
11500
11523
|
function stepsOf(input) {
|
|
11501
11524
|
const steps = input?.steps;
|
|
11502
11525
|
return Array.isArray(steps) && steps.length && steps.every((s) => typeof s === "string") ? steps : null;
|
|
@@ -12640,7 +12663,9 @@ ${renderMap()}`,
|
|
|
12640
12663
|
// important question of the run must not be refused for being long.
|
|
12641
12664
|
question: z21.string().min(1).max(8e3),
|
|
12642
12665
|
options: z21.array(z21.string().min(1)).max(8).optional(),
|
|
12643
|
-
why: z21.string().max(400).optional()
|
|
12666
|
+
why: z21.string().max(400).optional(),
|
|
12667
|
+
// A build asking what it needs to check the app that the code cannot tell (spec 77).
|
|
12668
|
+
about: z21.enum(["check"]).optional().describe("In a build, `check` when you ask what you need to check the app and cannot find in the code (a test user, where an email lands): the answer is kept for the next builds.")
|
|
12644
12669
|
}
|
|
12645
12670
|
},
|
|
12646
12671
|
async ({ question, options, why }, extra) => {
|
|
@@ -14727,7 +14752,7 @@ import { promisify as promisify9 } from "util";
|
|
|
14727
14752
|
// package.json
|
|
14728
14753
|
var package_default = {
|
|
14729
14754
|
name: "@nanopm/cli",
|
|
14730
|
-
version: "0.1.
|
|
14755
|
+
version: "0.1.22",
|
|
14731
14756
|
description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
|
|
14732
14757
|
type: "module",
|
|
14733
14758
|
license: "MIT",
|
package/package.json
CHANGED
package/skills/build/SKILL.md
CHANGED
|
@@ -36,6 +36,9 @@ NanoPM commits each step for you and opens the pull request when you are done.
|
|
|
36
36
|
5. **Check each acceptance criterion**, inside your last step: go through each one of
|
|
37
37
|
`.nanopm/SPEC.md` and check it as close to what the user sees as you can here — run the code
|
|
38
38
|
path it names (call the handler, run the script, render the component), and note what you saw.
|
|
39
|
+
`.nanopm/CHECK.md`, when there is one, holds what earlier builds asked the founder to check the
|
|
40
|
+
app; read it first. What you need and cannot find in the code (a test user, where an email
|
|
41
|
+
lands), ask with `ask_user` and `about: "check"`: the answer is kept for every build after.
|
|
39
42
|
Your sandbox cannot open a local port, so a running server, a browser or a device is out of
|
|
40
43
|
reach: say so for what needs one. Never touch a database, a service or a file outside your copy
|
|
41
44
|
to check something. Keep package caches in `$TMPDIR` (`npm install --cache "$TMPDIR/npm"`, a
|
|
@@ -50,7 +53,9 @@ NanoPM commits each step for you and opens the pull request when you are done.
|
|
|
50
53
|
- **How I checked it**: first one line per acceptance criterion, exactly `- AC1 — saw: what you
|
|
51
54
|
saw` or `- AC1 — could not: what, because why`. *Saw* is for what you saw happen when you ran
|
|
52
55
|
the code the criterion is about — say how (a handler called with what, a script's output);
|
|
53
|
-
what you could not reach is *could not*, with why
|
|
56
|
+
what you could not reach is *could not*, with why, and ends with `Try it:` and how a person
|
|
57
|
+
checks it in one or two steps, in the founder's words (« sign up with a new account; after the
|
|
58
|
+
notifications step, the warm-up appears »). Never say you saw what you did not run. Then the commands you ran, and their result.
|
|
54
59
|
7. `log` one line: what you built.
|
|
55
60
|
|
|
56
61
|
## When to ask the founder
|