@nanopm/cli 0.1.2 → 0.1.5
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 +25 -49
- package/package.json +1 -1
- package/skills/onboard/SKILL.md +5 -3
package/dist/index.js
CHANGED
|
@@ -863,22 +863,14 @@ var CHAINED = {
|
|
|
863
863
|
// (`mapping_completion_hook`): the opportunity map follows the market read below, and
|
|
864
864
|
// the founder confirms what the scan proposed on the card while it is drawn.
|
|
865
865
|
//
|
|
866
|
-
//
|
|
867
|
-
//
|
|
868
|
-
//
|
|
869
|
-
//
|
|
870
|
-
//
|
|
871
|
-
//
|
|
872
|
-
// the
|
|
873
|
-
|
|
874
|
-
//
|
|
875
|
-
// It does not close the gap — nothing chained can, while the read itself takes 161s — which
|
|
876
|
-
// is why the mission is asked for on the way in and the vision is drawn as *coming*
|
|
877
|
-
// (docs/nanopm-56-the-required-set.md). This makes that wait honest rather than shorter
|
|
878
|
-
// than it looks.
|
|
879
|
-
// Since 2026-09-30 the direction is queued only when the onboarding left the mission or the
|
|
880
|
-
// vision empty (`WRITTEN_BY_PARENT`): with both written, it had nothing to fill.
|
|
881
|
-
onboard: ["market", "direction"],
|
|
866
|
+
// **The direction crew does not follow an onboarding** (Nicolas, 2026-10-01, docs/ACTIVATION.md
|
|
867
|
+
// row 27). It was queued here, in the background, whenever the read left the mission or the
|
|
868
|
+
// vision empty, and it landed 9 to 24 minutes later — after the market, the map and the
|
|
869
|
+
// solutions — while the card's Direction step, right after the pitch, promised *a few
|
|
870
|
+
// minutes*. Direction now works like the card's other sections: the read writes what the
|
|
871
|
+
// repository states, the card asks the founder for a mission it did not find (`missionAsk`),
|
|
872
|
+
// and the crew runs when the founder asks for it from the business card.
|
|
873
|
+
onboard: ["market"],
|
|
882
874
|
// A problem list is what solutions hang under, so a fresh list is followed by fresh
|
|
883
875
|
// solutions. **The problem crew itself is retired** (docs/nanopm-59-opportunity-map.md,
|
|
884
876
|
// Guillaume, 2026-09-25): the opportunity map replaced it, and nothing queues it any more —
|
|
@@ -891,10 +883,8 @@ var CHAINED = {
|
|
|
891
883
|
// proposes beside a confirmed pitch and never over it (docs/nanopm-52-pitch-crew.md).
|
|
892
884
|
//
|
|
893
885
|
// The direction crew **was** chained here too, for the same reason — a vision rests on who
|
|
894
|
-
// else exists —
|
|
895
|
-
//
|
|
896
|
-
// the onboarding starts only once the market, queued a hair before it, has finished. All
|
|
897
|
-
// it gave up was waiting behind the pitch, which it never read. See the note on `onboard`.
|
|
886
|
+
// else exists — then after `onboard`, and since 2026-10-01 after nothing: see the note on
|
|
887
|
+
// `onboard`.
|
|
898
888
|
//
|
|
899
889
|
// Neither follows a personas run the founder started: they pressed one button, for one
|
|
900
890
|
// crew, with a note meant for it (Guillaume, 2026-09-22). A founder who wants both runs asks
|
|
@@ -948,16 +938,6 @@ var ONLY_AFTER = {
|
|
|
948
938
|
var NEVER_AFTER = {
|
|
949
939
|
market: { pitch: ["onboard"] }
|
|
950
940
|
};
|
|
951
|
-
var WRITTEN_BY_PARENT = {
|
|
952
|
-
onboard: { direction: ["mission", "vision"] }
|
|
953
|
-
};
|
|
954
|
-
function stillOwed(skill, next2, written) {
|
|
955
|
-
const needs2 = WRITTEN_BY_PARENT[skill] ?? {};
|
|
956
|
-
return next2.filter((n) => !needs2[n] || needs2[n].some((topic) => !written.has(topic)));
|
|
957
|
-
}
|
|
958
|
-
var BACKGROUND_EDGES = {
|
|
959
|
-
onboard: ["direction"]
|
|
960
|
-
};
|
|
961
941
|
var AFTER_DEEP = {
|
|
962
942
|
needs: ["opportunities"]
|
|
963
943
|
};
|
|
@@ -10956,12 +10936,6 @@ async function runDaemon(options = {}) {
|
|
|
10956
10936
|
}
|
|
10957
10937
|
return { daemonId, processed };
|
|
10958
10938
|
}
|
|
10959
|
-
async function topicsWritten(auth, projectId, skill) {
|
|
10960
|
-
const topics = [...new Set(Object.values(WRITTEN_BY_PARENT[skill] ?? {}).flat())];
|
|
10961
|
-
if (!topics.length) return /* @__PURE__ */ new Set();
|
|
10962
|
-
const { data } = await auth.client.from("memory").select("topic").eq("project_id", projectId).in("topic", topics).is("superseded_by", null);
|
|
10963
|
-
return new Set((data ?? []).map((r) => r.topic));
|
|
10964
|
-
}
|
|
10965
10939
|
async function chainAfter(auth, job, outcome, log) {
|
|
10966
10940
|
if (NOT_CHAINED_BY_DAEMON.includes(job.skill)) return;
|
|
10967
10941
|
if (job.params?.chain === false) return;
|
|
@@ -10969,14 +10943,13 @@ async function chainAfter(auth, job, outcome, log) {
|
|
|
10969
10943
|
const parent = job.parent_job_id ? await parentOf(auth, job.parent_job_id) : null;
|
|
10970
10944
|
const own = job.params ?? {};
|
|
10971
10945
|
const deep = own.depth === "deep" && job.trigger === "hook";
|
|
10972
|
-
const next2 =
|
|
10946
|
+
const next2 = daemonQueues(job.skill, 0, parent?.skill ?? null, deep);
|
|
10973
10947
|
if (!next2.length) return;
|
|
10974
10948
|
if (await chainDepth(auth, job) >= CHAIN_DEPTH_MAX) return;
|
|
10975
10949
|
const from = deep ? own.repo_path : parent?.params?.repo_path;
|
|
10976
10950
|
const repo = typeof from === "string" ? { repo_path: from } : null;
|
|
10977
10951
|
for (const skill of next2) {
|
|
10978
|
-
const
|
|
10979
|
-
const params = { ...repo ?? {}, ...deep ? { depth: "deep" } : {}, ...background ? { background: true } : {} };
|
|
10952
|
+
const params = { ...repo ?? {}, ...deep ? { depth: "deep", background: true } : {} };
|
|
10980
10953
|
const { data, error } = await auth.client.from("jobs").insert({
|
|
10981
10954
|
project_id: job.project_id,
|
|
10982
10955
|
skill,
|
|
@@ -11335,9 +11308,12 @@ function validateItem(item) {
|
|
|
11335
11308
|
}
|
|
11336
11309
|
return null;
|
|
11337
11310
|
}
|
|
11338
|
-
function overFounder(item, stated) {
|
|
11339
|
-
if (item.topic !== GOALS_TOPIC || item.provenance === "stated"
|
|
11340
|
-
return `the founder set "${item.key}" themselves; drop your proposal, never write it again`;
|
|
11311
|
+
function overFounder(item, stated, definitionLever = null) {
|
|
11312
|
+
if (item.topic !== GOALS_TOPIC || item.provenance === "stated") return null;
|
|
11313
|
+
if (stated.has(item.key)) return `the founder set "${item.key}" themselves; drop your proposal, never write it again`;
|
|
11314
|
+
const lever = item.key === OBJECTIVE_KEY ? LEVER_LINE.exec(item.content)?.[1] : void 0;
|
|
11315
|
+
if (definitionLever && lever && lever !== definitionLever) return `the founder chose ${definitionLever} when they defined their objective: write "${item.key}" for ${definitionLever}, or nothing`;
|
|
11316
|
+
return null;
|
|
11341
11317
|
}
|
|
11342
11318
|
function normaliseItem(item) {
|
|
11343
11319
|
return normalisePersona(item);
|
|
@@ -11799,9 +11775,9 @@ ${renderMap()}`,
|
|
|
11799
11775
|
valid.push({ ...item, content: fix.content });
|
|
11800
11776
|
} else valid.push(item);
|
|
11801
11777
|
}
|
|
11802
|
-
const stated = await statedGoals(ctx, valid);
|
|
11778
|
+
const { stated, definitionLever } = await statedGoals(ctx, valid);
|
|
11803
11779
|
const writable = valid.filter((item) => {
|
|
11804
|
-
const why = overFounder(item, stated);
|
|
11780
|
+
const why = overFounder(item, stated, definitionLever);
|
|
11805
11781
|
if (why) refused.push({ key: item.key, why });
|
|
11806
11782
|
return !why;
|
|
11807
11783
|
});
|
|
@@ -12367,11 +12343,11 @@ async function listContext(ctx, domain, topic) {
|
|
|
12367
12343
|
return { items };
|
|
12368
12344
|
}
|
|
12369
12345
|
async function statedGoals(ctx, items) {
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
const { data, error } = await ctx.client.from("memory").select("key").eq("project_id", ctx.projectId).eq("topic", GOALS_TOPIC).in("key", keys).eq("provenance", "stated").is("superseded_by", null);
|
|
12346
|
+
if (!items.some((i) => i.topic === GOALS_TOPIC && i.provenance !== "stated")) return { stated: /* @__PURE__ */ new Set(), definitionLever: null };
|
|
12347
|
+
const { data, error } = await ctx.client.from("memory").select("key, content").eq("project_id", ctx.projectId).eq("topic", GOALS_TOPIC).eq("provenance", "stated").is("superseded_by", null);
|
|
12373
12348
|
if (error) throw new Error(`write_context failed reading the founder's goals: ${error.message}`);
|
|
12374
|
-
|
|
12349
|
+
const definition = data.find((r) => r.key === DEFINITION_KEY);
|
|
12350
|
+
return { stated: new Set(data.map((r) => r.key)), definitionLever: definition ? parseDefinition(definition.content)?.lever ?? null : null };
|
|
12375
12351
|
}
|
|
12376
12352
|
async function writeContext(ctx, items, refused = []) {
|
|
12377
12353
|
const written = [];
|
|
@@ -13909,7 +13885,7 @@ import { promisify as promisify7 } from "util";
|
|
|
13909
13885
|
// package.json
|
|
13910
13886
|
var package_default = {
|
|
13911
13887
|
name: "@nanopm/cli",
|
|
13912
|
-
version: "0.1.
|
|
13888
|
+
version: "0.1.5",
|
|
13913
13889
|
description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
|
|
13914
13890
|
type: "module",
|
|
13915
13891
|
license: "MIT",
|
package/package.json
CHANGED
package/skills/onboard/SKILL.md
CHANGED
|
@@ -152,7 +152,9 @@ product is.
|
|
|
152
152
|
`nanopm personas` deepens them afterwards with the market view in hand.
|
|
153
153
|
- one aim (`goals`, key `objective-1`), unless a stated one exists: the founder may
|
|
154
154
|
have written theirs on the web while you were reading, so list_context with topic
|
|
155
|
-
`goals` right before this, and propose nothing over a stated one. If the
|
|
155
|
+
`goals` right before this, and propose nothing over a stated one. If the founder
|
|
156
|
+
already defined what it counts (`objective-1-definition`, stated), the aim is on **that
|
|
157
|
+
lever**, never another: they chose it. If the write is
|
|
156
158
|
refused because `objective-1` already exists, the founder set it meanwhile: drop it,
|
|
157
159
|
never write it again (dogo-3, 2026-10-01: a retry cost a round trip). Its first words
|
|
158
160
|
name the lever: `Lever: activation.`, `retention`, `acquisition` or `monetization`;
|
|
@@ -171,8 +173,8 @@ product is.
|
|
|
171
173
|
code does is the pitch with a wider adjective, and it is exactly the row this rule exists
|
|
172
174
|
to stop (docs/nanopm-53-direction.md §3). If the product's own words do not say where the
|
|
173
175
|
company is going, write nothing under these two topics and say so in your final log: the
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
card asks the founder for the mission in their own words, and the business card drafts a
|
|
177
|
+
mission and a vision with them when they ask. Do not write a gap for it: the card asks.
|
|
176
178
|
Only when the repo states a stance in words (a README that says "maintenance mode",
|
|
177
179
|
a roadmap that says "growth"), call set_preferences with `stance_provenance:
|
|
178
180
|
"inferred"`; otherwise do not call it at all. The stance is `grow` until someone says
|