@deftai/directive-core 0.92.0 → 0.94.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.
Files changed (111) hide show
  1. package/dist/cache/fetch.d.ts +29 -0
  2. package/dist/cache/fetch.js +131 -4
  3. package/dist/cache/operations.d.ts +8 -0
  4. package/dist/cache/operations.js +67 -4
  5. package/dist/check/cached-orchestrator.js +11 -0
  6. package/dist/check/consumer-gate-integrity.d.ts +68 -0
  7. package/dist/check/consumer-gate-integrity.js +265 -0
  8. package/dist/check/index.d.ts +1 -0
  9. package/dist/check/index.js +1 -0
  10. package/dist/check/orchestrator.js +10 -0
  11. package/dist/content-contracts/skills/greptile-detector.d.ts +9 -0
  12. package/dist/content-contracts/skills/greptile-detector.js +9 -2
  13. package/dist/doctor/index.d.ts +1 -0
  14. package/dist/doctor/index.js +1 -0
  15. package/dist/doctor/main.js +69 -0
  16. package/dist/doctor/openclaw-l2-adapter.d.ts +26 -0
  17. package/dist/doctor/openclaw-l2-adapter.js +199 -0
  18. package/dist/finish-loop/pr-finish-loop.js +1 -0
  19. package/dist/hooks/dispatcher.d.ts +24 -2
  20. package/dist/hooks/dispatcher.js +122 -11
  21. package/dist/hooks/readonly.d.ts +14 -0
  22. package/dist/hooks/readonly.js +126 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.js +2 -0
  25. package/dist/init-deposit/hygiene.js +22 -0
  26. package/dist/init-deposit/index.d.ts +3 -0
  27. package/dist/init-deposit/index.js +3 -0
  28. package/dist/init-deposit/init-deposit.js +10 -0
  29. package/dist/init-deposit/refresh.js +11 -0
  30. package/dist/init-deposit/scaffold.js +3 -133
  31. package/dist/init-deposit/skill-discovery-deposit.d.ts +65 -0
  32. package/dist/init-deposit/skill-discovery-deposit.js +287 -0
  33. package/dist/init-deposit/skill-discovery-hosts.d.ts +94 -0
  34. package/dist/init-deposit/skill-discovery-hosts.js +217 -0
  35. package/dist/init-deposit/slash-deposit.d.ts +46 -0
  36. package/dist/init-deposit/slash-deposit.js +174 -0
  37. package/dist/intake/issue-ingest.d.ts +20 -0
  38. package/dist/intake/issue-ingest.js +58 -0
  39. package/dist/policy/host-slash-commands.d.ts +28 -0
  40. package/dist/policy/host-slash-commands.js +103 -0
  41. package/dist/policy/index.d.ts +2 -0
  42. package/dist/policy/index.js +61 -7
  43. package/dist/policy/min-greptile-confidence.d.ts +57 -0
  44. package/dist/policy/min-greptile-confidence.js +123 -0
  45. package/dist/pr-merge-readiness/compute.d.ts +11 -1
  46. package/dist/pr-merge-readiness/compute.js +17 -4
  47. package/dist/pr-merge-readiness/evaluate.d.ts +8 -1
  48. package/dist/pr-merge-readiness/evaluate.js +7 -4
  49. package/dist/pr-merge-readiness/mergeability.d.ts +7 -1
  50. package/dist/pr-merge-readiness/mergeability.js +14 -2
  51. package/dist/pr-monitor/main.d.ts +2 -0
  52. package/dist/pr-monitor/main.js +31 -1
  53. package/dist/pr-monitor/monitor.js +5 -1
  54. package/dist/pr-monitor/readiness.js +3 -1
  55. package/dist/pr-monitor/types.d.ts +8 -0
  56. package/dist/pr-wait-mergeable/cascade.d.ts +8 -0
  57. package/dist/pr-wait-mergeable/cascade.js +28 -1
  58. package/dist/pr-wait-mergeable/main.d.ts +2 -0
  59. package/dist/pr-wait-mergeable/main.js +16 -0
  60. package/dist/pr-wait-mergeable/types.d.ts +3 -1
  61. package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
  62. package/dist/pr-wait-mergeable/wrappers.js +3 -0
  63. package/dist/pr-watch/constants.d.ts +4 -1
  64. package/dist/pr-watch/constants.js +5 -2
  65. package/dist/pr-watch/main.js +1 -0
  66. package/dist/pr-watch/probe.d.ts +1 -1
  67. package/dist/pr-watch/probe.js +4 -1
  68. package/dist/pr-watch/types.d.ts +7 -1
  69. package/dist/pr-watch/watch.js +3 -2
  70. package/dist/review-monitor/index.d.ts +1 -0
  71. package/dist/review-monitor/index.js +1 -0
  72. package/dist/review-monitor/l4-owner.d.ts +46 -0
  73. package/dist/review-monitor/l4-owner.js +262 -0
  74. package/dist/slash/emitters.d.ts +102 -0
  75. package/dist/slash/emitters.js +148 -0
  76. package/dist/slash/generator.d.ts +98 -0
  77. package/dist/slash/generator.js +145 -0
  78. package/dist/slash/index.d.ts +16 -0
  79. package/dist/slash/index.js +16 -0
  80. package/dist/slash/openclaw-adapter.d.ts +64 -0
  81. package/dist/slash/openclaw-adapter.js +198 -0
  82. package/dist/slash/openclaw-deposit.d.ts +73 -0
  83. package/dist/slash/openclaw-deposit.js +279 -0
  84. package/dist/slash/openclaw-slugs.d.ts +52 -0
  85. package/dist/slash/openclaw-slugs.js +126 -0
  86. package/dist/slash/product-set.d.ts +50 -0
  87. package/dist/slash/product-set.js +142 -0
  88. package/dist/tool-events/classify.js +1 -0
  89. package/dist/triage/welcome/writers.js +16 -7
  90. package/dist/umbrella-current-shape/index.d.ts +45 -0
  91. package/dist/umbrella-current-shape/index.js +162 -8
  92. package/dist/vbrief-reconcile/index.d.ts +2 -2
  93. package/dist/vbrief-reconcile/index.js +1 -1
  94. package/dist/vbrief-reconcile/types.d.ts +14 -0
  95. package/dist/vbrief-reconcile/umbrellas.d.ts +31 -2
  96. package/dist/vbrief-reconcile/umbrellas.js +231 -19
  97. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  98. package/dist/vbrief-validate/plan-hooks.js +50 -0
  99. package/dist/xbrief/create.d.ts +36 -0
  100. package/dist/xbrief/create.js +285 -0
  101. package/dist/xbrief/index.d.ts +14 -0
  102. package/dist/xbrief/index.js +42 -0
  103. package/dist/xbrief/paths.d.ts +37 -0
  104. package/dist/xbrief/paths.js +123 -0
  105. package/dist/xbrief/styles.d.ts +36 -0
  106. package/dist/xbrief/styles.js +235 -0
  107. package/dist/xbrief/types.d.ts +50 -0
  108. package/dist/xbrief/types.js +17 -0
  109. package/dist/xbrief/verify.d.ts +30 -0
  110. package/dist/xbrief/verify.js +251 -0
  111. package/package.json +11 -3
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, } from "node:fs";
2
2
  import { dirname, join, resolve } from "node:path";
