@getnoan/wizard 0.1.1 → 0.1.2

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.1",
3
+ "version": "0.1.2",
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
@@ -86,6 +86,21 @@ export function runSeeds(dest, env, { dryRun = false } = {}) {
86
86
  return { slugs, rows };
87
87
  }
88
88
 
89
+ /** The pack's grounding check: which blocks the agents read hold no fact. Runs in the clone; absent in an old pack. */
90
+ export function runGroundingCheck(dest, env, { dryRun = false } = {}) {
91
+ const p = path.join(dest, "agents", "grounding-check.mjs");
92
+ if (!existsSync(p)) return { available: false };
93
+ const r = spawnSync(process.execPath, [p, "--json", ...(dryRun ? ["--dry-run"] : [])], { cwd: path.join(dest, "agents"), encoding: "utf8", env: { ...process.env, ...env } });
94
+ return { available: true, ...parseGroundingOutput(r.stdout), error: r.status === 0 ? undefined : (r.stderr || "").trim().split("\n").slice(-1)[0] };
95
+ }
96
+ export function parseGroundingOutput(text) {
97
+ for (const line of String(text || "").split("\n").reverse()) {
98
+ if (!line.trim().startsWith("{")) continue;
99
+ try { const j = JSON.parse(line); return { gaps: j.gaps || [], filed: j.filed || [] }; } catch {}
100
+ }
101
+ return { gaps: [], filed: [] };
102
+ }
103
+
89
104
  export function dispatchDryRun(dest, workflow, dryRun) {
90
105
  if (dryRun) return { workflow, action: "would dispatch" };
91
106
  const r = gh(["workflow", "run", workflow, "-f", "dry_run=1"], { cwd: dest });
package/src/run.mjs CHANGED
@@ -86,7 +86,7 @@ export async function run(args) {
86
86
  ui.step(5, "The agent pack");
87
87
  let wantAgents = args.agents;
88
88
  if (wantAgents == null) wantAgents = ui.interactive ? await ui.confirm("Set up the six open-source agents on your GitHub account too?", false) : false;
89
- if (wantAgents) report.steps.agents = await setupAgents({ args, ui, dir, key, me });
89
+ if (wantAgents) report.steps.agents = await setupAgents({ args, ui, dir, key, me, report });
90
90
  else { ui.info("skipped" + (args.yes && args.agents == null ? " (pass --agents to include it)" : "")); report.steps.agents = { skipped: true }; }
91
91
 
92
92
  /* 6. report */
@@ -96,7 +96,7 @@ export async function run(args) {
96
96
 
97
97
  async function ask(ui, q, o) { return ui.ask(q, o); }
98
98
 
99
- async function setupAgents({ args, ui, dir, key, me }) {
99
+ async function setupAgents({ args, ui, dir, key, me, report }) {
100
100
  const ready = pack.ghReady();
101
101
  if (!ready.ok) { ui.warn(ready.reason); return { ok: false, reason: ready.reason }; }
102
102
  const fork = pack.forkAndClone(dir, { dryRun: args.dryRun });
@@ -131,6 +131,16 @@ async function setupAgents({ args, ui, dir, key, me }) {
131
131
  out.seeds = seeds.rows;
132
132
  for (const r of seeds.rows) ui.ok(`${r.seed}: ${r.action}${r.slugs != null ? ` (${r.slugs} slug(s))` : ""}${r.error ? ` — ${r.error}` : ""}`);
133
133
  Object.assign(vars, seeds.slugs);
134
+ // The blocks the agents read that hold no fact: one task each on the user's board, and a line in the report.
135
+ const grounding = args.dryRun ? { available: false } : pack.runGroundingCheck(fork.dest, { NOAN_PERSONAL_API_KEY: key });
136
+ out.grounding = grounding;
137
+ if (grounding.available) {
138
+ if (grounding.gaps.length) {
139
+ ui.warn(`${grounding.gaps.length} block(s) the agents read hold no fact — ${grounding.filed.filter(f => f.action === "filed").length} task(s) filed on your NOAN board`);
140
+ for (const g of grounding.gaps) ui.info(` ${g.title}: needed by ${g.agents.join(", ")}`);
141
+ report.next.push({ id: "grounding", say: `Fill the ${grounding.gaps.length} block(s) the agents read (tasks are on your NOAN board): ${grounding.gaps.map(g => g.title).join(", ")}.`, why: "an agent grounded in an empty block fails quietly" });
142
+ } else ui.ok("every block the agents read holds a fact");
143
+ }
134
144
  for (const [n, v] of Object.entries(vars)) { if (!v) continue; const r = pack.setVariable(fork.dest, n, v, args.dryRun); out.variables.push(r); }
135
145
  ui.ok(`${out.variables.length} repository variable(s) ${args.dryRun ? "to set" : "set"} (DRY_RUN=1: every agent stays in safe mode)`);
136
146
  // No point dispatching a run that will only fail for a missing key: say what is missing instead.