opencode-extended-sidebar 0.2.90 → 0.2.91
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/.oesignore +17 -17
- package/CHANGELOG.md +96 -95
- package/LICENSE +21 -21
- package/README.md +150 -235
- package/oes.json +13 -11
- package/package.json +100 -100
- package/scripts/install-git-hooks.mjs +29 -29
- package/src/pware.oc.core/constants/index.ts +18 -18
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventKind.ts +29 -29
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventName.ts +15 -13
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventType.ts +241 -241
- package/src/pware.oc.core/constants/pware.oc.core.constants.myWork.ts +55 -55
- package/src/pware.oc.core/constants/pware.oc.core.constants.partType.ts +65 -65
- package/src/pware.oc.core/constants/pware.oc.core.constants.phase.ts +58 -58
- package/src/pware.oc.core/constants/pware.oc.core.constants.pulse.ts +63 -63
- package/src/pware.oc.core/constants/pware.oc.core.constants.rowKind.ts +34 -34
- package/src/pware.oc.core/constants/pware.oc.core.constants.status.ts +74 -74
- package/src/pware.oc.core/constants/pware.oc.core.constants.toolName.ts +69 -69
- package/src/pware.oc.core/git/index.ts +7 -7
- package/src/pware.oc.core/git/pware.oc.core.git.ts +237 -237
- package/src/pware.oc.core/git/pware.oc.core.gitignore.ts +87 -87
- package/src/pware.oc.core/index.ts +19 -18
- package/src/pware.oc.core/pware.oc.core.bus.ts +42 -42
- package/src/pware.oc.core/pware.oc.core.cache.ts +48 -48
- package/src/pware.oc.core/pware.oc.core.clipboard.ts +97 -97
- package/src/pware.oc.core/pware.oc.core.debug.ts +280 -280
- package/src/pware.oc.core/pware.oc.core.events.ts +79 -79
- package/src/pware.oc.core/pware.oc.core.glyph.ts +135 -135
- package/src/pware.oc.core/pware.oc.core.layout.ts +234 -234
- package/src/pware.oc.core/pware.oc.core.oes.ts +99 -89
- package/src/pware.oc.core/pware.oc.core.paths.ts +240 -240
- package/src/pware.oc.core/pware.oc.core.preview.ts +74 -74
- package/src/pware.oc.core/pware.oc.core.pulse.ts +726 -726
- package/src/pware.oc.core/pware.oc.core.runtimeConfig.ts +41 -0
- package/src/pware.oc.core/pware.oc.core.sqlite.ts +164 -175
- package/src/pware.oc.core/pware.oc.core.sqliteGateway.ts +89 -0
- package/src/pware.oc.core/pware.oc.core.status.ts +214 -214
- package/src/pware.oc.core/pware.oc.core.timing.ts +63 -63
- package/src/pware.oc.core/pware.oc.core.width.ts +215 -215
- package/src/pware.oc.omo/constants/index.ts +15 -15
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.ts +18 -18
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.boulderStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.docKind.ts +29 -29
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.eventName.ts +11 -11
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.planStatus.ts +60 -60
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.reviewStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.startWork.ts +25 -25
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.verdict.ts +38 -38
- package/src/pware.oc.omo/index.ts +7 -7
- package/src/pware.oc.omo/resolver/index.ts +19 -19
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approval.ts +179 -179
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalGroup.ts +116 -116
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.ts +34 -34
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.boulder.ts +503 -503
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.config.ts +40 -40
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.doc.ts +269 -269
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.draftFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.notepadsFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.plan.ts +155 -155
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.planFile.ts +363 -361
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.proofFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.rulesFile.ts +11 -11
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.runContinuationFile.ts +14 -14
- package/src/pware.oc.opencode/constants/index.ts +10 -10
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.eventName.ts +15 -15
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.fileTouch.ts +18 -18
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.questionKind.ts +45 -45
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.sessionStatus.ts +58 -58
- package/src/pware.oc.opencode/index.ts +8 -8
- package/src/pware.oc.opencode/pware.oc.opencode.events.ts +182 -182
- package/src/pware.oc.opencode/pware.oc.opencode.files.ts +399 -399
- package/src/pware.oc.opencode/resolver/index.ts +223 -170
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.file.ts +150 -162
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.question.ts +149 -185
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.ts +248 -211
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.ts +22 -26
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.tool.ts +224 -236
- package/src/pware.oc.perf/index.ts +14 -14
- package/src/pware.oc.perf/pware.oc.perf.asciichart.d.ts +15 -15
- package/src/pware.oc.perf/pware.oc.perf.charts.ts +284 -284
- package/src/pware.oc.perf/pware.oc.perf.reader.ts +962 -927
- package/src/pware.oc.perf/pware.oc.perf.realtime.ts +88 -88
- package/src/pware.oc.perf/pware.oc.perf.realtimeBlock.ts +118 -118
- package/src/pware.oc.perf/pware.oc.perf.realtimeCpuRam.ts +79 -79
- package/src/pware.oc.perf/pware.oc.perf.realtimeSampler.ts +71 -71
- package/src/pware.oc.perf/pware.oc.perf.realtimeTimeline.ts +214 -214
- package/src/pware.oc.perf/pware.oc.perf.self.ts +179 -179
- package/src/pware.oc.perf/pware.oc.perf.view.tsx +463 -462
- package/src/pware.oc.runtime/index.ts +13 -13
- package/src/pware.oc.runtime/pware.oc.runtime.monitor.ts +142 -142
- package/src/pware.oc.runtime/pware.oc.runtime.mywork-enrich.ts +87 -79
- package/src/pware.oc.runtime/pware.oc.runtime.mywork.ts +282 -253
- package/src/pware.oc.runtime/pware.oc.runtime.omoRead.ts +37 -0
- package/src/pware.oc.runtime/pware.oc.runtime.questions.ts +83 -95
- package/src/pware.oc.runtime/pware.oc.runtime.snapshotClient.ts +130 -129
- package/src/pware.oc.runtime/pware.oc.runtime.source.ts +105 -105
- package/src/pware.oc.runtime/pware.oc.runtime.worker.ts +44 -43
- package/src/pware.oc.runtime/resolver/index.ts +268 -224
- package/src/pware.oc.runtime/resolver/pware.oc.runtime.resolver.delegate.ts +146 -146
- package/src/pware.oc.ui/index.ts +12 -12
- package/src/pware.oc.ui/pware.oc.ui.chrome.tsx +381 -339
- package/src/pware.oc.ui/pware.oc.ui.glyphs.tsx +144 -144
- package/src/pware.oc.ui/pware.oc.ui.host.tsx +111 -111
- package/src/pware.oc.ui/pware.oc.ui.live.tsx +82 -82
- package/src/pware.oc.ui/pware.oc.ui.menudialogs.tsx +843 -843
- package/src/pware.oc.ui/pware.oc.ui.sections.tsx +552 -526
- package/src/pware.oc.ui/pware.oc.ui.sidebar.tsx +1718 -1629
- package/src/pware.oc.ui.tsx +98 -98
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.omo.resolver.notepadsFile
|
|
3
|
-
*
|
|
4
|
-
* Notepad-file → writer-session index. Thin re-export of the shared omo file
|
|
5
|
-
* engine (`planFile.ts`), pinned to the `.omo/notepads/` document kind.
|
|
6
|
-
*/
|
|
7
|
-
import { DOC_KIND_NOTEPAD } from "../constants/pware.oc.omo.constants.docKind.js"
|
|
8
|
-
import {
|
|
9
|
-
makeOmoFileList,
|
|
10
|
-
makeOmoFileResolver,
|
|
11
|
-
} from "./pware.oc.omo.resolver.planFile.js"
|
|
12
|
-
|
|
13
|
-
/** Bidirectional notepad-file index for one project (see `omoFileIndex`). */
|
|
14
|
-
export const { sessionIndex: notepadsSessionIndex, sessionForFile: sessionForNotepadFile } =
|
|
15
|
-
makeOmoFileResolver("notepad")
|
|
16
|
-
|
|
17
|
-
/** Notepad files under `.omo/notepads/`, optionally filtered by writer session. */
|
|
18
|
-
export const NotepadFile = makeOmoFileList(DOC_KIND_NOTEPAD)
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.omo.resolver.notepadsFile
|
|
3
|
+
*
|
|
4
|
+
* Notepad-file → writer-session index. Thin re-export of the shared omo file
|
|
5
|
+
* engine (`planFile.ts`), pinned to the `.omo/notepads/` document kind.
|
|
6
|
+
*/
|
|
7
|
+
import { DOC_KIND_NOTEPAD } from "../constants/pware.oc.omo.constants.docKind.js"
|
|
8
|
+
import {
|
|
9
|
+
makeOmoFileList,
|
|
10
|
+
makeOmoFileResolver,
|
|
11
|
+
} from "./pware.oc.omo.resolver.planFile.js"
|
|
12
|
+
|
|
13
|
+
/** Bidirectional notepad-file index for one project (see `omoFileIndex`). */
|
|
14
|
+
export const { sessionIndex: notepadsSessionIndex, sessionForFile: sessionForNotepadFile } =
|
|
15
|
+
makeOmoFileResolver("notepad")
|
|
16
|
+
|
|
17
|
+
/** Notepad files under `.omo/notepads/`, optionally filtered by writer session. */
|
|
18
|
+
export const NotepadFile = makeOmoFileList(DOC_KIND_NOTEPAD)
|
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.core.omo.resolver.plan
|
|
3
|
-
*
|
|
4
|
-
* Plan.md parsing: status / slug / pending-action from YAML frontmatter or the
|
|
5
|
-
* `## State` section, plus the approval row label. Pure text — no fs, no scan.
|
|
6
|
-
*/
|
|
7
|
-
import path from "node:path"
|
|
8
|
-
import type { WorkState } from "../constants/pware.oc.omo.constants.planStatus.js"
|
|
9
|
-
|
|
10
|
-
export type ApprovalItem = {
|
|
11
|
-
/** Project-relative path. The panel must never show a root. */
|
|
12
|
-
rel: string
|
|
13
|
-
name: string
|
|
14
|
-
/** Frontmatter `status:`; null when the draft has none (a bare working note). */
|
|
15
|
-
status: string | null
|
|
16
|
-
/** What happens after approval — e.g. `write .omo/plans/<slug>.md`. */
|
|
17
|
-
pendingAction: string | null
|
|
18
|
-
updatedAt: number | null
|
|
19
|
-
/** ulw-plan review lifecycle from the draft frontmatter; null = no review requested. */
|
|
20
|
-
review: ReviewState | null
|
|
21
|
-
/** Boulder work completion state, for the My-work reconciliation. */
|
|
22
|
-
workState: WorkState
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** One ulw-plan review lane (momus / independent). */
|
|
26
|
-
export type ReviewLane = {
|
|
27
|
-
/** Lane lifecycle: pending | launching | in_flight | approved | changes_requested | inconclusive. */
|
|
28
|
-
status: string | null
|
|
29
|
-
/** Lane verdict, when the lane reported one (pass / fail / approved / blocked / …). */
|
|
30
|
-
result: string | null
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The ulw-plan review state persisted in the draft/plan frontmatter: one live
|
|
35
|
-
* round keyed by `review_round_id` + `plan_sha256` (any plan change bumps the
|
|
36
|
-
* hash and starts a fresh round), with a `momus` and an `independent` lane.
|
|
37
|
-
*/
|
|
38
|
-
export type ReviewState = {
|
|
39
|
-
required: boolean
|
|
40
|
-
/** `review_round_id` — the live round; a changed `plan_sha256` forces a new one. */
|
|
41
|
-
roundId: string | null
|
|
42
|
-
/** `round_status` — `active` while a round is live. */
|
|
43
|
-
roundStatus: string | null
|
|
44
|
-
/** `plan_sha256` — the reviewed plan's hash, null until a round is initialized. */
|
|
45
|
-
planSha256: string | null
|
|
46
|
-
lanes: { momus: ReviewLane; independent: ReviewLane }
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Row label for an approval item — basename without the `.md` extension. */
|
|
50
|
-
export function approvalName(rel: string): string {
|
|
51
|
-
return path.basename(rel).replace(/\.md$/i, "")
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function frontmatter(text: string): string | null {
|
|
55
|
-
const m = text.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/)
|
|
56
|
-
return m ? (m[1] ?? "") : null
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function stateSection(text: string): string | null {
|
|
60
|
-
const m = text.match(/##\s+State\s*\r?\n([\s\S]*?)(?=\r?\n##\s|$)/)
|
|
61
|
-
return m ? (m[1] ?? "") : null
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function parsePlanStatus(text: string): string | null {
|
|
65
|
-
const fm = frontmatter(text)
|
|
66
|
-
if (fm) {
|
|
67
|
-
const m = fm.match(/^status:\s*["']?([^"'\r\n]+)["']?/m)
|
|
68
|
-
if (m?.[1]) return m[1].trim()
|
|
69
|
-
}
|
|
70
|
-
const state = stateSection(text)
|
|
71
|
-
if (state) {
|
|
72
|
-
const m = state.match(/status:\s*[`'"]?\s*([^\s`'"\r\n]+)/m)
|
|
73
|
-
if (m?.[1]) return m[1].trim()
|
|
74
|
-
}
|
|
75
|
-
return null
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function parsePlanSlug(text: string): string | null {
|
|
79
|
-
const fm = frontmatter(text)
|
|
80
|
-
if (fm) {
|
|
81
|
-
const m = fm.match(/^slug:\s*["']?([^"'\r\n]+)["']?/m)
|
|
82
|
-
if (m?.[1]) return m[1].trim()
|
|
83
|
-
}
|
|
84
|
-
const state = stateSection(text)
|
|
85
|
-
if (state) {
|
|
86
|
-
const m = state.match(/slug:\s*[`'"]?\s*([^\s`'"\r\n]+)/m)
|
|
87
|
-
if (m?.[1]) return m[1].trim()
|
|
88
|
-
}
|
|
89
|
-
return null
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function parsePlanPendingAction(text: string): string | null {
|
|
93
|
-
const fm = frontmatter(text)
|
|
94
|
-
if (fm) {
|
|
95
|
-
const m = fm.match(/^pending-action:\s*["']?([^"'\r\n]+)["']?/m)
|
|
96
|
-
if (m?.[1]) return m[1].trim()
|
|
97
|
-
}
|
|
98
|
-
const slug = parsePlanSlug(text)
|
|
99
|
-
return slug ? `.omo/plans/${slug}.md` : null
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function scalar(fm: string, key: string): string | null {
|
|
103
|
-
const v = fm.match(new RegExp(`^${key}:\\s*(\\S+)`, "m"))?.[1]
|
|
104
|
-
if (!v || v === "null") return null
|
|
105
|
-
return v.trim()
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function stripQuotes(v: string): string {
|
|
109
|
-
return v.replace(/^["']|["']$/g, "")
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* One review lane (`momus:` / `independent:`) from the frontmatter lines:
|
|
114
|
-
* the lane's own `status` and `result`, stopping at the next top-level key.
|
|
115
|
-
* Line-based so the last frontmatter line needs no trailing newline.
|
|
116
|
-
*/
|
|
117
|
-
function laneState(fm: string, lane: "momus" | "independent"): ReviewLane | null {
|
|
118
|
-
const lines = fm.split(/\r?\n/)
|
|
119
|
-
const idx = lines.findIndex((l) => l.trim() === `${lane}:`)
|
|
120
|
-
if (idx < 0) return null
|
|
121
|
-
const laneIndent = lines[idx]!.match(/^\s*/)?.[0].length ?? 0
|
|
122
|
-
const out: ReviewLane = { status: null, result: null }
|
|
123
|
-
for (const line of lines.slice(idx + 1)) {
|
|
124
|
-
if ((line.match(/^\s*/)?.[0].length ?? 0) <= laneIndent) break
|
|
125
|
-
const m = line.match(/^\s*(\w[\w-]*):\s*(\S+)?/)
|
|
126
|
-
if (!m) continue
|
|
127
|
-
const val = m[2] && m[2] !== "null" ? stripQuotes(m[2]) : null
|
|
128
|
-
if (m[1] === "status") out.status = val
|
|
129
|
-
else if (m[1] === "result") out.result = val
|
|
130
|
-
}
|
|
131
|
-
return out
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* ulw-plan review lifecycle from the frontmatter `review:` block and its
|
|
136
|
-
* siblings (`review_required`, `review_round_id`, `round_status`,
|
|
137
|
-
* `plan_sha256`). Null when the draft carries no review fields at all.
|
|
138
|
-
*/
|
|
139
|
-
export function parseReviewBlock(text: string): ReviewState | null {
|
|
140
|
-
const fm = frontmatter(text)
|
|
141
|
-
if (!fm) return null
|
|
142
|
-
const required = /^review_required:\s*true/m.test(fm)
|
|
143
|
-
const hasBlock = /^review:\s*$/m.test(fm)
|
|
144
|
-
if (!required && !hasBlock) return null
|
|
145
|
-
return {
|
|
146
|
-
required,
|
|
147
|
-
roundId: scalar(fm, "review_round_id"),
|
|
148
|
-
roundStatus: scalar(fm, "round_status"),
|
|
149
|
-
planSha256: scalar(fm, "plan_sha256"),
|
|
150
|
-
lanes: {
|
|
151
|
-
momus: laneState(fm, "momus") ?? { status: null, result: null },
|
|
152
|
-
independent: laneState(fm, "independent") ?? { status: null, result: null },
|
|
153
|
-
},
|
|
154
|
-
}
|
|
155
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.core.omo.resolver.plan
|
|
3
|
+
*
|
|
4
|
+
* Plan.md parsing: status / slug / pending-action from YAML frontmatter or the
|
|
5
|
+
* `## State` section, plus the approval row label. Pure text — no fs, no scan.
|
|
6
|
+
*/
|
|
7
|
+
import path from "node:path"
|
|
8
|
+
import type { WorkState } from "../constants/pware.oc.omo.constants.planStatus.js"
|
|
9
|
+
|
|
10
|
+
export type ApprovalItem = {
|
|
11
|
+
/** Project-relative path. The panel must never show a root. */
|
|
12
|
+
rel: string
|
|
13
|
+
name: string
|
|
14
|
+
/** Frontmatter `status:`; null when the draft has none (a bare working note). */
|
|
15
|
+
status: string | null
|
|
16
|
+
/** What happens after approval — e.g. `write .omo/plans/<slug>.md`. */
|
|
17
|
+
pendingAction: string | null
|
|
18
|
+
updatedAt: number | null
|
|
19
|
+
/** ulw-plan review lifecycle from the draft frontmatter; null = no review requested. */
|
|
20
|
+
review: ReviewState | null
|
|
21
|
+
/** Boulder work completion state, for the My-work reconciliation. */
|
|
22
|
+
workState: WorkState
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** One ulw-plan review lane (momus / independent). */
|
|
26
|
+
export type ReviewLane = {
|
|
27
|
+
/** Lane lifecycle: pending | launching | in_flight | approved | changes_requested | inconclusive. */
|
|
28
|
+
status: string | null
|
|
29
|
+
/** Lane verdict, when the lane reported one (pass / fail / approved / blocked / …). */
|
|
30
|
+
result: string | null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The ulw-plan review state persisted in the draft/plan frontmatter: one live
|
|
35
|
+
* round keyed by `review_round_id` + `plan_sha256` (any plan change bumps the
|
|
36
|
+
* hash and starts a fresh round), with a `momus` and an `independent` lane.
|
|
37
|
+
*/
|
|
38
|
+
export type ReviewState = {
|
|
39
|
+
required: boolean
|
|
40
|
+
/** `review_round_id` — the live round; a changed `plan_sha256` forces a new one. */
|
|
41
|
+
roundId: string | null
|
|
42
|
+
/** `round_status` — `active` while a round is live. */
|
|
43
|
+
roundStatus: string | null
|
|
44
|
+
/** `plan_sha256` — the reviewed plan's hash, null until a round is initialized. */
|
|
45
|
+
planSha256: string | null
|
|
46
|
+
lanes: { momus: ReviewLane; independent: ReviewLane }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Row label for an approval item — basename without the `.md` extension. */
|
|
50
|
+
export function approvalName(rel: string): string {
|
|
51
|
+
return path.basename(rel).replace(/\.md$/i, "")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function frontmatter(text: string): string | null {
|
|
55
|
+
const m = text.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/)
|
|
56
|
+
return m ? (m[1] ?? "") : null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function stateSection(text: string): string | null {
|
|
60
|
+
const m = text.match(/##\s+State\s*\r?\n([\s\S]*?)(?=\r?\n##\s|$)/)
|
|
61
|
+
return m ? (m[1] ?? "") : null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function parsePlanStatus(text: string): string | null {
|
|
65
|
+
const fm = frontmatter(text)
|
|
66
|
+
if (fm) {
|
|
67
|
+
const m = fm.match(/^status:\s*["']?([^"'\r\n]+)["']?/m)
|
|
68
|
+
if (m?.[1]) return m[1].trim()
|
|
69
|
+
}
|
|
70
|
+
const state = stateSection(text)
|
|
71
|
+
if (state) {
|
|
72
|
+
const m = state.match(/status:\s*[`'"]?\s*([^\s`'"\r\n]+)/m)
|
|
73
|
+
if (m?.[1]) return m[1].trim()
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parsePlanSlug(text: string): string | null {
|
|
79
|
+
const fm = frontmatter(text)
|
|
80
|
+
if (fm) {
|
|
81
|
+
const m = fm.match(/^slug:\s*["']?([^"'\r\n]+)["']?/m)
|
|
82
|
+
if (m?.[1]) return m[1].trim()
|
|
83
|
+
}
|
|
84
|
+
const state = stateSection(text)
|
|
85
|
+
if (state) {
|
|
86
|
+
const m = state.match(/slug:\s*[`'"]?\s*([^\s`'"\r\n]+)/m)
|
|
87
|
+
if (m?.[1]) return m[1].trim()
|
|
88
|
+
}
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function parsePlanPendingAction(text: string): string | null {
|
|
93
|
+
const fm = frontmatter(text)
|
|
94
|
+
if (fm) {
|
|
95
|
+
const m = fm.match(/^pending-action:\s*["']?([^"'\r\n]+)["']?/m)
|
|
96
|
+
if (m?.[1]) return m[1].trim()
|
|
97
|
+
}
|
|
98
|
+
const slug = parsePlanSlug(text)
|
|
99
|
+
return slug ? `.omo/plans/${slug}.md` : null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function scalar(fm: string, key: string): string | null {
|
|
103
|
+
const v = fm.match(new RegExp(`^${key}:\\s*(\\S+)`, "m"))?.[1]
|
|
104
|
+
if (!v || v === "null") return null
|
|
105
|
+
return v.trim()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function stripQuotes(v: string): string {
|
|
109
|
+
return v.replace(/^["']|["']$/g, "")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* One review lane (`momus:` / `independent:`) from the frontmatter lines:
|
|
114
|
+
* the lane's own `status` and `result`, stopping at the next top-level key.
|
|
115
|
+
* Line-based so the last frontmatter line needs no trailing newline.
|
|
116
|
+
*/
|
|
117
|
+
function laneState(fm: string, lane: "momus" | "independent"): ReviewLane | null {
|
|
118
|
+
const lines = fm.split(/\r?\n/)
|
|
119
|
+
const idx = lines.findIndex((l) => l.trim() === `${lane}:`)
|
|
120
|
+
if (idx < 0) return null
|
|
121
|
+
const laneIndent = lines[idx]!.match(/^\s*/)?.[0].length ?? 0
|
|
122
|
+
const out: ReviewLane = { status: null, result: null }
|
|
123
|
+
for (const line of lines.slice(idx + 1)) {
|
|
124
|
+
if ((line.match(/^\s*/)?.[0].length ?? 0) <= laneIndent) break
|
|
125
|
+
const m = line.match(/^\s*(\w[\w-]*):\s*(\S+)?/)
|
|
126
|
+
if (!m) continue
|
|
127
|
+
const val = m[2] && m[2] !== "null" ? stripQuotes(m[2]) : null
|
|
128
|
+
if (m[1] === "status") out.status = val
|
|
129
|
+
else if (m[1] === "result") out.result = val
|
|
130
|
+
}
|
|
131
|
+
return out
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* ulw-plan review lifecycle from the frontmatter `review:` block and its
|
|
136
|
+
* siblings (`review_required`, `review_round_id`, `round_status`,
|
|
137
|
+
* `plan_sha256`). Null when the draft carries no review fields at all.
|
|
138
|
+
*/
|
|
139
|
+
export function parseReviewBlock(text: string): ReviewState | null {
|
|
140
|
+
const fm = frontmatter(text)
|
|
141
|
+
if (!fm) return null
|
|
142
|
+
const required = /^review_required:\s*true/m.test(fm)
|
|
143
|
+
const hasBlock = /^review:\s*$/m.test(fm)
|
|
144
|
+
if (!required && !hasBlock) return null
|
|
145
|
+
return {
|
|
146
|
+
required,
|
|
147
|
+
roundId: scalar(fm, "review_round_id"),
|
|
148
|
+
roundStatus: scalar(fm, "round_status"),
|
|
149
|
+
planSha256: scalar(fm, "plan_sha256"),
|
|
150
|
+
lanes: {
|
|
151
|
+
momus: laneState(fm, "momus") ?? { status: null, result: null },
|
|
152
|
+
independent: laneState(fm, "independent") ?? { status: null, result: null },
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
}
|