3
3
  import { containedWrite } from "../../fs/contained-write.js";
4
+ import { assertWriteTargetSafe } from "../../fs/projection-containment.js";
4
5
  import { hasArtifactSuffix, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
5
6
  import { migrateLegacyPolicyKey, PLAN_ONBOARDING_KEY, PLAN_POLICY_KEY, } from "../../policy/plan-extensions.js";
6
7
  import { projectDefinitionMutationLock } from "../../vbrief-build/project-definition-io.js";
@@ -32,21 +33,29 @@ export function appendAuditEntry(projectRoot, entry) {
32
33
  });
33
34
  return logPath;
34
35
  }
35
- function atomicWrite(_projectRoot, path, data) {
36
- const dir = dirname(path);
37
- mkdirSync(dir, { recursive: true });
36
+ /**
37
+ * Atomically write PROJECT-DEFINITION JSON under projectRoot containment (#3077).
38
+ * Containment root is projectRoot (not dirname(path)) so a force-added `xbrief/`
39
+ * directory symlink fails closed before temp+rename — same class as #3042.
40
+ */
41
+ function atomicWrite(projectRoot, path, data) {
42
+ const root = resolve(projectRoot);
43
+ const targetAbs = resolve(path);
44
+ // Refuse leaf/parent symlinks and out-of-root targets before temp+rename.
45
+ assertWriteTargetSafe(root, targetAbs);
46
+ const dir = dirname(targetAbs);
38
47
  const payload = `${JSON.stringify(data, null, 2)}\n`;
39
48
  const tmpName = `.${Date.now()}.tmp`;
40
49
  const tmp = join(dir, tmpName);
41
50
  try {
42
- // #2980 wave D: temp payload write uses containedWrite under the parent dir.
51
+ // #2980 wave D / #3077: product write routes through containedWrite under projectRoot.
43
52
  containedWrite({
44
- root: resolve(dir),
45
- target: tmpName,
53
+ root,
54
+ target: tmp,
46
55
  data: payload,
47
56
  mode: "create",
48
57
  });
49
- renameSync(tmp, path);
58
+ renameSync(tmp, targetAbs);
50
59
  }
51
60
  catch (err) {
52
61
  try {
@@ -66,6 +66,51 @@ export interface RunCurrentShapeOptions {
66
66
  readonly writeOut?: (text: string) => void;
67
67
  readonly writeErr?: (text: string) => void;
68
68
  }
69
+ /**
70
+ * Map a GitHub REST issue-comment object (or an already-normalized row) onto
71
+ * the internal `IssueComment` shape. Used by `umbrella:current-shape`, the
72
+ * triage cache put path, and `issue:ingest` so all three share one selector.
73
+ */
74
+ export declare function mapIssueCommentEntry(entry: unknown): IssueComment | null;
75
+ /**
76
+ * Labels that mark an issue as umbrella/tracker-like for cache comment enrichment
77
+ * (#1870). Mirrors `TRACKER_LABELS` in scope/open-umbrella-warning.ts.
78
+ */
79
+ export declare const UMBRELLA_TRACKER_LABELS: ReadonlySet<string>;
80
+ /**
81
+ * Key under which `cache:fetch-all` / `cache:put` stash the issue comment thread
82
+ * on the raw.json payload so `content.md` and `issue:ingest` can surface the
83
+ * canonical current-shape comment without a second live fetch (#1870).
84
+ */
85
+ export declare const RAW_ISSUE_COMMENTS_KEY: "comments";
86
+ /** Sidecar filename next to content.md carrying the selected current-shape payload. */
87
+ export declare const CURRENT_SHAPE_SIDECAR: "current-shape.json";
88
+ export interface CurrentShapeSidecar {
89
+ readonly commentId: number;
90
+ readonly htmlUrl: string;
91
+ readonly pass: number;
92
+ readonly authorLogin: string;
93
+ readonly authorAssociation: string;
94
+ readonly body: string;
95
+ }
96
+ /** True when labels or sub-issue summary mark the issue as umbrella/tracker-like. */
97
+ export declare function isUmbrellaLikeIssue(raw: Record<string, unknown>): boolean;
98
+ /** Normalize a raw issue payload's comment array (REST or pre-mapped). */
99
+ export declare function commentsFromRawPayload(raw: Record<string, unknown>): IssueComment[];
100
+ /**
101
+ * Count maintainer-authored `## Current shape` comments (for the !=1 lint).
102
+ * Non-maintainer forgeries are excluded (#2307).
103
+ */
104
+ export declare function countMaintainerCurrentShapeComments(comments: readonly IssueComment[]): number;
105
+ /** Markdown section appended to cache content.md when a canonical shape exists. */
106
+ export declare function formatCurrentShapeSection(selected: CurrentShapeComment): string;
107
+ /**
108
+ * Append the canonical current-shape comment (if any) to a rendered cache body.
109
+ * Returns the input unchanged when no maintainer-authored current-shape exists.
110
+ */
111
+ export declare function appendCurrentShapeSection(baseContent: string, raw: Record<string, unknown>): string;
112
+ /** Build the current-shape.json sidecar payload, or null when none is selectable. */
113
+ export declare function buildCurrentShapeSidecar(raw: Record<string, unknown>): CurrentShapeSidecar | null;
69
114
  /** Merge `gh api --paginate` concatenated JSON array pages into comment rows. */
70
115
  export declare function parseCommentsFromGhStdout(stdout: string): IssueComment[];
71
116
  export declare function extractPassFromBody(body: string): number | null;
@@ -44,24 +44,178 @@ export const REQUIRED_SECTIONS = [
44
44
  "readingOrder",
45
45
  ];
46
46
  export const OPTIONAL_SECTIONS = ["openQuestions"];
47
- function mapCommentEntry(entry) {
47
+ /**
48
+ * Map a GitHub REST issue-comment object (or an already-normalized row) onto
49
+ * the internal `IssueComment` shape. Used by `umbrella:current-shape`, the
50
+ * triage cache put path, and `issue:ingest` so all three share one selector.
51
+ */
52
+ export function mapIssueCommentEntry(entry) {
48
53
  if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
49
54
  return null;
50
55
  }
51
56
  const rec = entry;
52
- if (typeof rec.id !== "number" || typeof rec.body !== "string") {
57
+ // Accept either GitHub REST field names or the already-mapped camelCase shape.
58
+ const id = typeof rec.id === "number" ? rec.id : null;
59
+ const body = typeof rec.body === "string" ? rec.body : null;
60
+ if (id === null || body === null) {
53
61
  return null;
54
62
  }
55
63
  const user = typeof rec.user === "object" && rec.user !== null
56
64
  ? rec.user
57
65
  : null;
66
+ const htmlUrl = typeof rec.html_url === "string"
67
+ ? rec.html_url
68
+ : typeof rec.htmlUrl === "string"
69
+ ? rec.htmlUrl
70
+ : "";
71
+ const updatedAt = typeof rec.updated_at === "string"
72
+ ? rec.updated_at
73
+ : typeof rec.updatedAt === "string"
74
+ ? rec.updatedAt
75
+ : "";
76
+ const authorLogin = user !== null && typeof user.login === "string"
77
+ ? user.login
78
+ : typeof rec.authorLogin === "string"
79
+ ? rec.authorLogin
80
+ : "";
81
+ const authorAssociation = typeof rec.author_association === "string"
82
+ ? rec.author_association
83
+ : typeof rec.authorAssociation === "string"
84
+ ? rec.authorAssociation
85
+ : "";
86
+ return {
87
+ id,
88
+ body,
89
+ htmlUrl,
90
+ updatedAt,
91
+ authorLogin,
92
+ authorAssociation,
93
+ };
94
+ }
95
+ /** @deprecated Prefer `mapIssueCommentEntry` — kept as a private alias for call sites. */
96
+ function mapCommentEntry(entry) {
97
+ return mapIssueCommentEntry(entry);
98
+ }
99
+ /**
100
+ * Labels that mark an issue as umbrella/tracker-like for cache comment enrichment
101
+ * (#1870). Mirrors `TRACKER_LABELS` in scope/open-umbrella-warning.ts.
102
+ */
103
+ export const UMBRELLA_TRACKER_LABELS = new Set([
104
+ "epic",
105
+ "meta",
106
+ "tracker",
107
+ "type:tracker",
108
+ "status:tracker",
109
+ ]);
110
+ /**
111
+ * Key under which `cache:fetch-all` / `cache:put` stash the issue comment thread
112
+ * on the raw.json payload so `content.md` and `issue:ingest` can surface the
113
+ * canonical current-shape comment without a second live fetch (#1870).
114
+ */
115
+ export const RAW_ISSUE_COMMENTS_KEY = "comments";
116
+ /** Sidecar filename next to content.md carrying the selected current-shape payload. */
117
+ export const CURRENT_SHAPE_SIDECAR = "current-shape.json";
118
+ /** True when labels or sub-issue summary mark the issue as umbrella/tracker-like. */
119
+ export function isUmbrellaLikeIssue(raw) {
120
+ const labelsRaw = raw.labels;
121
+ if (Array.isArray(labelsRaw)) {
122
+ for (const label of labelsRaw) {
123
+ let name = "";
124
+ if (typeof label === "string") {
125
+ name = label;
126
+ }
127
+ else if (label !== null && typeof label === "object" && !Array.isArray(label)) {
128
+ const n = label.name;
129
+ if (typeof n === "string")
130
+ name = n;
131
+ }
132
+ if (name.length > 0 && UMBRELLA_TRACKER_LABELS.has(name.toLowerCase())) {
133
+ return true;
134
+ }
135
+ }
136
+ }
137
+ const summary = raw.sub_issues_summary;
138
+ if (summary !== null && typeof summary === "object" && !Array.isArray(summary)) {
139
+ const total = summary.total;
140
+ if (typeof total === "number" && Number.isFinite(total) && total > 0) {
141
+ return true;
142
+ }
143
+ }
144
+ return false;
145
+ }
146
+ /** Normalize a raw issue payload's comment array (REST or pre-mapped). */
147
+ export function commentsFromRawPayload(raw) {
148
+ const rawComments = raw[RAW_ISSUE_COMMENTS_KEY];
149
+ if (!Array.isArray(rawComments)) {
150
+ return [];
151
+ }
152
+ const out = [];
153
+ for (const entry of rawComments) {
154
+ const mapped = mapIssueCommentEntry(entry);
155
+ if (mapped !== null) {
156
+ out.push(mapped);
157
+ }
158
+ }
159
+ return out;
160
+ }
161
+ /**
162
+ * Count maintainer-authored `## Current shape` comments (for the !=1 lint).
163
+ * Non-maintainer forgeries are excluded (#2307).
164
+ */
165
+ export function countMaintainerCurrentShapeComments(comments) {
166
+ let count = 0;
167
+ for (const comment of comments) {
168
+ if (!isMaintainerAuthored(comment.authorAssociation)) {
169
+ continue;
170
+ }
171
+ if (extractPassFromBody(comment.body) !== null) {
172
+ count += 1;
173
+ }
174
+ }
175
+ return count;
176
+ }
177
+ /** Markdown section appended to cache content.md when a canonical shape exists. */
178
+ export function formatCurrentShapeSection(selected) {
179
+ const permalink = selected.htmlUrl.length > 0 ? selected.htmlUrl : `comment id ${selected.id}`;
180
+ const author = selected.authorLogin.length > 0 ? `@${selected.authorLogin}` : "maintainer";
181
+ return [
182
+ "",
183
+ "---",
184
+ "",
185
+ "## Canonical current shape (#1152 / #1870)",
186
+ "",
187
+ `_Authoritative planning surface — prefer this over the issue body (stale by design). ` +
188
+ `Source: [${permalink}](${permalink}) · pass-${selected.pass} · ${author}. ` +
189
+ `Deterministic read path: \`task umbrella:current-shape <N>\`._`,
190
+ "",
191
+ selected.body.trimEnd(),
192
+ "",
193
+ ].join("\n");
194
+ }
195
+ /**
196
+ * Append the canonical current-shape comment (if any) to a rendered cache body.
197
+ * Returns the input unchanged when no maintainer-authored current-shape exists.
198
+ */
199
+ export function appendCurrentShapeSection(baseContent, raw) {
200
+ const selected = selectCurrentShapeComment(commentsFromRawPayload(raw));
201
+ if (selected === null) {
202
+ return baseContent;
203
+ }
204
+ return `${baseContent.trimEnd()}\n${formatCurrentShapeSection(selected)}`;
205
+ }
206
+ /** Build the current-shape.json sidecar payload, or null when none is selectable. */
207
+ export function buildCurrentShapeSidecar(raw) {
208
+ const selected = selectCurrentShapeComment(commentsFromRawPayload(raw));
209
+ if (selected === null) {
210
+ return null;
211
+ }
58
212
  return {
59
- id: rec.id,
60
- body: rec.body,
61
- htmlUrl: typeof rec.html_url === "string" ? rec.html_url : "",
62
- updatedAt: typeof rec.updated_at === "string" ? rec.updated_at : "",
63
- authorLogin: user !== null && typeof user.login === "string" ? user.login : "",
64
- authorAssociation: typeof rec.author_association === "string" ? rec.author_association : "",
213
+ commentId: selected.id,
214
+ htmlUrl: selected.htmlUrl,
215
+ pass: selected.pass,
216
+ authorLogin: selected.authorLogin,
217
+ authorAssociation: selected.authorAssociation,
218
+ body: selected.body,
65
219
  };
66
220
  }
67
221
  /** Merge `gh api --paginate` concatenated JSON array pages into comment rows. */
@@ -5,6 +5,6 @@ export { PARITY_SCENARIO_NAMES, renderScenarioOutput, runParityScenario, } from
5
5
  export { pyRepr } from "./py-repr.js";
6
6
  export { buildSpecTaskIndex, detectStatusMarker, folderFromStatus, formatReconciliationMarkdown, hasDisagreement, loadOverrides, normalizeTaskId, OVERRIDES_FILENAME, parseOverridesYaml, reconcileScopeItems, writeReconciliationReport, } from "./reconciliation.js";
7
7
  export { allScopeIds, asStrList, candidateDepGraph, candidateFromPath, markCycles, } from "./swarm-deps.js";
8
- export type { Candidate, Child, ConflictEntry, LabelChange, LabelClient, ReconciledItem, ReconcileGraphOutcome, ReconcileLabelsOutcome, ReconcileUmbrellasOutcome, ReconciliationReport, SpecTaskEntry, UmbrellaChange, UmbrellaClient, } from "./types.js";
9
- export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, umbrellasOutcomeToJson, } from "./umbrellas.js";
8
+ export type { Candidate, Child, ConflictEntry, ForgeIssueState, LabelChange, LabelClient, ReconciledItem, ReconcileGraphOutcome, ReconcileLabelsOutcome, ReconcileUmbrellasOutcome, ReconciliationReport, SpecTaskEntry, UmbrellaChange, UmbrellaClient, } from "./types.js";
9
+ export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, indexChildrenByIssueNumber, isChildOpen, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileBodyChecklist, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, UNKNOWN_FOLDER, umbrellasOutcomeToJson, } from "./umbrellas.js";
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -5,5 +5,5 @@ export { PARITY_SCENARIO_NAMES, renderScenarioOutput, runParityScenario, } from
5
5
  export { pyRepr } from "./py-repr.js";
6
6
  export { buildSpecTaskIndex, detectStatusMarker, folderFromStatus, formatReconciliationMarkdown, hasDisagreement, loadOverrides, normalizeTaskId, OVERRIDES_FILENAME, parseOverridesYaml, reconcileScopeItems, writeReconciliationReport, } from "./reconciliation.js";
7
7
  export { allScopeIds, asStrList, candidateDepGraph, candidateFromPath, markCycles, } from "./swarm-deps.js";
8
- export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, umbrellasOutcomeToJson, } from "./umbrellas.js";
8
+ export { buildChildIndex, CHILD_REF_TYPE, CLOSED_FOLDERS, childFromData, classifyPassType, computeChildren, computeWaves, indexChildrenByIssueNumber, isChildOpen, LIFECYCLE_FOLDERS, nowIso, OPEN_FOLDERS, parseCurrentShape, reconcileBodyChecklist, reconcileUmbrellas, renderBody, renderUmbrellasReport, ScmUmbrellaClient, UmbrellaScmError, UNKNOWN_FOLDER, umbrellasOutcomeToJson, } from "./umbrellas.js";
9
9
  //# sourceMappingURL=index.js.map
@@ -94,7 +94,10 @@ export interface Child {
94
94
  readonly kind: string;
95
95
  readonly folder: string;
96
96
  readonly depends_on: string[];
97
+ /** Linked forge issue number when known (#1649). */
98
+ readonly issue_number?: number | null;
97
99
  }
100
+ export type ForgeIssueState = "open" | "closed";
98
101
  export interface UmbrellaChange {
99
102
  readonly story_id: string;
100
103
  readonly repo: string;
@@ -102,6 +105,11 @@ export interface UmbrellaChange {
102
105
  readonly action: "created" | "edited" | "unchanged";
103
106
  readonly pass_n: number;
104
107
  readonly body: string;
108
+ /**
109
+ * Issue-body checkbox reconcile (#1649): `edited` when checkboxes flipped,
110
+ * `unchanged` when already correct, `skipped` when body APIs unavailable.
111
+ */
112
+ readonly checklist_action?: "edited" | "unchanged" | "skipped";
105
113
  }
106
114
  export interface ReconcileUmbrellasOutcome {
107
115
  changed: UmbrellaChange[];
@@ -124,5 +132,11 @@ export interface UmbrellaClient {
124
132
  }>;
125
133
  editComment(repo: string, commentId: number, body: string): void;
126
134
  createComment(repo: string, issueNumber: number, body: string): number | null;
135
+ /** Optional: forge open/closed for child issues (#1649). Folder fallback when absent. */
136
+ fetchIssueStates?(repo: string, issueNumbers: readonly number[]): ReadonlyMap<number, ForgeIssueState>;
137
+ /** Optional: umbrella issue body for checklist reconcile (#1649). */
138
+ fetchIssueBody?(repo: string, issueNumber: number): string;
139
+ /** Optional: write reconciled checklist body (#1649). */
140
+ editIssueBody?(repo: string, issueNumber: number, body: string): void;
127
141
  }
128
142
  //# sourceMappingURL=types.d.ts.map
@@ -1,14 +1,40 @@
1
- import type { Child, ReconcileUmbrellasOutcome, UmbrellaClient } from "./types.js";
1
+ import type { Child, ForgeIssueState, ReconcileUmbrellasOutcome, UmbrellaClient } from "./types.js";
2
2
  export declare const OPEN_FOLDERS: readonly ["proposed", "pending", "active"];
3
3
  export declare const CLOSED_FOLDERS: readonly ["completed", "cancelled"];
4
4
  export declare const LIFECYCLE_FOLDERS: readonly ["proposed", "pending", "active", "completed", "cancelled"];
5
5
  export declare const CHILD_REF_TYPE = "x-xbrief/plan";
6
+ /** Synthetic lifecycle folder for issue-only children with no local xBRIEF (#1649). */
7
+ export declare const UNKNOWN_FOLDER: "unknown";
6
8
  export declare class UmbrellaScmError extends Error {
7
9
  name: string;
8
10
  }
9
11
  export declare function childFromData(data: Record<string, unknown>, folder: string, fallbackId: string): Child;
10
12
  export declare function buildChildIndex(vbriefDir: string): Record<string, Child>;
11
- export declare function computeChildren(epicData: Record<string, unknown>, index: Record<string, Child>): Child[];
13
+ /** Index children by forge issue number for github-issue ref resolution (#1649). */
14
+ export declare function indexChildrenByIssueNumber(index: Record<string, Child>): Map<number, Child>;
15
+ /**
16
+ * Resolve epic children from plan refs AND github-issue refs (#1649 defect 2).
17
+ * The epic's own github-issue ref is skipped when `epicIssueNumber` is provided.
18
+ * Issue-only children (no local xBRIEF) are synthesized with folder `unknown`.
19
+ */
20
+ export declare function computeChildren(epicData: Record<string, unknown>, index: Record<string, Child>, options?: {
21
+ epicIssueNumber?: number | null;
22
+ }): Child[];
23
+ /**
24
+ * Whether a child counts as open for current-shape + checklist (#1649 defect 3).
25
+ * Prefer forge issue state when present; fall back to lifecycle folder.
26
+ * `unknown` (issue-only, no local xBRIEF) counts open without forge confirmation.
27
+ */
28
+ export declare function isChildOpen(child: Child, forgeStates?: ReadonlyMap<number, ForgeIssueState> | null): boolean;
29
+ /**
30
+ * Reconcile umbrella issue-body checkboxes `- [ ] #N` / `- [x] #N` to child state (#1649).
31
+ * Only lines that mention a known child issue number are rewritten; other checkboxes untouched.
32
+ * Idempotent: returns `changed: false` when already correct.
33
+ */
34
+ export declare function reconcileBodyChecklist(body: string, closedIssueNumbers: ReadonlySet<number>, knownIssueNumbers: ReadonlySet<number>): {
35
+ body: string;
36
+ changed: boolean;
37
+ };
12
38
  export declare function computeWaves(children: readonly Child[]): string[][];
13
39
  export declare function renderBody(options: {
14
40
  passN: number;
@@ -34,6 +60,9 @@ export declare class ScmUmbrellaClient implements UmbrellaClient {
34
60
  }>;
35
61
  editComment(repo: string, commentId: number, body: string): void;
36
62
  createComment(repo: string, issueNumber: number, body: string): number | null;
63
+ fetchIssueStates(repo: string, issueNumbers: readonly number[]): ReadonlyMap<number, ForgeIssueState>;
64
+ fetchIssueBody(repo: string, issueNumber: number): string;
65
+ editIssueBody(repo: string, issueNumber: number, body: string): void;
37
66
  }
38
67
  export declare function nowIso(date?: Date): string;
39
68
  export interface ReconcileUmbrellasOptions {