@open-agent-toolkit/cli 0.2.65 → 0.2.67

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 (238) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/configuration.md +33 -2
  3. package/assets/docs/cli-utilities/index.md +3 -0
  4. package/assets/docs/cli-utilities/project-log.md +63 -4
  5. package/assets/docs/cli-utilities/remote-project-management.md +191 -0
  6. package/assets/docs/contributing/code.md +8 -4
  7. package/assets/docs/contributing/skills.md +29 -17
  8. package/assets/docs/docs-tooling/commands.md +3 -1
  9. package/assets/docs/provider-sync/manifest-and-drift.md +2 -18
  10. package/assets/docs/reference/cli-reference.md +12 -1
  11. package/assets/docs/reference/file-locations.md +22 -0
  12. package/assets/docs/reference/oat-directory-structure.md +30 -17
  13. package/assets/docs/reference/troubleshooting.md +44 -0
  14. package/assets/docs/workflows/projects/lifecycle.md +17 -0
  15. package/assets/docs/workflows/projects/picking-up-projects.md +18 -0
  16. package/assets/docs/workflows/skills/index.md +33 -0
  17. package/assets/docs/workflows/skills/repo-improve.md +7 -3
  18. package/assets/public-package-versions.json +4 -4
  19. package/assets/skills/create-agnostic-skill/SKILL.md +19 -37
  20. package/assets/skills/create-agnostic-skill/references/docs/skills-guide.md +58 -15
  21. package/assets/skills/create-agnostic-skill/references/skill-template.md +17 -15
  22. package/assets/skills/oat-agent-instructions-apply/SKILL.md +1 -1
  23. package/assets/skills/oat-agent-instructions-apply/references/instruction-file-templates/glob-scoped-rule.md +1 -4
  24. package/assets/skills/oat-doctor/SKILL.md +3 -6
  25. package/assets/skills/oat-pjm-remote/SKILL.md +101 -0
  26. package/assets/skills/oat-pjm-remote/references/external-action-protocol.md +43 -0
  27. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
  28. package/assets/skills/oat-project-complete/SKILL.md +158 -30
  29. package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +26 -7
  30. package/assets/skills/oat-project-complete/scripts/validate-durable-archive-receipt.mjs +183 -0
  31. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
  32. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
  33. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +1 -1
  34. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
  35. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
  36. package/assets/skills/oat-project-retro/SKILL.md +1 -1
  37. package/assets/skills/oat-project-retro/references/apply-procedure.md +11 -0
  38. package/assets/skills/oat-project-review-provide/SKILL.md +4 -4
  39. package/assets/skills/oat-project-summary/SKILL.md +24 -2
  40. package/assets/skills/oat-repo-improve/SKILL.md +9 -2
  41. package/assets/skills/oat-repo-improve/references/plan-template.md +4 -6
  42. package/assets/skills/oat-repo-knowledge-index/SKILL.md +11 -9
  43. package/assets/skills/oat-wave-execute/SKILL.md +20 -6
  44. package/assets/skills/oat-wave-program/SKILL.md +9 -4
  45. package/dist/commands/backlog/new.d.ts +1 -0
  46. package/dist/commands/backlog/new.d.ts.map +1 -1
  47. package/dist/commands/backlog/new.js +3 -1
  48. package/dist/commands/config/index.d.ts +3 -1
  49. package/dist/commands/config/index.d.ts.map +1 -1
  50. package/dist/commands/config/index.js +362 -36
  51. package/dist/commands/docs/index-generate/index.d.ts.map +1 -1
  52. package/dist/commands/docs/index-generate/index.js +68 -12
  53. package/dist/commands/docs/init/index.d.ts +11 -0
  54. package/dist/commands/docs/init/index.d.ts.map +1 -1
  55. package/dist/commands/docs/init/index.js +19 -1
  56. package/dist/commands/gate/index.d.ts.map +1 -1
  57. package/dist/commands/gate/index.js +10 -2
  58. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  59. package/dist/commands/instructions/instructions.utils.js +42 -9
  60. package/dist/commands/internal/validate-skill-version-bumps.d.ts.map +1 -1
  61. package/dist/commands/internal/validate-skill-version-bumps.js +5 -5
  62. package/dist/commands/pjm/doctor.d.ts +2 -0
  63. package/dist/commands/pjm/doctor.d.ts.map +1 -1
  64. package/dist/commands/pjm/doctor.js +71 -1
  65. package/dist/commands/pjm/index.d.ts +2 -0
  66. package/dist/commands/pjm/index.d.ts.map +1 -1
  67. package/dist/commands/pjm/index.js +3 -0
  68. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts +60 -0
  69. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts.map +1 -0
  70. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.js +151 -0
  71. package/dist/commands/pjm/remote/association.d.ts +27 -0
  72. package/dist/commands/pjm/remote/association.d.ts.map +1 -0
  73. package/dist/commands/pjm/remote/association.js +68 -0
  74. package/dist/commands/pjm/remote/authority.d.ts +210 -0
  75. package/dist/commands/pjm/remote/authority.d.ts.map +1 -0
  76. package/dist/commands/pjm/remote/authority.js +341 -0
  77. package/dist/commands/pjm/remote/batch.d.ts +15 -0
  78. package/dist/commands/pjm/remote/batch.d.ts.map +1 -0
  79. package/dist/commands/pjm/remote/batch.js +126 -0
  80. package/dist/commands/pjm/remote/closeout.d.ts +69 -0
  81. package/dist/commands/pjm/remote/closeout.d.ts.map +1 -0
  82. package/dist/commands/pjm/remote/closeout.js +208 -0
  83. package/dist/commands/pjm/remote/create-binding.d.ts +62 -0
  84. package/dist/commands/pjm/remote/create-binding.d.ts.map +1 -0
  85. package/dist/commands/pjm/remote/create-binding.js +126 -0
  86. package/dist/commands/pjm/remote/credential-safety.d.ts +7 -0
  87. package/dist/commands/pjm/remote/credential-safety.d.ts.map +1 -0
  88. package/dist/commands/pjm/remote/credential-safety.js +21 -0
  89. package/dist/commands/pjm/remote/discussion.d.ts +49 -0
  90. package/dist/commands/pjm/remote/discussion.d.ts.map +1 -0
  91. package/dist/commands/pjm/remote/discussion.js +124 -0
  92. package/dist/commands/pjm/remote/doctor.d.ts +22 -0
  93. package/dist/commands/pjm/remote/doctor.d.ts.map +1 -0
  94. package/dist/commands/pjm/remote/doctor.js +380 -0
  95. package/dist/commands/pjm/remote/external-action.d.ts +208 -0
  96. package/dist/commands/pjm/remote/external-action.d.ts.map +1 -0
  97. package/dist/commands/pjm/remote/external-action.js +367 -0
  98. package/dist/commands/pjm/remote/host-execution.d.ts +28 -0
  99. package/dist/commands/pjm/remote/host-execution.d.ts.map +1 -0
  100. package/dist/commands/pjm/remote/host-execution.js +63 -0
  101. package/dist/commands/pjm/remote/index.d.ts +41 -0
  102. package/dist/commands/pjm/remote/index.d.ts.map +1 -0
  103. package/dist/commands/pjm/remote/index.js +280 -0
  104. package/dist/commands/pjm/remote/jira-adf.d.ts +35 -0
  105. package/dist/commands/pjm/remote/jira-adf.d.ts.map +1 -0
  106. package/dist/commands/pjm/remote/jira-adf.js +169 -0
  107. package/dist/commands/pjm/remote/lifecycle.d.ts +163 -0
  108. package/dist/commands/pjm/remote/lifecycle.d.ts.map +1 -0
  109. package/dist/commands/pjm/remote/lifecycle.js +224 -0
  110. package/dist/commands/pjm/remote/local-projection.d.ts +30 -0
  111. package/dist/commands/pjm/remote/local-projection.d.ts.map +1 -0
  112. package/dist/commands/pjm/remote/local-projection.js +95 -0
  113. package/dist/commands/pjm/remote/managed-markdown.d.ts +30 -0
  114. package/dist/commands/pjm/remote/managed-markdown.d.ts.map +1 -0
  115. package/dist/commands/pjm/remote/managed-markdown.js +128 -0
  116. package/dist/commands/pjm/remote/migrate.d.ts +14 -0
  117. package/dist/commands/pjm/remote/migrate.d.ts.map +1 -0
  118. package/dist/commands/pjm/remote/migrate.js +32 -0
  119. package/dist/commands/pjm/remote/operation-state.d.ts +15 -0
  120. package/dist/commands/pjm/remote/operation-state.d.ts.map +1 -0
  121. package/dist/commands/pjm/remote/operation-state.js +143 -0
  122. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts +25 -0
  123. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts.map +1 -0
  124. package/dist/commands/pjm/remote/outbound-projection-safety.js +99 -0
  125. package/dist/commands/pjm/remote/output.d.ts +60 -0
  126. package/dist/commands/pjm/remote/output.d.ts.map +1 -0
  127. package/dist/commands/pjm/remote/output.js +48 -0
  128. package/dist/commands/pjm/remote/preview.d.ts +88 -0
  129. package/dist/commands/pjm/remote/preview.d.ts.map +1 -0
  130. package/dist/commands/pjm/remote/preview.js +154 -0
  131. package/dist/commands/pjm/remote/provider-conformance.d.ts +13 -0
  132. package/dist/commands/pjm/remote/provider-conformance.d.ts.map +1 -0
  133. package/dist/commands/pjm/remote/provider-conformance.js +37 -0
  134. package/dist/commands/pjm/remote/provider.d.ts +57 -0
  135. package/dist/commands/pjm/remote/provider.d.ts.map +1 -0
  136. package/dist/commands/pjm/remote/provider.js +28 -0
  137. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts +46 -0
  138. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts.map +1 -0
  139. package/dist/commands/pjm/remote/providers/github-publication-safety.js +126 -0
  140. package/dist/commands/pjm/remote/providers/github.d.ts +207 -0
  141. package/dist/commands/pjm/remote/providers/github.d.ts.map +1 -0
  142. package/dist/commands/pjm/remote/providers/github.js +1456 -0
  143. package/dist/commands/pjm/remote/providers/jira.d.ts +245 -0
  144. package/dist/commands/pjm/remote/providers/jira.d.ts.map +1 -0
  145. package/dist/commands/pjm/remote/providers/jira.js +1650 -0
  146. package/dist/commands/pjm/remote/providers/linear.d.ts +182 -0
  147. package/dist/commands/pjm/remote/providers/linear.d.ts.map +1 -0
  148. package/dist/commands/pjm/remote/providers/linear.js +1289 -0
  149. package/dist/commands/pjm/remote/purpose-policy.d.ts +29 -0
  150. package/dist/commands/pjm/remote/purpose-policy.d.ts.map +1 -0
  151. package/dist/commands/pjm/remote/purpose-policy.js +118 -0
  152. package/dist/commands/pjm/remote/reconcile.d.ts +42 -0
  153. package/dist/commands/pjm/remote/reconcile.d.ts.map +1 -0
  154. package/dist/commands/pjm/remote/reconcile.js +101 -0
  155. package/dist/commands/pjm/remote/resolution.d.ts +122 -0
  156. package/dist/commands/pjm/remote/resolution.d.ts.map +1 -0
  157. package/dist/commands/pjm/remote/resolution.js +243 -0
  158. package/dist/commands/pjm/remote/schema.d.ts +9309 -0
  159. package/dist/commands/pjm/remote/schema.d.ts.map +1 -0
  160. package/dist/commands/pjm/remote/schema.js +1503 -0
  161. package/dist/commands/pjm/remote/service.d.ts +20 -0
  162. package/dist/commands/pjm/remote/service.d.ts.map +1 -0
  163. package/dist/commands/pjm/remote/service.js +5121 -0
  164. package/dist/commands/pjm/remote/shared-storage.d.ts +36 -0
  165. package/dist/commands/pjm/remote/shared-storage.d.ts.map +1 -0
  166. package/dist/commands/pjm/remote/shared-storage.js +110 -0
  167. package/dist/commands/pjm/remote/snapshot.d.ts +32 -0
  168. package/dist/commands/pjm/remote/snapshot.d.ts.map +1 -0
  169. package/dist/commands/pjm/remote/snapshot.js +140 -0
  170. package/dist/commands/pjm/remote/storage-locator.d.ts +29 -0
  171. package/dist/commands/pjm/remote/storage-locator.d.ts.map +1 -0
  172. package/dist/commands/pjm/remote/storage-locator.js +62 -0
  173. package/dist/commands/pjm/remote/store.d.ts +85 -0
  174. package/dist/commands/pjm/remote/store.d.ts.map +1 -0
  175. package/dist/commands/pjm/remote/store.js +553 -0
  176. package/dist/commands/pjm/remote/verification.d.ts +40 -0
  177. package/dist/commands/pjm/remote/verification.d.ts.map +1 -0
  178. package/dist/commands/pjm/remote/verification.js +100 -0
  179. package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
  180. package/dist/commands/project/dispatch-ceiling/index.js +32 -18
  181. package/dist/commands/project/log/append.d.ts +48 -0
  182. package/dist/commands/project/log/append.d.ts.map +1 -1
  183. package/dist/commands/project/log/append.js +207 -30
  184. package/dist/commands/project/log/check.d.ts +87 -1
  185. package/dist/commands/project/log/check.d.ts.map +1 -1
  186. package/dist/commands/project/log/check.js +139 -5
  187. package/dist/commands/project/log/grammar.d.ts +165 -0
  188. package/dist/commands/project/log/grammar.d.ts.map +1 -1
  189. package/dist/commands/project/log/grammar.js +219 -1
  190. package/dist/commands/project/log/rollup.d.ts +16 -1
  191. package/dist/commands/project/log/rollup.d.ts.map +1 -1
  192. package/dist/commands/project/log/rollup.js +22 -2
  193. package/dist/commands/project/log/synthesize.d.ts +16 -0
  194. package/dist/commands/project/log/synthesize.d.ts.map +1 -1
  195. package/dist/commands/project/log/synthesize.js +85 -9
  196. package/dist/commands/status/index.d.ts.map +1 -1
  197. package/dist/commands/status/index.js +24 -2
  198. package/dist/commands/sync/apply.d.ts.map +1 -1
  199. package/dist/commands/sync/apply.js +6 -4
  200. package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
  201. package/dist/commands/tools/shared/pack-manifest.js +1 -0
  202. package/dist/config/dispatch-matrix.d.ts.map +1 -1
  203. package/dist/config/dispatch-matrix.js +13 -5
  204. package/dist/config/oat-config.d.ts +41 -0
  205. package/dist/config/oat-config.d.ts.map +1 -1
  206. package/dist/config/oat-config.js +247 -9
  207. package/dist/config/own-keys.d.ts +22 -0
  208. package/dist/config/own-keys.d.ts.map +1 -0
  209. package/dist/config/own-keys.js +32 -0
  210. package/dist/config/resolve.d.ts +11 -0
  211. package/dist/config/resolve.d.ts.map +1 -1
  212. package/dist/config/resolve.js +35 -11
  213. package/dist/config/sync-config.d.ts.map +1 -1
  214. package/dist/config/sync-config.js +16 -0
  215. package/dist/drift/detector.d.ts.map +1 -1
  216. package/dist/drift/detector.js +52 -1
  217. package/dist/drift/skill-view-diagnostic.d.ts.map +1 -1
  218. package/dist/drift/skill-view-diagnostic.js +2 -23
  219. package/dist/engine/compute-plan.d.ts.map +1 -1
  220. package/dist/engine/compute-plan.js +22 -72
  221. package/dist/engine/managed-copy-hash.d.ts +69 -0
  222. package/dist/engine/managed-copy-hash.d.ts.map +1 -0
  223. package/dist/engine/managed-copy-hash.js +175 -0
  224. package/dist/fs/assets.d.ts +11 -0
  225. package/dist/fs/assets.d.ts.map +1 -1
  226. package/dist/fs/assets.js +1 -1
  227. package/dist/manifest/hash.d.ts +110 -0
  228. package/dist/manifest/hash.d.ts.map +1 -1
  229. package/dist/manifest/hash.js +111 -13
  230. package/dist/providers/ceiling/registry.d.ts.map +1 -1
  231. package/dist/providers/ceiling/registry.js +6 -1
  232. package/dist/providers/identity/dispatch-report.d.ts.map +1 -1
  233. package/dist/providers/identity/dispatch-report.js +4 -1
  234. package/dist/release/public-package-contract.d.ts.map +1 -1
  235. package/dist/release/public-package-contract.js +21 -0
  236. package/dist/validation/skills.d.ts.map +1 -1
  237. package/dist/validation/skills.js +305 -40
  238. package/package.json +2 -2
