@indigoai-us/hq-cli 5.186.0 → 5.187.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.187.0] — 2026-09-25
6
+
7
+ ### Fixed
8
+
9
+ - `hq mesh context prd-sync` sends only stories whose Board status changed (a first snapshot of already-done stories is not re-emitted). Quiet `session_end` summaries read Claude Code `result` lines and Codex/Grok `item.completed` agent messages, not only `type=assistant` turns.
10
+
5
11
  ## [5.186.0] — 2026-09-25
6
12
 
7
13
  ### Changed
@@ -14,7 +14,7 @@ import chalk from "chalk";
14
14
  import { loadCachedTokens, isMachineIdentity } from "../utils/cognito-session.js";
15
15
  import * as readline from "node:readline/promises";
16
16
  import { STORY_STATUSES, appendThreadEvent, ensureProjectThread, eventPayload, getProjectView, listActiveMembershipCompanies, listActiveThreads, patchStory, putProjectView, registerMeshProject, resolveActiveMembershipCompany, resolveMeshPrincipalUid, warmMeshConversationCache, } from "../lib/mesh/api.js";
17
- import { closeSupersededProjectThread, ensureMissingBoardStories, ensureProjectBoard, formatStoryStatusChanges, mergeProjectStories, } from "../lib/mesh/project-board.js";
17
+ import { closeSupersededProjectThread, ensureMissingBoardStories, ensureProjectBoard, formatStoryStatusChanges, mergeProjectStories, prdSyncEventsThatChangeBoard, } from "../lib/mesh/project-board.js";
18
18
  import { buildPreflightFixtures } from "../lib/work-context/preflight-contract.js";
19
19
  import { createCandidatesFetcher, createMigratePoster, createOrganizePoster, createWorkSessionDeliverer, fetchCompanyLive, formatCompanyLiveTable, openMeshTransport, probeMigrationCapabilityForMemberships, requireToken, } from "../lib/mesh/client.js";
20
20
  import { clearDefaultCompany, isDefaultCompanyDisabled, readDeviceConfig, recordMigrationCapabilitySnapshot, setRepairHeldWithDefault, setDefaultCompany, } from "../lib/work-context/config.js";
@@ -603,7 +603,25 @@ async function runPrdSync(opts) {
603
603
  ...(bound?.projectId ? { project: bound.projectId } : {}),
604
604
  touchedPaths: [resolvePath(file)],
605
605
  };
