@getnoan/wizard 0.1.4 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getnoan/wizard",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "One command from nothing to a grounded agent: sign in to NOAN, wire your coding assistant, install the skill, and set up the agent pack.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/agents.mjs CHANGED
@@ -6,7 +6,7 @@
6
6
  * the pack's documented ones. Safe mode stays on: the wizard never sets DRY_RUN to 0.
7
7
  */
8
8
  import { spawnSync } from "node:child_process";
9
- import { existsSync } from "node:fs";
9
+ import { existsSync, readFileSync } from "node:fs";
10
10
  import path from "node:path";
11
11
  import { randomBytes } from "node:crypto";
12
12
  import { writeEnv } from "./env-file.mjs";
@@ -128,15 +128,36 @@ export function setVariable(dest, name, value, dryRun) {
128
128
  return { name, action: r.status === 0 ? "set" : "failed", error: r.status === 0 ? undefined : (r.stderr || "").trim().split("\n")[0] };
129
129
  }
130
130
 
131
+ /** Where a pack file lives in the clone, named as the pack names it (`seed-deck.mjs`).
132
+ *
133
+ * The pack groups agents/ by agent — agents/<agent>/ for one agent's files, agents/shared/
134
+ * for the rest — and writes agents/pack-layout.json mapping each name to its path. A pack
135
+ * cloned before that grouping has neither the folders nor the map, and every file sits
136
+ * directly in agents/. Both are live at once (a fork keeps whatever it was cloned from), so
137
+ * both are read.
138
+ *
139
+ * A layout file that exists but cannot be read THROWS. Falling back to the flat path would
140
+ * report every seed "missing" and seed nothing — the silent failure the map exists to stop. */
141
+ export function packFile(dest, name) {
142
+ const layoutPath = path.join(dest, "agents", "pack-layout.json");
143
+ if (existsSync(layoutPath)) {
144
+ let files;
145
+ try { files = JSON.parse(readFileSync(layoutPath, "utf8")).files; }
146
+ catch (e) { throw new Error(`agents/pack-layout.json in ${dest} is not readable JSON: ${e.message}`); }
147
+ if (files?.[name]) return path.join(dest, "agents", files[name]);
148
+ }
149
+ return path.join(dest, "agents", name);
150
+ }
151
+
131
152
  /** Run every seed script the pack ships; collect the block slugs they print. */
132
153
  export function runSeeds(dest, env, { dryRun = false } = {}) {
133
154
  const seeds = ["seed-weekly-activity-report.mjs", "seed-fact-alignment.mjs", "seed-market-research-refresh.mjs", "seed-customer-support.mjs", "seed-deck.mjs"];
134
155
  const slugs = {}; const rows = [];
135
156
  for (const s of seeds) {
136
- const p = path.join(dest, "agents", s);
157
+ const p = packFile(dest, s);
137
158
  if (!existsSync(p)) { rows.push({ seed: s, action: "missing" }); continue; }
138
159
  if (dryRun) { rows.push({ seed: s, action: "would run" }); continue; }
139
- const r = spawnSync(process.execPath, [p], { cwd: path.join(dest, "agents"), encoding: "utf8", env: { ...process.env, ...env } });
160
+ const r = spawnSync(process.execPath, [p], { cwd: path.dirname(p), encoding: "utf8", env: { ...process.env, ...env } });
140
161
  const found = parseSeedOutput((r.stdout || "") + "\n" + (r.stderr || ""));
141
162
  Object.assign(slugs, found);
142
163
  rows.push({ seed: s, action: r.status === 0 ? "ran" : "failed", slugs: Object.keys(found).length, error: r.status === 0 ? undefined : (r.stderr || r.stdout || "").trim().split("\n").slice(-1)[0] });
@@ -146,9 +167,9 @@ export function runSeeds(dest, env, { dryRun = false } = {}) {
146
167
 
147
168
  /** The pack's grounding check: which blocks the agents read hold no fact. Runs in the clone; absent in an old pack. */
148
169
  export function runGroundingCheck(dest, env, { dryRun = false } = {}) {
149
- const p = path.join(dest, "agents", "grounding-check.mjs");
170
+ const p = packFile(dest, "grounding-check.mjs");
150
171
  if (!existsSync(p)) return { available: false };
151
- const r = spawnSync(process.execPath, [p, "--json", ...(dryRun ? ["--dry-run"] : [])], { cwd: path.join(dest, "agents"), encoding: "utf8", env: { ...process.env, ...env } });
172
+ const r = spawnSync(process.execPath, [p, "--json", ...(dryRun ? ["--dry-run"] : [])], { cwd: path.dirname(p), encoding: "utf8", env: { ...process.env, ...env } });
152
173
  return { available: true, ...parseGroundingOutput(r.stdout), error: r.status === 0 ? undefined : (r.stderr || "").trim().split("\n").slice(-1)[0] };
153
174
  }
154
175
  export function parseGroundingOutput(text) {
package/src/run.mjs CHANGED
@@ -168,7 +168,8 @@ async function setupAgents({ args, ui, dir, key, me, report }) {
168
168
  pack.writePackEnv(fork.dest, { NOAN_PERSONAL_API_KEY: key, ...(anthropic && { [mName]: anthropic }), ...(mBase && { ANTHROPIC_BASE_URL: mBase }), ...(resend && { RESEND_API_KEY: resend }) }, args.dryRun);
169
169
  const seeds = pack.runSeeds(fork.dest, { NOAN_PERSONAL_API_KEY: key }, { dryRun: args.dryRun });
170
170
  out.seeds = seeds.rows;
171
- for (const r of seeds.rows) ui.ok(`${r.seed}: ${r.action}${r.slugs != null ? ` (${r.slugs} slug(s))` : ""}${r.error ? ` — ${r.error}` : ""}`);
171
+ // A missing or failed seed is a warning, not a tick: its agent has no Config fact and no slug variable.
172
+ for (const r of seeds.rows) (/^(missing|failed)$/.test(r.action) ? ui.warn : ui.ok)(`${r.seed}: ${r.action}${r.slugs != null ? ` (${r.slugs} slug(s))` : ""}${r.error ? ` — ${r.error}` : ""}`);
172
173
  Object.assign(vars, seeds.slugs);
173
174
  // The blocks the agents read that hold no fact: one task each on the user's board, and a line in the report.
174
175
  const grounding = args.dryRun ? { available: false } : pack.runGroundingCheck(fork.dest, { NOAN_PERSONAL_API_KEY: key });