@@ -5,8 +5,39 @@ import { type ProjectLogSection, type ProjectLogScope, type ProjectLogType } fro
5
5
  export declare const PROJECT_LOG_FILENAME = "project-log.md";
6
6
  export declare const SYNTHESIS_PENDING_HEADING = "## End-of-run synthesis (pending \u2014 do not skip at project completion)";
7
7
  export declare const SYNTHESIS_COMPLETE_HEADING = "## End-of-run synthesis";
8
+ /**
9
+ * The completion seal a project log already carries.
10
+ *
11
+ * `count` is reported rather than collapsed so a log double-sealed before the
12
+ * seal append became idempotent stays legible instead of looking like a normal
13
+ * single-sealed log; `heading` is always the **first** seal, which is the one
14
+ * that actually closed the log.
15
+ */
16
+ export interface ProjectLogSeal {
17
+ heading: string;
18
+ date: string;
19
+ keyed: boolean;
20
+ count: number;
21
+ }
8
22
  export interface ProjectLogCheckResult {
9
- status: 'ok' | 'absent' | 'synthesis_pending';
23
+ /**
24
+ * A *sealed* log deliberately keeps its ordinary status and carries `sealed`
25
+ * alongside it, because it is a healthy log that must keep routing normally —
26
+ * a distinct value there would break every `status: "ok"` route in
27
+ * `oat-project-complete/SKILL.md` and `oat-project-summary/SKILL.md`.
28
+ *
29
+ * `ambiguous` is the opposite case and is the one value that may join them.
30
+ * The log's markers have two readings, so no verdict below can be computed
31
+ * honestly: every other field is zeroed and must be treated as unknown, not as
32
+ * a finding. Breaking the routing is the point — both mutators already refuse
33
+ * this file, and `check` answering `ok` while they refuse is what let the
34
+ * completion gate trust a reader that disagreed with every writer.
35
+ */
36
+ status: 'ok' | 'absent' | 'synthesis_pending' | 'ambiguous';
37
+ /** Why the log has two readings, or null when it has one. */
38
+ ambiguity: string | null;
39
+ sealed: boolean;
40
+ seal: ProjectLogSeal | null;
10
41
  logPath: string | null;
11
42
  entryCounts: {
12
43
  structural: number;
@@ -60,7 +91,62 @@ export interface ProjectLogCheckCommandDependencies extends CheckProjectLogDepen
60
91
  resolveProjectRoot: (cwd: string) => Promise<string>;
61
92
  }
62
93
  export declare function findCanonicalProjectLogSynthesisSection(content: string): ProjectLogSynthesisSection;
94
+ /**
95
+ * Parses the entries region into typed entries.
96
+ *
97
+ * The split is `/\r?\n/`, not `'\n'`. With a bare LF split every line of a
98
+ * CRLF log keeps a trailing `\r`, which no heading pattern can match — their
99
+ * `$` sits after a `[^·\r\n]+` run — so a CRLF log parsed to zero entries and
100
+ * an existing completion seal was invisible to `check` and to the append guard.
101
+ * A *lone* CR is still not a boundary, which is what keeps a body from forging
102
+ * a heading; only the CRLF pair is folded in.
103
+ */
63
104
  export declare function parseProjectLogEntries(content: string): ParsedProjectLog;
105
+ /**
106
+ * Summarizes the completion seal carried by already-parsed entries.
107
+ *
108
+ * Seal identity is `isProjectLogSealEntry` and nothing else, so `check` and
109
+ * `append` cannot drift apart on what "sealed" means.
110
+ */
111
+ export declare function summarizeProjectLogSeal(entries: readonly ParsedProjectLogEntry[]): ProjectLogSeal | null;
112
+ /**
113
+ * Finds the completion seal in raw project-log content.
114
+ *
115
+ * `append` uses this so its refusal and `check`'s report are the same reading
116
+ * of the same file.
117
+ */
118
+ /**
119
+ * The reason this log has two readings, or undefined when it has one.
120
+ *
121
+ * The single question `check`, `rollup`, both append paths and `synthesize` all
122
+ * ask before trusting a parse. It is one function rather than one call each,
123
+ * because a surface that asked a slightly different question is exactly how
124
+ * `rollup` came to rewrite `summary.md` from a log the other three refused,
125
+ * reporting output byte-identical to an empty log.
126
+ *
127
+ * Two shapes qualify. A heading that a `^…$/m` reader starts a line at and the
128
+ * line-feed-only parser does not; and a completion seal that is physically in
129
+ * the file but outside the region the parser reads, where "is this sealed?" has
130
+ * one answer to a human and another to the parser.
131
+ */
132
+ export declare function findProjectLogAmbiguity(content: string, logPath: string): string | undefined;
133
+ /**
134
+ * Explains a refusal caused by a seal the entries parser cannot reach, naming
135
+ * the actual obstruction.
136
+ *
137
+ * The first version of this message assumed the only way to hide a seal was to
138
+ * place it outside `## Entries`, and offered that remedy for a CRLF log whose
139
+ * seal sat exactly where it belonged — a remedy that did not apply and could
140
+ * not be followed, on the one path a completion run cannot get past.
141
+ *
142
+ * Shared by both mutators so one file cannot produce two different accounts of
143
+ * why it was refused.
144
+ */
145
+ export declare function unreachableProjectLogSealError(logPath: string, content: string, seal: {
146
+ line: string;
147
+ index: number;
148
+ }): Error;
149
+ export declare function findProjectLogSeal(content: string): ProjectLogSeal | null;
64
150
  export declare function checkProjectLog(input: CheckProjectLogInput, overrides?: Partial<CheckProjectLogDependencies>): Promise<ProjectLogCheckResult>;
65
151
  export declare function createProjectLogCheckCommand(overrides?: Partial<ProjectLogCheckCommandDependencies>): Command;
66
152
  //# sourceMappingURL=check.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/check.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ import { resolveActiveProject, } from '../../../config/oat-config.js';
6
6
  import { dirExists, fileExists } from '../../../fs/io.js';
7
7
  import { resolveProjectRoot } from '../../../fs/paths.js';
8
8
  import { Command } from 'commander';
9
- import { findProjectLogSections, isProjectLogEntryMarker, JUDGMENT_HEADING_RE, PROJECT_LOG_TYPES, STRUCTURAL_HEADING_RE, } from './grammar.js';
9
+ import { carriesProjectLogSealKey, containsAmbiguousProjectLogMarker, findProjectLogSealHeadingLine, findProjectLogSections, isProjectLogEntryMarker, isProjectLogSealEntry, JUDGMENT_HEADING_RE, PROJECT_LOG_TYPES, STRUCTURAL_HEADING_RE, } from './grammar.js';
10
10
  export const PROJECT_LOG_FILENAME = 'project-log.md';
11
11
  export const SYNTHESIS_PENDING_HEADING = '## End-of-run synthesis (pending — do not skip at project completion)';
12
12
  export const SYNTHESIS_COMPLETE_HEADING = '## End-of-run synthesis';
@@ -32,8 +32,10 @@ function emptyCounts() {
32
32
  scopeCounts: { project: 0, general: 0 },
33
33
  };
34
34
  }
