@nodii/dev 0.6.39 → 0.6.40

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/core.js CHANGED
@@ -4,7 +4,7 @@ import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
4
  import { homedir, platform } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
6
 
7
- export const VERSION = "0.6.39";
7
+ export const VERSION = "0.6.40";
8
8
  export const configPath = () =>
9
9
  process.env.NODII_DEV_CONFIG_PATH ??
10
10
  join(homedir(), ".config/nodii-dev/config.json");
package/dist/freeze.js CHANGED
@@ -109,6 +109,7 @@ export const GATHER_LOOKUP_IDS = ["figma-guidelines"];
109
109
  /** Build the stored contract; Delta Gather keeps lane-authored sections. */
110
110
  export function freezeFromGather({
111
111
  gather,
112
+ ask,
112
113
  boundaries,
113
114
  issue,
114
115
  laneNotes,
@@ -131,7 +132,8 @@ export function freezeFromGather({
131
132
  );
132
133
  return {
133
134
  objective: {
134
- ask: gather.objective,
135
+ // Dev's Lane contract names the ask; without one, Gather's objective.
136
+ ask: ask ?? gather.objective,
135
137
  issue: issue === undefined ? (current?.objective?.issue ?? null) : issue,
136
138
  laneNotes:
137
139
  laneNotes === undefined
package/dist/lanes.js CHANGED
@@ -98,7 +98,23 @@ const agentFollowUp = (ref, shortlistId, flags) => [
98
98
  .filter((flag) => typeof flags[flag] === "string")
99
99
  .map((flag) => `--${flag} ${shellQuote(flags[flag])}`),
100
100
  ].join(" ");
101
+ // Dev's Lane contract (hub-58): objective, issue body, the coordinator's lane
102
+ // notes, done check and boundaries, printed before anything else.
103
+ const contractLines = (data) => (data.laneContract ? ["Lane contract", data.laneContract, ""] : []);
104
+ async function laneContract(id, request, options) {
105
+ try {
106
+ return (await request("GET", `/api/v1/lanes/${encode(id)}/contract`, undefined, options)).data?.text ?? null;
107
+ } catch (error) {
108
+ // A Dev from before the Lane contract route: say so, never hide it.
109
+ if (error?.code === "HTTP_404" || error?.code === "NOT_FOUND") return "(this Dev does not serve the Lane contract yet; read the lane with `nodii-dev lane show`)";
110
+ throw error;
111
+ }
112
+ }
113
+ // Gather never ends on a bare "finished" (hub-58): the next step is the task.
114
+ const nextStepText = (ref, doneCheck) =>
115
+ `Gather is finished. Next: Task. Work the objective until ${doneCheck} is met, then run \`nodii-dev lane land ${ref}\` (or the lane's finish command). Don't stop before that.`;
101
116
  const agentShortlistText = (ref, data, flags) => [
117
+ ...contractLines(data),
102
118
  `Trigger shortlist ${data.shortlistId} · ${JSON.stringify(data.objective)}`,
103
119
  "You are Gather's picker (picker: agent). Nothing is recorded yet.",
104
120
  ...data.triggers.map((trigger) => ` ${trigger.code}\t${trigger.title ?? ""}: ${trigger.sentence ?? ""}${trigger.forcedBy?.length ? ` [forced by ${trigger.forcedBy.join(", ")}]` : ""}`),
@@ -387,8 +403,10 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
387
403
  : await request("POST", "/api/v1/rules/shortlist", shortlistBody, { signal });
388
404
  const shortlistId = shortlist.data?.shortlistId;
389
405
  const triggers = shortlist.data?.triggers ?? [];
390
- if (byAgent && !byAgent.shortlist)
391
- return { payload: { data: { recorded: false, picker: "agent", shortlistId, objective, triggers } }, human: (p) => agentShortlistText(ref, p.data, flags) };
406
+ if (byAgent && !byAgent.shortlist) {
407
+ const contract = await laneContract(id, request, { signal });
408
+ return { payload: { data: { recorded: false, picker: "agent", shortlistId, objective, triggers, laneContract: contract } }, human: (p) => agentShortlistText(ref, p.data, flags) };
409
+ }
392
410
  // Only the local model pick takes the per-lane pick lock; an agent pick
393
411
  // records a choice already made (AGENT-HARNESS-19 + GATHER-NA).
394
412
  const pickInput = {
@@ -409,7 +427,8 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
409
427
  // No picker on this Mac: the agent picks from the same shortlist (hub-58).
410
428
  const fallback = agentFallbackReason(error);
411
429
  if (!fallback) throw error;
412
- return { payload: { data: { recorded: false, picker: "agent", fallback, shortlistId, objective, triggers } }, human: (p) => `picker: agent (${p.data.fallback})\n${agentShortlistText(ref, p.data, flags)}` };
430
+ const contract = await laneContract(id, request, { signal });
431
+ return { payload: { data: { recorded: false, picker: "agent", fallback, shortlistId, objective, triggers, laneContract: contract } }, human: (p) => `picker: agent (${p.data.fallback})\n${agentShortlistText(ref, p.data, flags)}` };
413
432
  }
414
433
  }
415
434
  const payload = await request("POST", `/api/v1/lanes/${encode(id)}/gather`, {
@@ -421,6 +440,7 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
421
440
  ...(sectionLimit !== undefined ? { sectionLimitPerDocument: sectionLimit } : {}),
422
441
  shortlistId,
423
442
  picker: byAgent ? "agent" : picked.model ?? PICKER_MODEL,
443
+ doneCheck,
424
444
  }, { signal });
425
445
  const figma = figmaScope.map((file) => ({
426
446
  fileKey: file.fileKey,
@@ -434,7 +454,19 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
434
454
  lane.launch?.narrowingRemainder && `Narrowing remainder: ${lane.launch.narrowingRemainder}`,
435
455
  ].filter(Boolean);
436
456
  const freezeRequest = (method, path, body, options = {}) => request(method, path, body, { ...options, signal });
437
- await storeFreeze(id, {
457
+ // Dev's Lane contract is what Gather printed; the Freeze stores it word for word.
458
+ const contract = payload.data?.contract;
459
+ const contractRepos = contract?.boundaries?.repos;
460
+ const fromContract = Boolean(contractRepos?.length && contractRepos.every((repository) => repository.repo && repository.branch && repository.worktree));
461
+ await storeFreeze(id, fromContract ? {
462
+ gather: payload.data,
463
+ ask: contract.objective.ask,
464
+ boundaries: contract.boundaries,
465
+ issue: contract.objective.issue,
466
+ laneNotes: contract.objective.laneNotes,
467
+ doneCheck: contract.objective.doneCheck ?? doneCheck,
468
+ lookups: lookup ? [lookup] : [],
469
+ } : {
438
470
  gather: payload.data,
439
471
  boundaries: { repos: repositories, ...(declaredFigma ? { figma } : {}) },
440
472
  issue,
@@ -453,7 +485,8 @@ export async function laneCommand(action, args, flags, { request = devRequest, c
453
485
  // The pick's model and latency ride along in --json (morning check records them).
454
486
  const result = { ...payload, picker: { model: picked.model ?? null, latency: picked.latency ?? null, ...(picked.timing ? { timing: picked.timing, stats: picked.stats } : {}) } };
455
487
  const timingLine = picked.timing ? `\n${pickTimingLine(picked.timing, picked.stats)}` : "";
456
- return { payload: result, human: (p) => `${gatherText(p, objective, reasons)}${scopeNotice}${timingLine}` };
488
+ const doneText = contract?.objective?.doneCheck ?? doneCheck;
489
+ return { payload: result, human: (p) => `${gatherText(p, objective, reasons)}${scopeNotice}${timingLine}\n\n${nextStepText(ref, doneText)}` };
457
490
  } catch (error) {
458
491
  if (signal.aborted) throw new CliError("GATHER_TIMEOUT", `Gather's Dev requests exceeded their ${Math.round(deadline.ms / 1000)}-second deadline (the local pick runs on its own budget); nothing was retried.`, 1);
459
492
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodii/dev",
3
- "version": "0.6.39",
3
+ "version": "0.6.40",
4
4
  "description": "Agent CLI for Nodii Dev, reached through the platform edge",
5
5
  "type": "module",
6
6
  "bin": {