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,179 +1,179 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.core.omo.resolver.approval
|
|
3
|
-
*
|
|
4
|
-
* OMO plan approval queue — drafts/plans under `.omo/` (or legacy `.sisyphus/`)
|
|
5
|
-
* whose `status` says they are waiting for the user's sign-off. File discovery
|
|
6
|
-
* and stat are shared with the docs index: both this module and `doc.ts` read
|
|
7
|
-
* one per-root omo scan cache, so `.omo/` is walked once per TTL no matter how
|
|
8
|
-
* many consumers ask. Classification then reads each plan/draft file's
|
|
9
|
-
* frontmatter; the result is memoized against the same scan record and is
|
|
10
|
-
* recomputed only when that record turns over. A missing `.omo/` is an empty
|
|
11
|
-
* list, not an error.
|
|
12
|
-
*/
|
|
13
|
-
import fs from "node:fs"
|
|
14
|
-
import path from "node:path"
|
|
15
|
-
import { profile } from "../../pware.oc.core/pware.oc.core.debug.js"
|
|
16
|
-
import { canonicalizePath } from "../../pware.oc.core/pware.oc.core.paths.js"
|
|
17
|
-
import {
|
|
18
|
-
MY_WORK_GROUP_DRAFTING,
|
|
19
|
-
MY_WORK_GROUP_FINISHED,
|
|
20
|
-
MY_WORK_GROUP_READY_REVIEW,
|
|
21
|
-
MY_WORK_GROUP_READY_START,
|
|
22
|
-
} from "../../pware.oc.core/constants/pware.oc.core.constants.myWork.js"
|
|
23
|
-
import { resolveApprovalGroup } from "./pware.oc.omo.resolver.approvalGroup.js"
|
|
24
|
-
import { planWorkStateByPlanName } from "./pware.oc.omo.resolver.boulder.js"
|
|
25
|
-
import {
|
|
26
|
-
approvalName,
|
|
27
|
-
parsePlanPendingAction,
|
|
28
|
-
parsePlanStatus,
|
|
29
|
-
parseReviewBlock,
|
|
30
|
-
type ApprovalItem,
|
|
31
|
-
} from "./pware.oc.omo.resolver.plan.js"
|
|
32
|
-
import { DOC_KIND_DRAFT, DOC_KIND_PLAN } from "../constants/pware.oc.omo.constants.docKind.js"
|
|
33
|
-
import {
|
|
34
|
-
omoKindRows,
|
|
35
|
-
omoScanRecord,
|
|
36
|
-
resetDocsCache,
|
|
37
|
-
type OmoDocScan,
|
|
38
|
-
} from "./pware.oc.omo.resolver.doc.js"
|
|
39
|
-
import type { DocView } from "./pware.oc.omo.resolver.doc.js"
|
|
40
|
-
|
|
41
|
-
const MAX_ITEMS = 40
|
|
42
|
-
|
|
43
|
-
/** The "My work" approval buckets, keyed by group, in scan order. */
|
|
44
|
-
export type ScanResult = {
|
|
45
|
-
drafting: ApprovalItem[]
|
|
46
|
-
readyReview: ApprovalItem[]
|
|
47
|
-
readyStart: ApprovalItem[]
|
|
48
|
-
finished: ApprovalItem[]
|
|
49
|
-
/** Draft documents no action group covers — approved/done, unknown or no status. */
|
|
50
|
-
draftDocs: ApprovalItem[]
|
|
51
|
-
/** Plan documents no action group covers — unknown or no status. */
|
|
52
|
-
plans: ApprovalItem[]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function emptyScan(): ScanResult {
|
|
56
|
-
return {
|
|
57
|
-
drafting: [],
|
|
58
|
-
readyReview: [],
|
|
59
|
-
readyStart: [],
|
|
60
|
-
finished: [],
|
|
61
|
-
draftDocs: [],
|
|
62
|
-
plans: [],
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function sortApprovals(items: ApprovalItem[]): ApprovalItem[] {
|
|
67
|
-
items.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0) || a.name.localeCompare(b.name))
|
|
68
|
-
return items.slice(0, MAX_ITEMS)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function isMarkdown(rel: string): boolean {
|
|
72
|
-
return rel.toLowerCase().endsWith(".md")
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Drafts/plans split into the four "My work" buckets by status + file type. */
|
|
76
|
-
function classify(
|
|
77
|
-
root: string,
|
|
78
|
-
draftRows: readonly DocView[],
|
|
79
|
-
planRows: readonly DocView[],
|
|
80
|
-
): ScanResult {
|
|
81
|
-
const drafting: ApprovalItem[] = []
|
|
82
|
-
const readyReview: ApprovalItem[] = []
|
|
83
|
-
const readyStart: ApprovalItem[] = []
|
|
84
|
-
const finished: ApprovalItem[] = []
|
|
85
|
-
const draftDocs: ApprovalItem[] = []
|
|
86
|
-
const plans: ApprovalItem[] = []
|
|
87
|
-
const seen = new Set<string>()
|
|
88
|
-
const workStates = planWorkStateByPlanName(root)
|
|
89
|
-
const visit = (rows: readonly DocView[], isDraft: boolean) => {
|
|
90
|
-
for (const row of rows) {
|
|
91
|
-
const rel = row.rel
|
|
92
|
-
if (!isMarkdown(rel) || seen.has(rel)) continue
|
|
93
|
-
let text = ""
|
|
94
|
-
try {
|
|
95
|
-
text = fs.readFileSync(path.join(root, rel), "utf8")
|
|
96
|
-
} catch {
|
|
97
|
-
continue
|
|
98
|
-
}
|
|
99
|
-
const status = parsePlanStatus(text)
|
|
100
|
-
const name = approvalName(rel)
|
|
101
|
-
const workState = workStates.get(name) ?? "absent"
|
|
102
|
-
// A plan with no parseable status still reconciles against boulder: one
|
|
103
|
-
// whose boulder work completed lands in Finished (resolveApprovalGroup →
|
|
104
|
-
// planWorkDone returns true for a completed work state regardless of
|
|
105
|
-
// status). A no-status plan with no completed work falls through to the
|
|
106
|
-
// Plans archive below; a no-status draft is a working document — Draft docs.
|
|
107
|
-
const group = resolveApprovalGroup(status, isDraft, workState, false)
|
|
108
|
-
const item: ApprovalItem = {
|
|
109
|
-
rel,
|
|
110
|
-
name,
|
|
111
|
-
status,
|
|
112
|
-
pendingAction: parsePlanPendingAction(text),
|
|
113
|
-
updatedAt: row.updatedAt,
|
|
114
|
-
review: parseReviewBlock(text),
|
|
115
|
-
workState,
|
|
116
|
-
}
|
|
117
|
-
seen.add(rel)
|
|
118
|
-
if (!group) {
|
|
119
|
-
// A document no action group covers (superseded approved/done, unknown
|
|
120
|
-
// or no status) is browsable, not a queue item: drafts → Draft docs,
|
|
121
|
-
// plans → Plans.
|
|
122
|
-
if (isDraft) draftDocs.push(item)
|
|
123
|
-
else plans.push(item)
|
|
124
|
-
continue
|
|
125
|
-
}
|
|
126
|
-
switch (group) {
|
|
127
|
-
case MY_WORK_GROUP_DRAFTING:
|
|
128
|
-
drafting.push(item)
|
|
129
|
-
break
|
|
130
|
-
case MY_WORK_GROUP_READY_REVIEW:
|
|
131
|
-
readyReview.push(item)
|
|
132
|
-
break
|
|
133
|
-
case MY_WORK_GROUP_READY_START:
|
|
134
|
-
readyStart.push(item)
|
|
135
|
-
break
|
|
136
|
-
case MY_WORK_GROUP_FINISHED:
|
|
137
|
-
finished.push(item)
|
|
138
|
-
break
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
visit(draftRows, true)
|
|
143
|
-
visit(planRows, false)
|
|
144
|
-
return {
|
|
145
|
-
drafting: sortApprovals(drafting),
|
|
146
|
-
readyReview: sortApprovals(readyReview),
|
|
147
|
-
readyStart: sortApprovals(readyStart),
|
|
148
|
-
finished: sortApprovals(finished),
|
|
149
|
-
draftDocs: sortApprovals(draftDocs),
|
|
150
|
-
plans: sortApprovals(plans),
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Approval buckets memoized against the shared per-root omo scan record. */
|
|
155
|
-
const approvalMemo = new WeakMap<OmoDocScan, ScanResult>()
|
|
156
|
-
|
|
157
|
-
/** Drop the shared omo scan cache — approvals and docs both re-read next call. */
|
|
158
|
-
export function resetApprovalsCache(): void {
|
|
159
|
-
resetDocsCache()
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Drafts/plans split into the four "My work" buckets by status + file type. */
|
|
163
|
-
export function listApprovals(projectRoot: string | null | undefined): ScanResult {
|
|
164
|
-
if (!projectRoot) return emptyScan()
|
|
165
|
-
const root = canonicalizePath(projectRoot)
|
|
166
|
-
const scan = omoScanRecord(root)
|
|
167
|
-
if (!scan) return emptyScan()
|
|
168
|
-
const memoized = approvalMemo.get(scan)
|
|
169
|
-
if (memoized) return memoized
|
|
170
|
-
const result = profile("omo.approvals", () =>
|
|
171
|
-
classify(
|
|
172
|
-
root,
|
|
173
|
-
omoKindRows(DOC_KIND_DRAFT, root),
|
|
174
|
-
omoKindRows(DOC_KIND_PLAN, root),
|
|
175
|
-
),
|
|
176
|
-
)
|
|
177
|
-
approvalMemo.set(scan, result)
|
|
178
|
-
return result
|
|
179
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.core.omo.resolver.approval
|
|
3
|
+
*
|
|
4
|
+
* OMO plan approval queue — drafts/plans under `.omo/` (or legacy `.sisyphus/`)
|
|
5
|
+
* whose `status` says they are waiting for the user's sign-off. File discovery
|
|
6
|
+
* and stat are shared with the docs index: both this module and `doc.ts` read
|
|
7
|
+
* one per-root omo scan cache, so `.omo/` is walked once per TTL no matter how
|
|
8
|
+
* many consumers ask. Classification then reads each plan/draft file's
|
|
9
|
+
* frontmatter; the result is memoized against the same scan record and is
|
|
10
|
+
* recomputed only when that record turns over. A missing `.omo/` is an empty
|
|
11
|
+
* list, not an error.
|
|
12
|
+
*/
|
|
13
|
+
import fs from "node:fs"
|
|
14
|
+
import path from "node:path"
|
|
15
|
+
import { profile } from "../../pware.oc.core/pware.oc.core.debug.js"
|
|
16
|
+
import { canonicalizePath } from "../../pware.oc.core/pware.oc.core.paths.js"
|
|
17
|
+
import {
|
|
18
|
+
MY_WORK_GROUP_DRAFTING,
|
|
19
|
+
MY_WORK_GROUP_FINISHED,
|
|
20
|
+
MY_WORK_GROUP_READY_REVIEW,
|
|
21
|
+
MY_WORK_GROUP_READY_START,
|
|
22
|
+
} from "../../pware.oc.core/constants/pware.oc.core.constants.myWork.js"
|
|
23
|
+
import { resolveApprovalGroup } from "./pware.oc.omo.resolver.approvalGroup.js"
|
|
24
|
+
import { planWorkStateByPlanName } from "./pware.oc.omo.resolver.boulder.js"
|
|
25
|
+
import {
|
|
26
|
+
approvalName,
|
|
27
|
+
parsePlanPendingAction,
|
|
28
|
+
parsePlanStatus,
|
|
29
|
+
parseReviewBlock,
|
|
30
|
+
type ApprovalItem,
|
|
31
|
+
} from "./pware.oc.omo.resolver.plan.js"
|
|
32
|
+
import { DOC_KIND_DRAFT, DOC_KIND_PLAN } from "../constants/pware.oc.omo.constants.docKind.js"
|
|
33
|
+
import {
|
|
34
|
+
omoKindRows,
|
|
35
|
+
omoScanRecord,
|
|
36
|
+
resetDocsCache,
|
|
37
|
+
type OmoDocScan,
|
|
38
|
+
} from "./pware.oc.omo.resolver.doc.js"
|
|
39
|
+
import type { DocView } from "./pware.oc.omo.resolver.doc.js"
|
|
40
|
+
|
|
41
|
+
const MAX_ITEMS = 40
|
|
42
|
+
|
|
43
|
+
/** The "My work" approval buckets, keyed by group, in scan order. */
|
|
44
|
+
export type ScanResult = {
|
|
45
|
+
drafting: ApprovalItem[]
|
|
46
|
+
readyReview: ApprovalItem[]
|
|
47
|
+
readyStart: ApprovalItem[]
|
|
48
|
+
finished: ApprovalItem[]
|
|
49
|
+
/** Draft documents no action group covers — approved/done, unknown or no status. */
|
|
50
|
+
draftDocs: ApprovalItem[]
|
|
51
|
+
/** Plan documents no action group covers — unknown or no status. */
|
|
52
|
+
plans: ApprovalItem[]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function emptyScan(): ScanResult {
|
|
56
|
+
return {
|
|
57
|
+
drafting: [],
|
|
58
|
+
readyReview: [],
|
|
59
|
+
readyStart: [],
|
|
60
|
+
finished: [],
|
|
61
|
+
draftDocs: [],
|
|
62
|
+
plans: [],
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sortApprovals(items: ApprovalItem[]): ApprovalItem[] {
|
|
67
|
+
items.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0) || a.name.localeCompare(b.name))
|
|
68
|
+
return items.slice(0, MAX_ITEMS)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isMarkdown(rel: string): boolean {
|
|
72
|
+
return rel.toLowerCase().endsWith(".md")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Drafts/plans split into the four "My work" buckets by status + file type. */
|
|
76
|
+
function classify(
|
|
77
|
+
root: string,
|
|
78
|
+
draftRows: readonly DocView[],
|
|
79
|
+
planRows: readonly DocView[],
|
|
80
|
+
): ScanResult {
|
|
81
|
+
const drafting: ApprovalItem[] = []
|
|
82
|
+
const readyReview: ApprovalItem[] = []
|
|
83
|
+
const readyStart: ApprovalItem[] = []
|
|
84
|
+
const finished: ApprovalItem[] = []
|
|
85
|
+
const draftDocs: ApprovalItem[] = []
|
|
86
|
+
const plans: ApprovalItem[] = []
|
|
87
|
+
const seen = new Set<string>()
|
|
88
|
+
const workStates = planWorkStateByPlanName(root)
|
|
89
|
+
const visit = (rows: readonly DocView[], isDraft: boolean) => {
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
const rel = row.rel
|
|
92
|
+
if (!isMarkdown(rel) || seen.has(rel)) continue
|
|
93
|
+
let text = ""
|
|
94
|
+
try {
|
|
95
|
+
text = fs.readFileSync(path.join(root, rel), "utf8")
|
|
96
|
+
} catch {
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
const status = parsePlanStatus(text)
|
|
100
|
+
const name = approvalName(rel)
|
|
101
|
+
const workState = workStates.get(name) ?? "absent"
|
|
102
|
+
// A plan with no parseable status still reconciles against boulder: one
|
|
103
|
+
// whose boulder work completed lands in Finished (resolveApprovalGroup →
|
|
104
|
+
// planWorkDone returns true for a completed work state regardless of
|
|
105
|
+
// status). A no-status plan with no completed work falls through to the
|
|
106
|
+
// Plans archive below; a no-status draft is a working document — Draft docs.
|
|
107
|
+
const group = resolveApprovalGroup(status, isDraft, workState, false)
|
|
108
|
+
const item: ApprovalItem = {
|
|
109
|
+
rel,
|
|
110
|
+
name,
|
|
111
|
+
status,
|
|
112
|
+
pendingAction: parsePlanPendingAction(text),
|
|
113
|
+
updatedAt: row.updatedAt,
|
|
114
|
+
review: parseReviewBlock(text),
|
|
115
|
+
workState,
|
|
116
|
+
}
|
|
117
|
+
seen.add(rel)
|
|
118
|
+
if (!group) {
|
|
119
|
+
// A document no action group covers (superseded approved/done, unknown
|
|
120
|
+
// or no status) is browsable, not a queue item: drafts → Draft docs,
|
|
121
|
+
// plans → Plans.
|
|
122
|
+
if (isDraft) draftDocs.push(item)
|
|
123
|
+
else plans.push(item)
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
switch (group) {
|
|
127
|
+
case MY_WORK_GROUP_DRAFTING:
|
|
128
|
+
drafting.push(item)
|
|
129
|
+
break
|
|
130
|
+
case MY_WORK_GROUP_READY_REVIEW:
|
|
131
|
+
readyReview.push(item)
|
|
132
|
+
break
|
|
133
|
+
case MY_WORK_GROUP_READY_START:
|
|
134
|
+
readyStart.push(item)
|
|
135
|
+
break
|
|
136
|
+
case MY_WORK_GROUP_FINISHED:
|
|
137
|
+
finished.push(item)
|
|
138
|
+
break
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
visit(draftRows, true)
|
|
143
|
+
visit(planRows, false)
|
|
144
|
+
return {
|
|
145
|
+
drafting: sortApprovals(drafting),
|
|
146
|
+
readyReview: sortApprovals(readyReview),
|
|
147
|
+
readyStart: sortApprovals(readyStart),
|
|
148
|
+
finished: sortApprovals(finished),
|
|
149
|
+
draftDocs: sortApprovals(draftDocs),
|
|
150
|
+
plans: sortApprovals(plans),
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Approval buckets memoized against the shared per-root omo scan record. */
|
|
155
|
+
const approvalMemo = new WeakMap<OmoDocScan, ScanResult>()
|
|
156
|
+
|
|
157
|
+
/** Drop the shared omo scan cache — approvals and docs both re-read next call. */
|
|
158
|
+
export function resetApprovalsCache(): void {
|
|
159
|
+
resetDocsCache()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Drafts/plans split into the four "My work" buckets by status + file type. */
|
|
163
|
+
export function listApprovals(projectRoot: string | null | undefined): ScanResult {
|
|
164
|
+
if (!projectRoot) return emptyScan()
|
|
165
|
+
const root = canonicalizePath(projectRoot)
|
|
166
|
+
const scan = omoScanRecord(root)
|
|
167
|
+
if (!scan) return emptyScan()
|
|
168
|
+
const memoized = approvalMemo.get(scan)
|
|
169
|
+
if (memoized) return memoized
|
|
170
|
+
const result = profile("omo.approvals", () =>
|
|
171
|
+
classify(
|
|
172
|
+
root,
|
|
173
|
+
omoKindRows(DOC_KIND_DRAFT, root),
|
|
174
|
+
omoKindRows(DOC_KIND_PLAN, root),
|
|
175
|
+
),
|
|
176
|
+
)
|
|
177
|
+
approvalMemo.set(scan, result)
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.omo.resolver.approvalGroup
|
|
3
|
-
*
|
|
4
|
-
* Which "My work" approval group a plan belongs to, from its OMO plan status
|
|
5
|
-
* alone, plus whether a plan path is still a draft. Lives in omo (not core)
|
|
6
|
-
* because it consumes the OMO plan-status constants; core may not import them.
|
|
7
|
-
*/
|
|
8
|
-
import {
|
|
9
|
-
MY_WORK_GROUP_DRAFTING,
|
|
10
|
-
MY_WORK_GROUP_FINISHED,
|
|
11
|
-
MY_WORK_GROUP_READY_REVIEW,
|
|
12
|
-
MY_WORK_GROUP_READY_START,
|
|
13
|
-
type ApprovalGroupKind,
|
|
14
|
-
} from "../../pware.oc.core/constants/pware.oc.core.constants.myWork.js"
|
|
15
|
-
import {
|
|
16
|
-
PLAN_PENDING_STATUSES,
|
|
17
|
-
PLAN_STATUS_APPROVED,
|
|
18
|
-
PLAN_STATUS_DONE,
|
|
19
|
-
PLAN_STATUS_DRAFTING,
|
|
20
|
-
WORK_STATE_ABSENT,
|
|
21
|
-
WORK_STATE_COMPLETED,
|
|
22
|
-
type WorkState,
|
|
23
|
-
} from "../../pware.oc.omo/constants/pware.oc.omo.constants.planStatus.js"
|
|
24
|
-
|
|
25
|
-
/** The pending sign-off set widened so `.includes()` accepts any status string. */
|
|
26
|
-
const PENDING_STATUSES: readonly string[] = PLAN_PENDING_STATUSES
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Which approval group a plan belongs to, from its OMO status alone:
|
|
30
|
-
* `drafting` while still a draft, `ready-to-review` for a pending status,
|
|
31
|
-
* `ready-to-start` for an approved plan, `finished` for a done plan. Null when
|
|
32
|
-
* the item is superseded — an approved or done draft, or an unknown status —
|
|
33
|
-
* and must not be shown.
|
|
34
|
-
*/
|
|
35
|
-
export function approvalGroup(
|
|
36
|
-
status: string | null,
|
|
37
|
-
isDraft: boolean,
|
|
38
|
-
): ApprovalGroupKind | null {
|
|
39
|
-
const s = (status ?? "").toLowerCase()
|
|
40
|
-
if (s === PLAN_STATUS_DRAFTING) return MY_WORK_GROUP_DRAFTING
|
|
41
|
-
if (PENDING_STATUSES.includes(s)) return MY_WORK_GROUP_READY_REVIEW
|
|
42
|
-
if (s === PLAN_STATUS_APPROVED) return isDraft ? null : MY_WORK_GROUP_READY_START
|
|
43
|
-
if (s === PLAN_STATUS_DONE) return isDraft ? null : MY_WORK_GROUP_FINISHED
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** True when a plan path points at a draft still being written, not a final plan. */
|
|
48
|
-
export function isDraftOf(rel: string): boolean {
|
|
49
|
-
return rel.startsWith("drafts/") || rel.startsWith(".sisyphus/drafts/")
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* True when we have trustworthy evidence the plan's work finished: a boulder
|
|
54
|
-
* work completed (layer 1), or — with no boulder work for this plan — the
|
|
55
|
-
* writer session's todos all completed (layer 2). Layer 2 only applies to an
|
|
56
|
-
* `approved` plan; a pending/drafting plan was never cleared to start, so its
|
|
57
|
-
* completed todos are the planner's, not the work's.
|
|
58
|
-
*/
|
|
59
|
-
export function planWorkDone(
|
|
60
|
-
status: string | null,
|
|
61
|
-
workState: WorkState,
|
|
62
|
-
todosDone: boolean,
|
|
63
|
-
): boolean {
|
|
64
|
-
const s = (status ?? "").toLowerCase()
|
|
65
|
-
if (workState === WORK_STATE_COMPLETED) return true
|
|
66
|
-
if (workState === WORK_STATE_ABSENT && todosDone && s === PLAN_STATUS_APPROVED) return true
|
|
67
|
-
return false
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* The effective My-work group, reconciling the (untrusted) frontmatter status
|
|
72
|
-
* against boulder + db evidence. Done work → finished regardless of the
|
|
73
|
-
* frontmatter (a draft stays superseded); otherwise it falls back to the pure
|
|
74
|
-
* `approvalGroup` mapping, so the plugin still works with no boulder and no db.
|
|
75
|
-
*/
|
|
76
|
-
export function resolveApprovalGroup(
|
|
77
|
-
status: string | null,
|
|
78
|
-
isDraft: boolean,
|
|
79
|
-
workState: WorkState,
|
|
80
|
-
todosDone: boolean,
|
|
81
|
-
): ApprovalGroupKind | null {
|
|
82
|
-
if (planWorkDone(status, workState, todosDone)) {
|
|
83
|
-
return isDraft ? null : MY_WORK_GROUP_FINISHED
|
|
84
|
-
}
|
|
85
|
-
return approvalGroup(status, isDraft)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** True when the group is the drafting bucket. */
|
|
89
|
-
export function isDrafting(g: ApprovalGroupKind | null): boolean {
|
|
90
|
-
return g === MY_WORK_GROUP_DRAFTING
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** True when the group is waiting for the user's sign-off. */
|
|
94
|
-
export function isReadyToReview(g: ApprovalGroupKind | null): boolean {
|
|
95
|
-
return g === MY_WORK_GROUP_READY_REVIEW
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Alias for isReadyToReview — a plan awaiting approval. */
|
|
99
|
-
export function isWaitingApproval(g: ApprovalGroupKind | null): boolean {
|
|
100
|
-
return g === MY_WORK_GROUP_READY_REVIEW
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Alias for isReadyToReview — a plan pending approval. */
|
|
104
|
-
export function isPendingApproval(g: ApprovalGroupKind | null): boolean {
|
|
105
|
-
return g === MY_WORK_GROUP_READY_REVIEW
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** True when the group is approved and ready to start. */
|
|
109
|
-
export function isReadyToStart(g: ApprovalGroupKind | null): boolean {
|
|
110
|
-
return g === MY_WORK_GROUP_READY_START
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** True when the group is finished. */
|
|
114
|
-
export function isFinished(g: ApprovalGroupKind | null): boolean {
|
|
115
|
-
return g === MY_WORK_GROUP_FINISHED
|
|
116
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.omo.resolver.approvalGroup
|
|
3
|
+
*
|
|
4
|
+
* Which "My work" approval group a plan belongs to, from its OMO plan status
|
|
5
|
+
* alone, plus whether a plan path is still a draft. Lives in omo (not core)
|
|
6
|
+
* because it consumes the OMO plan-status constants; core may not import them.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
MY_WORK_GROUP_DRAFTING,
|
|
10
|
+
MY_WORK_GROUP_FINISHED,
|
|
11
|
+
MY_WORK_GROUP_READY_REVIEW,
|
|
12
|
+
MY_WORK_GROUP_READY_START,
|
|
13
|
+
type ApprovalGroupKind,
|
|
14
|
+
} from "../../pware.oc.core/constants/pware.oc.core.constants.myWork.js"
|
|
15
|
+
import {
|
|
16
|
+
PLAN_PENDING_STATUSES,
|
|
17
|
+
PLAN_STATUS_APPROVED,
|
|
18
|
+
PLAN_STATUS_DONE,
|
|
19
|
+
PLAN_STATUS_DRAFTING,
|
|
20
|
+
WORK_STATE_ABSENT,
|
|
21
|
+
WORK_STATE_COMPLETED,
|
|
22
|
+
type WorkState,
|
|
23
|
+
} from "../../pware.oc.omo/constants/pware.oc.omo.constants.planStatus.js"
|
|
24
|
+
|
|
25
|
+
/** The pending sign-off set widened so `.includes()` accepts any status string. */
|
|
26
|
+
const PENDING_STATUSES: readonly string[] = PLAN_PENDING_STATUSES
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Which approval group a plan belongs to, from its OMO status alone:
|
|
30
|
+
* `drafting` while still a draft, `ready-to-review` for a pending status,
|
|
31
|
+
* `ready-to-start` for an approved plan, `finished` for a done plan. Null when
|
|
32
|
+
* the item is superseded — an approved or done draft, or an unknown status —
|
|
33
|
+
* and must not be shown.
|
|
34
|
+
*/
|
|
35
|
+
export function approvalGroup(
|
|
36
|
+
status: string | null,
|
|
37
|
+
isDraft: boolean,
|
|
38
|
+
): ApprovalGroupKind | null {
|
|
39
|
+
const s = (status ?? "").toLowerCase()
|
|
40
|
+
if (s === PLAN_STATUS_DRAFTING) return MY_WORK_GROUP_DRAFTING
|
|
41
|
+
if (PENDING_STATUSES.includes(s)) return MY_WORK_GROUP_READY_REVIEW
|
|
42
|
+
if (s === PLAN_STATUS_APPROVED) return isDraft ? null : MY_WORK_GROUP_READY_START
|
|
43
|
+
if (s === PLAN_STATUS_DONE) return isDraft ? null : MY_WORK_GROUP_FINISHED
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** True when a plan path points at a draft still being written, not a final plan. */
|
|
48
|
+
export function isDraftOf(rel: string): boolean {
|
|
49
|
+
return rel.startsWith("drafts/") || rel.startsWith(".sisyphus/drafts/")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* True when we have trustworthy evidence the plan's work finished: a boulder
|
|
54
|
+
* work completed (layer 1), or — with no boulder work for this plan — the
|
|
55
|
+
* writer session's todos all completed (layer 2). Layer 2 only applies to an
|
|
56
|
+
* `approved` plan; a pending/drafting plan was never cleared to start, so its
|
|
57
|
+
* completed todos are the planner's, not the work's.
|
|
58
|
+
*/
|
|
59
|
+
export function planWorkDone(
|
|
60
|
+
status: string | null,
|
|
61
|
+
workState: WorkState,
|
|
62
|
+
todosDone: boolean,
|
|
63
|
+
): boolean {
|
|
64
|
+
const s = (status ?? "").toLowerCase()
|
|
65
|
+
if (workState === WORK_STATE_COMPLETED) return true
|
|
66
|
+
if (workState === WORK_STATE_ABSENT && todosDone && s === PLAN_STATUS_APPROVED) return true
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The effective My-work group, reconciling the (untrusted) frontmatter status
|
|
72
|
+
* against boulder + db evidence. Done work → finished regardless of the
|
|
73
|
+
* frontmatter (a draft stays superseded); otherwise it falls back to the pure
|
|
74
|
+
* `approvalGroup` mapping, so the plugin still works with no boulder and no db.
|
|
75
|
+
*/
|
|
76
|
+
export function resolveApprovalGroup(
|
|
77
|
+
status: string | null,
|
|
78
|
+
isDraft: boolean,
|
|
79
|
+
workState: WorkState,
|
|
80
|
+
todosDone: boolean,
|
|
81
|
+
): ApprovalGroupKind | null {
|
|
82
|
+
if (planWorkDone(status, workState, todosDone)) {
|
|
83
|
+
return isDraft ? null : MY_WORK_GROUP_FINISHED
|
|
84
|
+
}
|
|
85
|
+
return approvalGroup(status, isDraft)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** True when the group is the drafting bucket. */
|
|
89
|
+
export function isDrafting(g: ApprovalGroupKind | null): boolean {
|
|
90
|
+
return g === MY_WORK_GROUP_DRAFTING
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** True when the group is waiting for the user's sign-off. */
|
|
94
|
+
export function isReadyToReview(g: ApprovalGroupKind | null): boolean {
|
|
95
|
+
return g === MY_WORK_GROUP_READY_REVIEW
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Alias for isReadyToReview — a plan awaiting approval. */
|
|
99
|
+
export function isWaitingApproval(g: ApprovalGroupKind | null): boolean {
|
|
100
|
+
return g === MY_WORK_GROUP_READY_REVIEW
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Alias for isReadyToReview — a plan pending approval. */
|
|
104
|
+
export function isPendingApproval(g: ApprovalGroupKind | null): boolean {
|
|
105
|
+
return g === MY_WORK_GROUP_READY_REVIEW
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** True when the group is approved and ready to start. */
|
|
109
|
+
export function isReadyToStart(g: ApprovalGroupKind | null): boolean {
|
|
110
|
+
return g === MY_WORK_GROUP_READY_START
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** True when the group is finished. */
|
|
114
|
+
export function isFinished(g: ApprovalGroupKind | null): boolean {
|
|
115
|
+
return g === MY_WORK_GROUP_FINISHED
|
|
116
|
+
}
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.omo.resolver.approvalstate
|
|
3
|
-
*
|
|
4
|
-
* Reads the planner session's `.omo/run-continuation` background-task marker.
|
|
5
|
-
* Lives in the omo layer — no sibling domain may read omo files. The runtime
|
|
6
|
-
* layer's my-work enrich module composes this marker with the session activity
|
|
7
|
-
* state.
|
|
8
|
-
*/
|
|
9
|
-
import path from "node:path"
|
|
10
|
-
import { firstOmoPath } from "./pware.oc.omo.resolver.boulder.js"
|
|
11
|
-
import { readJson } from "../../pware.oc.core/pware.oc.core.paths.js"
|
|
12
|
-
|
|
13
|
-
/** First existing `.omo`/`.sisyphus` run-continuation directory, or null. */
|
|
14
|
-
export function firstRunContinuationDir(projectRoot: string): string | null {
|
|
15
|
-
return firstOmoPath(projectRoot, "run-continuation")
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The `.omo/run-continuation` background-task state for a session, or null when
|
|
20
|
-
* absent/unreadable.
|
|
21
|
-
*/
|
|
22
|
-
export function readRunContinuationState(
|
|
23
|
-
projectRoot: string | null | undefined,
|
|
24
|
-
sessionId: string,
|
|
25
|
-
): string | null {
|
|
26
|
-
if (!projectRoot) return null
|
|
27
|
-
const dir = firstRunContinuationDir(projectRoot)
|
|
28
|
-
if (!dir) return null
|
|
29
|
-
const raw = readJson(path.join(dir, `${sessionId}.json`)) as {
|
|
30
|
-
sources?: { "background-task"?: { state?: unknown } }
|
|
31
|
-
} | null
|
|
32
|
-
const state = raw?.sources?.["background-task"]?.state
|
|
33
|
-
return typeof state === "string" ? state : null
|
|
34
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.omo.resolver.approvalstate
|
|
3
|
+
*
|
|
4
|
+
* Reads the planner session's `.omo/run-continuation` background-task marker.
|
|
5
|
+
* Lives in the omo layer — no sibling domain may read omo files. The runtime
|
|
6
|
+
* layer's my-work enrich module composes this marker with the session activity
|
|
7
|
+
* state.
|
|
8
|
+
*/
|
|
9
|
+
import path from "node:path"
|
|
10
|
+
import { firstOmoPath } from "./pware.oc.omo.resolver.boulder.js"
|
|
11
|
+
import { readJson } from "../../pware.oc.core/pware.oc.core.paths.js"
|
|
12
|
+
|
|
13
|
+
/** First existing `.omo`/`.sisyphus` run-continuation directory, or null. */
|
|
14
|
+
export function firstRunContinuationDir(projectRoot: string): string | null {
|
|
15
|
+
return firstOmoPath(projectRoot, "run-continuation")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The `.omo/run-continuation` background-task state for a session, or null when
|
|
20
|
+
* absent/unreadable.
|
|
21
|
+
*/
|
|
22
|
+
export function readRunContinuationState(
|
|
23
|
+
projectRoot: string | null | undefined,
|
|
24
|
+
sessionId: string,
|
|
25
|
+
): string | null {
|
|
26
|
+
if (!projectRoot) return null
|
|
27
|
+
const dir = firstRunContinuationDir(projectRoot)
|
|
28
|
+
if (!dir) return null
|
|
29
|
+
const raw = readJson(path.join(dir, `${sessionId}.json`)) as {
|
|
30
|
+
sources?: { "background-task"?: { state?: unknown } }
|
|
31
|
+
} | null
|
|
32
|
+
const state = raw?.sources?.["background-task"]?.state
|
|
33
|
+
return typeof state === "string" ? state : null
|
|
34
|
+
}
|