35
+ /** The heading that opens the region entries are parsed from. */
36
+ const ENTRIES_HEADING = '## Entries';
35
37
  function entriesSection(content) {
36
- const section = findProjectLogSections(content).find(({ heading }) => heading === '## Entries');
38
+ const section = findProjectLogSections(content).find(({ heading }) => heading === ENTRIES_HEADING);
37
39
  if (!section) {
38
40
  return '';
39
41
  }
@@ -54,8 +56,18 @@ export function findCanonicalProjectLogSynthesisSection(content) {
54
56
  section,
55
57
  };
56
58
  }
59
+ /**
60
+ * Parses the entries region into typed entries.
61
+ *
62
+ * The split is `/\r?\n/`, not `'\n'`. With a bare LF split every line of a
63
+ * CRLF log keeps a trailing `\r`, which no heading pattern can match — their
64
+ * `$` sits after a `[^·\r\n]+` run — so a CRLF log parsed to zero entries and
65
+ * an existing completion seal was invisible to `check` and to the append guard.
66
+ * A *lone* CR is still not a boundary, which is what keeps a body from forging
67
+ * a heading; only the CRLF pair is folded in.
68
+ */
57
69
  export function parseProjectLogEntries(content) {
58
- const lines = entriesSection(content).split('\n');
70
+ const lines = entriesSection(content).split(/\r?\n/);
59
71
  const entries = [];
60
72
  const grammarViolations = [];
61
73
  for (let index = 0; index < lines.length; index += 1) {
@@ -101,6 +113,90 @@ export function parseProjectLogEntries(content) {
101
113
  }
102
114
  return { entries, grammarViolations };
103
115
  }
116
+ /**
117
+ * Summarizes the completion seal carried by already-parsed entries.
118
+ *
119
+ * Seal identity is `isProjectLogSealEntry` and nothing else, so `check` and
120
+ * `append` cannot drift apart on what "sealed" means.
121
+ */
122
+ export function summarizeProjectLogSeal(entries) {
123
+ const seals = entries.filter((entry) => entry.class === 'structural' && isProjectLogSealEntry(entry));
124
+ const first = seals[0];
125
+ if (first === undefined) {
126
+ return null;
127
+ }
128
+ return {
129
+ heading: first.heading,
130
+ date: first.date,
131
+ keyed: carriesProjectLogSealKey(first.body),
132
+ count: seals.length,
133
+ };
134
+ }
135
+ /**
136
+ * Finds the completion seal in raw project-log content.
137
+ *
138
+ * `append` uses this so its refusal and `check`'s report are the same reading
139
+ * of the same file.
140
+ */
141
+ /**
142
+ * The reason this log has two readings, or undefined when it has one.
143
+ *
144
+ * The single question `check`, `rollup`, both append paths and `synthesize` all
145
+ * ask before trusting a parse. It is one function rather than one call each,
146
+ * because a surface that asked a slightly different question is exactly how
147
+ * `rollup` came to rewrite `summary.md` from a log the other three refused,
148
+ * reporting output byte-identical to an empty log.
149
+ *
150
+ * Two shapes qualify. A heading that a `^…$/m` reader starts a line at and the
151
+ * line-feed-only parser does not; and a completion seal that is physically in
152
+ * the file but outside the region the parser reads, where "is this sealed?" has
153
+ * one answer to a human and another to the parser.
154
+ */
155
+ export function findProjectLogAmbiguity(content, logPath) {
156
+ if (containsAmbiguousProjectLogMarker(content)) {
157
+ return `Project log ${logPath} has a '## ' heading, or a dated '### ' entry heading, starting a line after a carriage return, U+2028, or U+2029 rather than a line feed, so its structure has two readings and no seal or count can be reported honestly. Replace those line terminators with line feeds.`;
158
+ }
159
+ if (findProjectLogSeal(content) === null) {
160
+ const unreachableSeal = findProjectLogSealHeadingLine(content);
161
+ if (unreachableSeal !== undefined) {
162
+ // Delegated so the precise diagnosis — which of the two obstructions this
163
+ // is — survives being reached through the shared predicate. Collapsing
164
+ // both causes into one sentence here would undo the round that made the
165
+ // remedy actionable.
166
+ return unreachableProjectLogSealError(logPath, content, unreachableSeal)
167
+ .message;
168
+ }
169
+ }
170
+ return undefined;
171
+ }
172
+ /**
173
+ * Explains a refusal caused by a seal the entries parser cannot reach, naming
174
+ * the actual obstruction.
175
+ *
176
+ * The first version of this message assumed the only way to hide a seal was to
177
+ * place it outside `## Entries`, and offered that remedy for a CRLF log whose
178
+ * seal sat exactly where it belonged — a remedy that did not apply and could
179
+ * not be followed, on the one path a completion run cannot get past.
180
+ *
181
+ * Shared by both mutators so one file cannot produce two different accounts of
182
+ * why it was refused.
183
+ */
184
+ export function unreachableProjectLogSealError(logPath, content, seal) {
185
+ // Compared by offset, not by substring membership: prose inside `## Entries`
186
+ // can quote a seal heading verbatim, and a membership test would then report a
187
+ // seal that really sits under a later section as correctly placed, sending the
188
+ // operator to rewrite line terminators that were never the obstruction.
189
+ const entries = findProjectLogSections(content).find(({ heading }) => heading === ENTRIES_HEADING);
190
+ const outsideEntries = entries === undefined ||
191
+ seal.index < entries.start ||
192
+ seal.index >= entries.end;
193
+ return new Error(`Project log ${logPath} already carries the completion-seal heading '${seal.line.trim()}', but the entries parser cannot reach it, so whether the log is sealed has two answers and nothing may be read from it or written to it with confidence. ${outsideEntries
194
+ ? `The seal sits outside the '${ENTRIES_HEADING}' section; move it under that heading, then retry.`
195
+ : 'The seal is under the correct heading, so the obstruction is its line terminators; rewrite the log with line feeds, then retry.'}`);
196
+ }
197
+ export function findProjectLogSeal(content) {
198
+ return summarizeProjectLogSeal(parseProjectLogEntries(content).entries);
199
+ }
104
200
  async function resolveTargetProject(input, dependencies) {
105
201
  if (input.project) {
106
202
  const projectPath = isAbsolute(input.project)
@@ -125,6 +221,9 @@ export async function checkProjectLog(input, overrides = {}) {
125
221
  if (!(await fileExists(logPath))) {
126
222
  return {
127
223
  status: 'absent',
224
+ ambiguity: null,
225
+ sealed: false,
226
+ seal: null,
128
227
  logPath: null,
129
228
  ...emptyCounts(),
130
229
  lastEntryDate: null,
@@ -133,6 +232,24 @@ export async function checkProjectLog(input, overrides = {}) {
133
232
  };
134
233
  }
135
234
  const content = await readFile(logPath, 'utf8');
235
+ // Fail closed, exactly as every other surface does on this same file. `check`
236
+ // used to be the one reader that always produced a confident verdict: on a
237
+ // `preamble<U+2028>## Entries` log it answered `sealed` and count fields the
238
+ // writers refused to act on, and the completion gate keys on those fields.
239
+ const ambiguity = findProjectLogAmbiguity(content, logPath);
240
+ if (ambiguity !== undefined) {
241
+ return {
242
+ status: 'ambiguous',
243
+ ambiguity,
244
+ sealed: false,
245
+ seal: null,
246
+ logPath,
247
+ ...emptyCounts(),
248
+ lastEntryDate: null,
249
+ synthesisPending: false,
250
+ grammarViolations: [],
251
+ };
252
+ }
136
253
  const parsed = parseProjectLogEntries(content);
137
254
  const counts = emptyCounts();
138
255
  for (const entry of parsed.entries) {
@@ -145,8 +262,12 @@ export async function checkProjectLog(input, overrides = {}) {
145
262
  }
146
263
  }
147
264
  const synthesisPending = findProjectLogSections(content).some(({ heading }) => heading === SYNTHESIS_PENDING_HEADING);
265
+ const seal = summarizeProjectLogSeal(parsed.entries);
148
266
  return {
149
267
  status: synthesisPending ? 'synthesis_pending' : 'ok',
268
+ ambiguity: null,
269
+ sealed: seal !== null,
270
+ seal,
150
271
  logPath,
151
272
  ...counts,
152
273
  lastEntryDate: parsed.entries.at(-1)?.date ?? null,
@@ -161,14 +282,27 @@ async function runCheckCommand(options, context, dependencies) {
161
282
  if (context.json) {
162
283
  context.logger.json(result);
163
284
  }
285
+ else if (result.status === 'ambiguous') {
286
+ context.logger.error(result.ambiguity ?? 'Project log has two readings.');
287
+ }
164
288
  else {
165
289
  context.logger.info(result.status === 'absent'
166
290
  ? 'Project log: absent'
167
291
  : `Project log: ${result.status}; entries: ${result.entryCounts.structural +
168
- PROJECT_LOG_TYPES.reduce((total, type) => total + result.entryCounts.judgment[type], 0)}`);
292
+ PROJECT_LOG_TYPES.reduce((total, type) => total + result.entryCounts.judgment[type], 0)}${result.seal
293
+ ? `; sealed${result.seal.count > 1
294
+ ? ` (${result.seal.count} seal entries)`
295
+ : ''}`
296
+ : ''}`);
169
297
  }
298
+ // An ambiguous log exits non-zero whether or not synthesis was required: a
299
+ // caller that only checks the exit status must not read this as a clean log,
300
+ // and both mutators refuse the same file.
170
301
  process.exitCode =
171
- options.requireSynthesis && result.synthesisPending ? 1 : 0;
302
+ result.status === 'ambiguous' ||
303
+ (options.requireSynthesis === true && result.synthesisPending)
304
+ ? 1
305
+ : 0;
172
306
  }
173
307
  catch (error) {
174
308
  const message = error instanceof Error ? error.message : String(error);
@@ -13,7 +13,103 @@ export interface ProjectLogSection {
13
13
  }
14
14
  export declare function isProjectLogSectionMarker(line: string): boolean;
15
15
  export declare function isProjectLogEntryMarker(line: string): boolean;
16
+ /**
17
+ * Every character JavaScript treats as a LineTerminator, and therefore every
18
+ * character a multiline `^` anchors after: LF, CR, U+2028 LINE SEPARATOR, and
19
+ * U+2029 PARAGRAPH SEPARATOR. VT, FF, and U+0085 NEL are deliberately absent —
20
+ * ECMAScript does not count them, so `^` does not match after them either.
21
+ *
22
+ * The set is exported rather than inlined because the append validator and this
23
+ * parser must enumerate exactly the same characters. A validator that split on
24
+ * fewer terminators than the parser anchored after is precisely how a body
25
+ * could smuggle a `## ` past validation and have it become a real section
26
+ * boundary, silently truncating the parsed entries region and voiding the
27
+ * completion seal.
28
+ */
29
+ export declare const PROJECT_LOG_LINE_TERMINATOR_RE: RegExp;
30
+ /**
31
+ * A *lone* line terminator: a carriage return not followed by a line feed, or a
32
+ * U+2028 / U+2029 separator.
33
+ *
34
+ * CRLF is deliberately excluded, and that carve-out is the whole point. Every
35
+ * reader in this module treats `\r\n` as exactly one boundary — `splitProjectLogLines`,
36
+ * the `/\r?\n/` scans, and `parseProjectLogEntries` — so a CRLF body has one
37
+ * reading and is accepted, as it was before this guard existed. A *lone* CR has
38
+ * two readings (a boundary to a multiline `^`, body text to everything here),
39
+ * and so do U+2028 and U+2029, so those stay refused.
40
+ *
41
+ * Written as one pattern rather than a strip-then-test so the rule is legible at
42
+ * the point of use: `\r(?!\n)` is "a carriage return that is not part of CRLF".
43
+ */
44
+ export declare const PROJECT_LOG_LONE_TERMINATOR_RE: RegExp;
45
+ /**
46
+ * Canonicalizes accepted line endings to LF.
47
+ *
48
+ * The command parses leniently and writes strictly: CRLF is accepted from a
49
+ * caller — a `--body -` fed from a file, tool output, or a pasted buffer — and
50
+ * stored as LF, so a log this command writes never contains a terminator other
51
+ * than LF. That makes the file's single reading a property of its bytes rather
52
+ * than of every reader agreeing about them, which is the weaker guarantee this
53
+ * module kept failing to hold.
54
+ *
55
+ * It is not needed to satisfy `rollup`'s `^…$/m` ledger scan: a multiline `$`
56
+ * matches before a CR as readily as before an LF, so that scan reads a CRLF log
57
+ * correctly and always did. Storing LF is a narrowing of what future readers
58
+ * must cope with, not a repair of a present disagreement.
59
+ */
60
+ export declare function normalizeProjectLogLineEndings(value: string): string;
61
+ /**
62
+ * Splits on every LineTerminator, treating CRLF as one boundary.
63
+ *
64
+ * Validators use this rather than a `/\r?\n/` split so that a lone CR, a
65
+ * U+2028, or a U+2029 starts a new line for validation just as it would for a
66
+ * regular expression's multiline `^`.
67
+ */
68
+ export declare function splitProjectLogLines(content: string): string[];
69
+ /**
70
+ * The log's `## ` section boundaries, where a boundary is a `## ` that begins
71
+ * the content or directly follows a line feed.
72
+ *
73
+ * LF is the only boundary on purpose. The previous `/^## [^\r\n]+/gm` anchored
74
+ * after all four LineTerminators while the append validator only split on
75
+ * `/\r?\n/`, so a judgment body reading `carrier<CR>## Injected` passed
76
+ * validation and then became a real section boundary — truncating `## Entries`
77
+ * so `check` reported an already-written seal as `sealed: false` and appends
78
+ * kept succeeding onto a sealed log. Anchoring on LF alone keeps this parser
79
+ * and `parseProjectLogEntries`, which splits its section on `/\r?\n/`, reading
80
+ * the same file the same way, and it holds for a log written by hand as well as one
81
+ * the command wrote.
82
+ */
16
83
  export declare function findProjectLogSections(content: string): ProjectLogSection[];
84
+ /**
85
+ * Whether the log contains a heading that a `^…$/m` reader starts a line at but
86
+ * an LF-only reader does not — that is, whether the file has two readings.
87
+ *
88
+ * The two competing readers are enumerated rather than approximated: the
89
+ * pre-fix section scan `/^## [^\r\n]+/gm`, and `rollup.ts`'s ledger scan, which
90
+ * recognizes only the complete dated judgment grammar. A `### ` line that
91
+ * neither recognizes — `carrier<CR>### Notes` — is body text to every reader and
92
+ * is deliberately *not* flagged; refusing it would strand logs a previous
93
+ * release could legitimately have written.
94
+ *
95
+ * Mutators consult this and refuse rather than guess, because a file with two
96
+ * readings cannot answer "is this sealed?". Tightening the section parser to LF
97
+ * alone would otherwise silently *widen* such logs: a hand-written
98
+ * `preamble<U+2028>## Entries` used to parse, so its seal was found and a
99
+ * replayed seal was deduplicated, and under an LF-only reading the same file has
100
+ * no entries region, reports unsealed, and accepts a second seal. Refusing is
101
+ * the only resolution that is not weaker than the reading it replaces.
102
+ *
103
+ * CRLF is deliberately not caught, and the entries parser now earns that
104
+ * exemption rather than merely asserting it. The character immediately before a
105
+ * marker on a `\r\n`-terminated line is the LF, so a `## ` section boundary was
106
+ * always unambiguous; `### ` entries and the structural heading pattern were not,
107
+ * because their `$` cannot match before a trailing `\r`, so a CRLF log parsed to
108
+ * zero entries and an existing seal was invisible. `parseProjectLogEntries` now
109
+ * splits on `/\r?\n/`, which makes all three agree and makes "CRLF has exactly
110
+ * one reading" a fact about this module rather than a hope.
111
+ */
112
+ export declare function containsAmbiguousProjectLogMarker(content: string): boolean;
17
113
  export declare function composeJudgmentHeading(input: {
18
114
  date: string;
19
115
  scope: ProjectLogScope;
@@ -25,4 +121,73 @@ export declare function composeStructuralHeading(input: {
25
121
  producer: string;
26
122
  ref: string;
27
123
  }): string;
124
+ /**
125
+ * The producer and ref that together identify the completion seal — the last
126
+ * entry a project log may ever receive.
127
+ *
128
+ * Both fields are required. A `seal` ref from another producer is an ordinary
129
+ * structural entry, and an `oat-project-complete` entry with another ref is an
130
+ * ordinary lifecycle entry; treating either as a seal would let a foreign
131
+ * append freeze a log that was never completed.
132
+ */
133
+ export declare const PROJECT_LOG_SEAL_PRODUCER = "oat-project-complete";
134
+ export declare const PROJECT_LOG_SEAL_REF = "seal";
135
+ /**
136
+ * The prefix of the seal's idempotency token (`oat-seal:<project>`).
137
+ *
138
+ * The token is what makes a replayed seal recognize its own entry, so it is
139
+ * prefixed rather than bare: a project name alone could occur in ordinary prose
140
+ * and match an unrelated body.
141
+ */
142
+ export declare const PROJECT_LOG_SEAL_KEY_PREFIX = "oat-seal:";
143
+ /**
144
+ * The single definition of "this entry is the completion seal", shared by the
145
+ * `check` probe and the `append` guard.
146
+ *
147
+ * Both callers route on this one predicate on purpose: two definitions of
148
+ * sealed is the regression that lets a log report itself unsealed to one
149
+ * command and sealed to the other.
150
+ */
151
+ export declare function isProjectLogSealEntry(entry: {
152
+ producer: string;
153
+ ref: string;
154
+ }): boolean;
155
+ /**
156
+ * Whether a composed or parsed entry *heading* is the completion seal's.
157
+ *
158
+ * The heading-level counterpart of `isProjectLogSealEntry`, and it delegates to
159
+ * it rather than restating the rule. One predicate serves both directions —
160
+ * "is this request the seal?" and "does this already-appended heading name a
161
+ * seal?" — so the two can never drift into disagreeing about what a seal
162
+ * heading is. It reads the producer and ref back out of the heading text rather
163
+ * than from a caller's flags, which is the same normalization
164
+ * `parseProjectLogEntries` applies.
165
+ */
166
+ export declare function isProjectLogSealHeading(heading: string): boolean;
167
+ /**
168
+ * The raw line of `content` that is a completion-seal heading, or undefined.
169
+ *
170
+ * Deliberately independent of `parseProjectLogEntries`: it answers "is a seal
171
+ * physically in this file?" where the parser answers "can this file's structure
172
+ * be read to contain a seal?". The gap between those two questions is the state
173
+ * every mutator must refuse, and all of them ask it the same way.
174
+ *
175
+ * The offset is returned alongside the text because callers must locate the
176
+ * seal, not merely recognize it: prose inside `## Entries` can quote a seal
177
+ * heading verbatim, so a substring test would place a seal that really sits
178
+ * under a later section inside the entries region and blame the wrong cause.
179
+ */
180
+ export declare function findProjectLogSealHeadingLine(content: string): {
181
+ line: string;
182
+ index: number;
183
+ } | undefined;
184
+ /**
185
+ * Whether a seal body carries the canonical stand-alone seal token.
186
+ *
187
+ * `idempotencyToken` records the whole whitespace-delimited word containing a
188
+ * key, so a key glued to the varying completion timestamp never matches its own
189
+ * earlier append. A seal written before this convention carries no token at
190
+ * all, which is exactly why the seal is also recognized structurally.
191
+ */
192
+ export declare function carriesProjectLogSealKey(body: string): boolean;
28
193
  //# sourceMappingURL=grammar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/grammar.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,YAC5B,KAAK,EACL,UAAU,EACV,aAAa,EACb,UAAU,CACF,CAAC;AACX,eAAO,MAAM,kBAAkB,YAAI,SAAS,EAAE,SAAS,CAAU,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,6BAA6B,WAAM,CAAC;AAEjD,eAAO,MAAM,mBAAmB,QACqE,CAAC;AACtG,eAAO,MAAM,qBAAqB,QACoC,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAO3E;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAET;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,MAAM,CAET"}
1
+ {"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/log/grammar.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,YAC5B,KAAK,EACL,UAAU,EACV,aAAa,EACb,UAAU,CACF,CAAC;AACX,eAAO,MAAM,kBAAkB,YAAI,SAAS,EAAE,SAAS,CAAU,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,6BAA6B,WAAM,CAAC;AAEjD,eAAO,MAAM,mBAAmB,QACqE,CAAC;AACtG,eAAO,MAAM,qBAAqB,QACoC,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,QAAuB,CAAC;AAEnE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,QAA4B,CAAC;AAExE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAO3E;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAc1E;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAET;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,MAAM,CAET;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,yBAAyB,CAAC;AAChE,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAKV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAShE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,GACd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAY7C;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQ9D"}