606
+ let toEmit = plan.events;
606
607
  if (bound?.companyUid && bound.projectId) {
608
+ try {
609
+ const live = await getProjectView(token, bound.companyUid, bound.projectId);
610
+ toEmit = prdSyncEventsThatChangeBoard(plan.events, live.stories);
611
+ }
612
+ catch {
613
+ // Board unread — emit the snapshot diff; the server drops same-status.
614
+ }
615
+ if (toEmit.length === 0) {
616
+ plan.commit();
617
+ if (opts.json) {
618
+ console.log(JSON.stringify({ ok: true, action: "context.prd-sync", changed: 0 }));
619
+ }
620
+ else {
621
+ console.log("Work mesh: prd-sync unchanged");
622
+ }
623
+ return;
624
+ }
607
625
  try {
608
626
  const source = parseProjectPlan(readFileSync(file, "utf8"), file);
609
627
  await ensureMissingBoardStories({
@@ -617,7 +635,7 @@ async function runPrdSync(opts) {
617
635
  description: source.description ?? "",
618
636
  stories: source.stories,
619
637
  repos: source.repos ?? [],
620
- requiredIds: plan.events.map((event) => event.taskId),
638
+ requiredIds: toEmit.map((event) => event.taskId),
621
639
  });
622
640
  }
623
641
  catch (err) {
@@ -625,9 +643,12 @@ async function runPrdSync(opts) {
625
643
  console.error(`work-mesh: prd-sync ensure failed; emitting anyway (${reason.slice(0, 160)})`);
626
644
  }
627
645
  }
646
+ const alreadyOnBoard = plan.events
647
+ .filter((event) => !toEmit.some((row) => row.taskId === event.taskId))
648
+ .map((event) => event.taskId);
628
649
  const meshRoot = workMeshHomeRoot();
629
- const seqFirst = reserveSessionSeq(sessionId, plan.events.length);
630
- const events = plan.events.map((event, index) => buildMeshEmitEvent({
650
+ const seqFirst = reserveSessionSeq(sessionId, toEmit.length);
651
+ const events = toEmit.map((event, index) => buildMeshEmitEvent({
631
652
  eventId: generateUlid(Date.now() + index),
632
653
  kind: "task_status",
633
654
  sessionId,
@@ -659,7 +680,7 @@ async function runPrdSync(opts) {
659
680
  blocked.add(id);
660
681
  }
661
682
  const advanced = events.flatMap((event) => (event.eventId && event.taskId && !blocked.has(event.eventId) ? [event.taskId] : []));
662
- plan.commit(advanced);
683
+ plan.commit([...alreadyOnBoard, ...advanced]);
663
684
  if (plan.taskId && advanced.includes(plan.taskId)) {
664
685
  updateSessionState(sessionId, root, (current) => {
665
686
  if (!current)
@@ -673,10 +694,10 @@ async function runPrdSync(opts) {
673
694
  });
674
695
  }
675
696
  if (opts.json) {
676
- console.log(JSON.stringify({ ok: true, action: "context.prd-sync", changed: plan.events.length, taskId: plan.taskId }));
697
+ console.log(JSON.stringify({ ok: true, action: "context.prd-sync", changed: toEmit.length, taskId: plan.taskId }));
677
698
  return;
678
699
  }
679
- console.log(`Work mesh: prd-sync emitted ${plan.events.length} task_status`);
700
+ console.log(`Work mesh: prd-sync emitted ${toEmit.length} task_status`);
680
701
  }
681
702
  function noteCallingSessionBound(projectId, company) {
682
703
  try {
@@ -75,14 +75,27 @@ export function extractLastAssistantText(jsonl) {
75
75
  const type = typeof obj.type === "string" ? obj.type : "";
76
76
  const role = typeof obj.role === "string" ? obj.role : "";
77
77
  const payload = obj.payload;
78
+ const item = obj.item;
79
+ const message = obj.message;
78
80
  let text = "";
79
81
  if (type === "assistant" || role === "assistant") {
80
82
  text = textFromUnknown(obj.message ?? obj.content);
81
83
  }
84
+ else if (message && (message.role === "assistant" || message.type === "assistant")) {
85
+ text = textFromUnknown(message.content ?? message.text ?? message);
86
+ }
82
87
  else if (payload &&
83
88
  (payload.type === "agent_message" || payload.type === "assistant")) {
84
89
  text = textFromUnknown(payload.message ?? payload.content ?? payload.text);
85
90
  }
91
+ else if (type === "item.completed" &&
92
+ item &&
93
+ item.type === "agent_message") {
94
+ text = textFromUnknown(item.text ?? item.content ?? item.message);
95
+ }
96
+ else if (type === "result" && typeof obj.result === "string") {
97
+ text = obj.result;
98
+ }
86
99
  const trimmed = text.trim();
87
100
  if (trimmed)
88
101
  last = trimmed;
@@ -4,6 +4,15 @@ export declare function mergeProjectStories(current: MeshProjectStoryInput[], pl
4
4
  resetStatus?: boolean;
5
5
  }): MeshProjectStoryInput[];
6
6
  export declare function boardStoryStatus(story: MeshProjectStoryInput): string;
7
+ /**
8
+ * Keep only prd-sync task_status events whose Board status actually differs.
9
+ * Missing Board rows stay (ensure will add them). Same-status replays of
10
+ * already-done stories must not be sent again (US-051b).
11
+ */
12
+ export declare function prdSyncEventsThatChangeBoard<T extends {
13
+ taskId: string;
14
+ status: string;
15
+ }>(events: readonly T[], boardStories: readonly MeshProjectStoryInput[]): T[];
7
16
  /** One-line summary of live→next status diffs for existing story ids. */
8
17
  export declare function formatStoryStatusChanges(current: MeshProjectStoryInput[], next: MeshProjectStoryInput[]): string | undefined;
9
18
  /**
@@ -34,6 +34,15 @@ export function boardStoryStatus(story) {
34
34
  return story.status;
35
35
  return story.passes === true ? "done" : "queued";
36
36
  }
37
+ /**
38
+ * Keep only prd-sync task_status events whose Board status actually differs.
39
+ * Missing Board rows stay (ensure will add them). Same-status replays of
40
+ * already-done stories must not be sent again (US-051b).
41
+ */
42
+ export function prdSyncEventsThatChangeBoard(events, boardStories) {
43
+ const byId = new Map(boardStories.map((story) => [story.id, boardStoryStatus(story)]));
44
+ return events.filter((event) => byId.get(event.taskId) !== event.status);
45
+ }
37
46
  /** One-line summary of live→next status diffs for existing story ids. */
38
47
  export function formatStoryStatusChanges(current, next) {
39
48
  const currentById = new Map(current.map((story) => [story.id, story]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.186.0",
3
+ "version": "5.187.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {