create-pmos 0.107.0 → 0.108.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/guide/notes.md +6 -4
- package/workspace/lib/notes.js +98 -1
- package/workspace/lib/routes-notes.js +32 -11
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/app.js +2 -2
- package/workspace/public/pm/components.css +315 -163
- package/workspace/public/pm/dest-notes.js +383 -0
- package/workspace/public/pm/frame-copy.js +1 -0
- package/workspace/public/pm/note-dock.js +68 -22
- package/workspace/public/pm/notes-links.js +3 -3
- package/workspace/public/pm/notes-task.js +36 -0
- package/workspace/public/pm/panel-cockpit.js +1 -2
- package/workspace/public/pm/registry.js +2 -0
- package/workspace/public/pm/topbar.js +42 -17
- package/workspace/public/pm/view-notes.js +120 -158
- package/workspace/public/vendor/lucide-sprite.js +8 -2
- package/workspace/test-files.json +3 -1
- package/workspace/third-party/README.md +1 -1
- package/workspace/public/pm/view-notes-menu.js +0 -317
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.108.0",
|
|
4
4
|
"description": "Scaffold the PMOS portable kit — run the PMOS product-management workflow (initiative → lane-fit spec → contract → build → three gates, with work-type lanes for features, research, maintenance, incidents, and content) entirely inside your own repo, with all state as git-versioned files. Includes `workflow-sweep`, which audits the state every board derives from and separates real defects from work you simply have not done. `create-pmos workspace` also installs + launches the local Terminal Workspace GUI. No database, no network, no telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Eval calibration log"
|
|
4
4
|
description: "Append-only record of every PM override of an evaluator verdict."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-08T03:03:28Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Health budgets"
|
|
4
4
|
description: "Closed registry of health-budget anchors (D59), edited only by the PM."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-08T03:03:28Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Discoveries and friction log"
|
|
4
4
|
description: "Append-only record of what runs discovered and where the process rubbed."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-08T03:03:28Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/guide/notes.md
CHANGED
|
@@ -7,12 +7,14 @@ timestamp: 2026-10-08
|
|
|
7
7
|
|
|
8
8
|
# Guide: notes
|
|
9
9
|
|
|
10
|
+
**Notes** is a page in the top bar, between Docs and People. The list on the left shows every note by initiative, with the time or day and an amber dot when a note conflicts with a file. The note you pick opens on the right: its title, the **Resume** button and the **⋯** menu, then the conflicts, the highlights, the overview, the decisions, the next steps (each with **Make task**), the open questions and a **Discussion** fold with the topics and the transcript. Press the title, the overview or any decision, next step or question to edit it. PMOS keeps the words the agent wrote. At the top right of the bar, **Record** starts a new note and listens at once. While it listens, the bar shows the red dot and the clock, a **Highlight** button and **Pause**. When it is paused, the bar shows **Resume**. Press the clock to open the note as a pop-up. Each note below is shown in the pop-up and on the page in the same way.
|
|
11
|
+
|
|
10
12
|
The Notes view shows one meeting, top to bottom. A bar at the top shows whether notes are being taken, with **Stop** or **Listen** and the **⋯** menu. Under it are the title, the day, the meeting, the initiative and the people. Then come the overview, the key points, the decisions, the next steps, the open questions and your own notes, each item with the meeting time it was said or written. The transcript folds away at the end. Notes are kept on this machine, outside every repository.
|
|
11
13
|
|
|
12
14
|
## Start a meeting
|
|
13
15
|
|
|
14
|
-
1. Press
|
|
15
|
-
2.
|
|
16
|
+
1. Press **Record** in the top bar, or press **Notes** to see all notes (or press ⌘K, type **notes** and press Enter). The page is the same in every repository.
|
|
17
|
+
2. Type a title. A note made with **Record** has none until you add one.
|
|
16
18
|
3. Under **Your notes**, type a note and press Enter. Each note is its own line, with the meeting time you added it at. To remove a line, press the **x** beside it. Each line also shows in the transcript as **You, typed** and is labelled like a spoken line, so a decision or a next step you type goes into its section. Your notes steer the summary, and they save at once.
|
|
17
19
|
4. Press **All notes** to open an earlier note. The **⋯** menu has Copy as Markdown, New meeting, the meeting and the initiative, Summary by agent, Mark private, Settings and **Delete note**.
|
|
18
20
|
|
|
@@ -125,7 +127,7 @@ To stop keeping audio, clear **Keep the audio for replay** in the settings. To d
|
|
|
125
127
|
|
|
126
128
|
## Capture highlights during the meeting
|
|
127
129
|
|
|
128
|
-
While listening, press
|
|
130
|
+
While listening, press the highlighter in the top bar (or in the note) to capture the last 30 seconds of conversation. A pop-up shows the highlight's progress:
|
|
129
131
|
|
|
130
132
|
1. **Capturing**: the last 30 seconds are being recorded.
|
|
131
133
|
2. **Listening**: the highlight listens forward. Choose how long: **30 seconds**, **1 minute** or **2 minutes**. The remaining time counts down.
|
|
@@ -165,7 +167,7 @@ Under the title, a note shows two lines: **Meeting** and **Initiative**.
|
|
|
165
167
|
|
|
166
168
|
When a note has no initiative, the summary suggests one: the open initiative that shares 3 or more words with what was said, when no other initiative shares as many. A suggestion never replaces an initiative you chose.
|
|
167
169
|
|
|
168
|
-
To take notes for a calendar event, open the event's detail on the Agenda and press **Take notes**. PMOS starts to listen at once, to your microphone and to the meeting when local transcription is set up. Pick the meeting's tab or window when the browser asks. Let people know that you are taking notes. The note is linked to the event, and to the event's initiative when it has one. The
|
|
170
|
+
To take notes for a calendar event, open the event's detail on the Agenda and press **Take notes**. PMOS starts to listen at once, to your microphone and to the meeting when local transcription is set up. Pick the meeting's tab or window when the browser asks. Let people know that you are taking notes. The note is linked to the event, and to the event's initiative when it has one. The top bar shows the red dot and the clock on every page. Press the clock to open the note as a pop-up: add your notes, read the key points, and press **Stop** when the meeting ends. The pop-up stays steady while it listens: new lines are added at the end, and your place in the pop-up, the line you are typing and its cursor do not move. If the transcript is scrolled to its end, it follows the new lines. Press **Minimize** or Esc to make it small again. **Open in Notes** in the **⋯** menu shows it on the full screen. One note is taken at a time. If you press **Take notes** on another event while a note listens, PMOS asks first: **Keep it**, or **Stop it and start this one**. The note stays in the top bar after a reload. Press **Stop** in the note to put it away. An event that already has a note shows its summary and **Open notes**, which opens the note in the pop-up. See [Today](today.md) and [the initiative sheet](initiatives-screen.md).
|
|
169
171
|
|
|
170
172
|
## Dictate into your notes
|
|
171
173
|
|
package/workspace/lib/notes.js
CHANGED
|
@@ -109,9 +109,29 @@ function rowOf(n) {
|
|
|
109
109
|
return { id: n.id, title: n.title || 'Untitled note', created_at: n.created_at, updated_at: n.updated_at, private: !!n.private,
|
|
110
110
|
words: wordsOf(n.body), segments: (n.transcript || []).length, summary: !!(n.summary || (n.agent && n.agent.markdown)),
|
|
111
111
|
meeting: n.meeting || null, initiative: n.initiative || null, ws: n.ws || null,
|
|
112
|
+
// the meeting time so far (a paused note's clock) and how many statements differ from a repository file (the Notes menu)
|
|
113
|
+
meeting_ms: n.recorded_from ? meetingMs(n) : 0, conflicts: n.private ? 0 : conflictsOf(n).length,
|
|
112
114
|
// a private note shows its title on the calendar and the initiative, never its summary
|
|
113
115
|
summary_lines: n.private ? [] : summaryLines(n) };
|
|
114
116
|
}
|
|
117
|
+
/** The statements of a note that a repository file says otherwise: transcript lines, the agent's bullets and the PM's
|
|
118
|
+
* highlights whose check verdict is 'differs'. → [{ text, doc, line, t, from }] */
|
|
119
|
+
function conflictsOf(n) {
|
|
120
|
+
const out = [];
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
const add = (x, from) => {
|
|
123
|
+
const c = x && !x.dropped && x.check;
|
|
124
|
+
if (!c || c.verdict !== 'differs' || !c.doc) return;
|
|
125
|
+
const k = c.doc + ':' + (c.line || 0);
|
|
126
|
+
if (seen.has(k)) return;
|
|
127
|
+
seen.add(k);
|
|
128
|
+
out.push({ text: x.text, doc: c.doc, line: c.line || null, words: c.words || null, t: typeof x.t === 'number' ? x.t : null, from });
|
|
129
|
+
};
|
|
130
|
+
(n.highlights || []).forEach((x) => add(x, 'highlight'));
|
|
131
|
+
(n.transcript || []).forEach((x) => add(x, 'line'));
|
|
132
|
+
(n.bullets || []).forEach((x) => add(x, 'bullet'));
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
115
135
|
/** Up to three lines of the note's summary: the agent's, else the local overview. Plain text. */
|
|
116
136
|
function summaryLines(n) {
|
|
117
137
|
if (n.agent && n.agent.markdown) {
|
|
@@ -1418,7 +1438,84 @@ function editBullet(dir, { id, bid, cat, drop, text, now = Date.now() } = {}) {
|
|
|
1418
1438
|
return { ok: true, bullet: b, summary: note.summary || null };
|
|
1419
1439
|
}
|
|
1420
1440
|
|
|
1421
|
-
|
|
1441
|
+
/* ── highlights and the PM's edits to the final notes (Notes menu) ──────────────────────────────── */
|
|
1442
|
+
/* A HIGHLIGHT is the PM's mark on a stretch of the meeting: the last 30 seconds before the press and the time it listened
|
|
1443
|
+
* after (30 s, 1 or 2 min). It is made from the transcript lines said in that window, so it is only as good as what was
|
|
1444
|
+
* transcribed: its text is those lines, its label is the strongest label among them (decision, then action, question,
|
|
1445
|
+
* fact, statement), and the caller adds the repository check. A window with no line makes no highlight. */
|
|
1446
|
+
const MAX_HIGHLIGHTS = 50;
|
|
1447
|
+
const HL_ORDER = ['decision', 'action', 'question', 'fact', 'statement'];
|
|
1448
|
+
const HL_WORD = { decision: 'Decision', action: 'Next step', question: 'Question', fact: 'Fact', statement: 'Statement' };
|
|
1449
|
+
function addHighlight(dir, { id, from, to, now = Date.now() } = {}) {
|
|
1450
|
+
const note = load(dir, id);
|
|
1451
|
+
if (!note) return err(404, 'no such note');
|
|
1452
|
+
if (note.private) return err(409, 'this note is private, so it keeps no highlights');
|
|
1453
|
+
const a = Number(from); const b = Number(to);
|
|
1454
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || b <= a || b - a > 5 * 60 * 1000) return err(400, 'from and to must be meeting times in ms, at most 5 minutes apart');
|
|
1455
|
+
const lines = (note.transcript || []).map((x, index) => ({ ...x, index })).filter((x) => x && !x.dropped && x.text && typeof x.t === 'number' && x.t >= Math.max(0, a) && x.t <= b);
|
|
1456
|
+
if (!lines.length) return err(422, 'nothing was transcribed in that window');
|
|
1457
|
+
const cats = lines.map((x) => (isFact(x) ? 'fact' : x.cat));
|
|
1458
|
+
const cat = HL_ORDER.find((c) => cats.includes(c)) || 'statement';
|
|
1459
|
+
const differs = lines.find((x) => x.check && x.check.verdict === 'differs');
|
|
1460
|
+
const h = { id: 'h-' + crypto.randomBytes(4).toString('hex'), at: iso(now), from: Math.max(0, Math.round(a)), to: Math.round(b), t: Math.max(0, Math.round(a)),
|
|
1461
|
+
text: clip(lines.map((x) => x.text).join(' '), 1200), cat, label: HL_WORD[cat], lines: lines.map((x) => x.index),
|
|
1462
|
+
check: differs ? { ...differs.check } : null };
|
|
1463
|
+
if (!Array.isArray(note.highlights)) note.highlights = [];
|
|
1464
|
+
note.highlights.push(h);
|
|
1465
|
+
if (note.highlights.length > MAX_HIGHLIGHTS) note.highlights = note.highlights.slice(-MAX_HIGHLIGHTS);
|
|
1466
|
+
note.updated_at = iso(now);
|
|
1467
|
+
store(dir, note);
|
|
1468
|
+
return { ok: true, highlight: h };
|
|
1469
|
+
}
|
|
1470
|
+
/** The repository check (or Jev's label) of one highlight, written after it was made. */
|
|
1471
|
+
function updateHighlight(dir, { id, hid, patch, now = Date.now() } = {}) {
|
|
1472
|
+
const note = load(dir, id);
|
|
1473
|
+
if (!note) return err(404, 'no such note');
|
|
1474
|
+
const h = (note.highlights || []).find((x) => x.id === hid);
|
|
1475
|
+
if (!h) return err(404, 'no such highlight');
|
|
1476
|
+
if (patch && patch.check !== undefined) h.check = patch.check;
|
|
1477
|
+
if (patch && patch.cat && HL_WORD[patch.cat]) { h.cat = patch.cat; h.label = HL_WORD[patch.cat]; }
|
|
1478
|
+
note.updated_at = iso(now);
|
|
1479
|
+
store(dir, note);
|
|
1480
|
+
return { ok: true, highlight: h };
|
|
1481
|
+
}
|
|
1482
|
+
function removeHighlight(dir, { id, hid, now = Date.now() } = {}) {
|
|
1483
|
+
const note = load(dir, id);
|
|
1484
|
+
if (!note) return err(404, 'no such note');
|
|
1485
|
+
const before = (note.highlights || []).length;
|
|
1486
|
+
note.highlights = (note.highlights || []).filter((x) => x.id !== hid);
|
|
1487
|
+
if (note.highlights.length === before) return err(404, 'no such highlight');
|
|
1488
|
+
note.updated_at = iso(now);
|
|
1489
|
+
store(dir, note);
|
|
1490
|
+
return { ok: true };
|
|
1491
|
+
}
|
|
1492
|
+
/** The PM's correction to the final notes: the overview, or the text of one decision, next step or open question. The
|
|
1493
|
+
* first words the agent wrote are kept as `orig_text`. */
|
|
1494
|
+
const FINAL_SECS = { decision: 'decisions', action: 'actions', question: 'questions' };
|
|
1495
|
+
function editFinal(dir, { id, sec, i, text, now = Date.now() } = {}) {
|
|
1496
|
+
const note = load(dir, id);
|
|
1497
|
+
if (!note) return err(404, 'no such note');
|
|
1498
|
+
const f = note.final_notes;
|
|
1499
|
+
if (!f) return err(409, 'this note has no final notes yet');
|
|
1500
|
+
const t = clip(text, sec === 'overview' ? 2000 : 400);
|
|
1501
|
+
if (!t) return err(400, 'the text is empty');
|
|
1502
|
+
if (sec === 'overview') {
|
|
1503
|
+
if (f.overview_orig === undefined && f.overview !== t) f.overview_orig = f.overview || '';
|
|
1504
|
+
f.overview = t;
|
|
1505
|
+
} else {
|
|
1506
|
+
const list = FINAL_SECS[sec] ? f[FINAL_SECS[sec]] : null;
|
|
1507
|
+
const x = Array.isArray(list) ? list[Number(i)] : null;
|
|
1508
|
+
if (!x) return err(404, 'no such item');
|
|
1509
|
+
if (x.orig_text === undefined && x.text !== t) x.orig_text = x.text;
|
|
1510
|
+
x.text = t;
|
|
1511
|
+
}
|
|
1512
|
+
keepVersion(note, now);
|
|
1513
|
+
note.updated_at = iso(now);
|
|
1514
|
+
store(dir, note);
|
|
1515
|
+
return { ok: true, final_notes: f };
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
module.exports = { addHighlight, updateHighlight, removeHighlight, editFinal, conflictsOf, setFinal, setFinalState, renameFinalSpeaker, finalSpeaker, transcriptLines, cleanNotes, notesMarkdown, groundQuote, parseTime, NOTES_SHAPE,
|
|
1422
1519
|
wisprHandoff, wisprPrompt, setWispr, importWispr, notesOutOf, wisprOutOf, list, read, create, link, findByEvent, sameEvent, cleanMeeting, summaryLines, save, remove, record, pause, meetingMs, appendTranscript, updateSegment, setLabel, editLine, versionsOf, renameSpeaker, speakerOf, categoryOf, isFact, CATS, PM_CATS, VERSION_FOLD_MS, setPrivate, dictation, cleanSentences, spokenPunctuation,
|
|
1423
1520
|
summarize, localSummary, agentHandoff, agentStarted, toMarkdown, promptFor, fmtTime, ownerOf, dueOf, ID_RE, notesDir, handoffOf, agentOutOf,
|
|
1424
1521
|
meetsBar, checkable, vocabOf, termsIn, namesIn, SECTION_CAP, JOIN_GAP_MS,
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
* without `ok`, or { started: false, reason } when no agent was asked
|
|
40
40
|
* POST /api/notes/summarize-agent { id, ws? } → { ok, started, session, handoff, out, withheld, reason }
|
|
41
41
|
* POST /api/notes/delete { id } → { ok }
|
|
42
|
+
* POST /api/notes/highlight { id, from, to } → { ok, highlight, checked, jev } the PM's highlight: the transcript lines said
|
|
43
|
+
* between the meeting times from and to, labelled, then checked against the repository
|
|
44
|
+
* POST /api/notes/highlight-remove { id, hid } → { ok }
|
|
45
|
+
* POST /api/notes/final-edit { id, sec: 'overview'|'decision'|'action'|'question', i?, text } → { ok, final_notes } the PM's correction
|
|
42
46
|
*
|
|
43
47
|
* Notes are the PM's own and live in the per-user runtime directory (ctx.runtimeDir), never in a repository, so no route here
|
|
44
48
|
* writes into a registered workspace. Every POST goes through pm-guard `requirePagePress` (a body that is not an object or carries
|
|
@@ -75,7 +79,7 @@ const VOCAB_TTL_MS = 60 * 1000;
|
|
|
75
79
|
const prefixes = ['/api/notes'];
|
|
76
80
|
const POSTS = new Set(['/api/notes/new', '/api/notes/save', '/api/notes/dictate', '/api/notes/record', '/api/notes/transcript',
|
|
77
81
|
'/api/notes/private', '/api/notes/pause', '/api/notes/summarize', '/api/notes/summarize-agent', '/api/notes/delete', '/api/notes/audio', '/api/notes/label',
|
|
78
|
-
'/api/notes/speaker', '/api/notes/switch', '/api/notes/whisper-setup', '/api/notes/final', '/api/notes/final-setup', '/api/notes/line', '/api/notes/recording-delete', '/api/notes/link', '/api/notes/bullet', '/api/notes/
|
|
82
|
+
'/api/notes/speaker', '/api/notes/switch', '/api/notes/whisper-setup', '/api/notes/final', '/api/notes/final-setup', '/api/notes/line', '/api/notes/recording-delete', '/api/notes/link', '/api/notes/bullet', '/api/notes/highlight', '/api/notes/highlight-remove', '/api/notes/final-edit']);
|
|
79
83
|
|
|
80
84
|
/* ── labels and checks, one note at a time ───────────────────────────────────────────────────── */
|
|
81
85
|
const chains = new Map(); // note id → the promise of its work so far (minute batches, imports, labels, checks)
|
|
@@ -690,17 +694,34 @@ async function handle(req, res, url, ctx) {
|
|
|
690
694
|
return done(r);
|
|
691
695
|
}
|
|
692
696
|
if (p === '/api/notes/delete') { disarm(id); unwatchLate(id); cancelFinal(ctx, id); unwatchWispr(id); if (transcriber) transcriber.drop(id); const r = notes.remove(dir, id); if (r.ok) notesAudio.remove(dir, id); return done(r); }
|
|
693
|
-
if (p === '/api/notes/
|
|
694
|
-
//
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
697
|
+
if (p === '/api/notes/highlight') {
|
|
698
|
+
// the PM's highlight: the transcript of the window, then Jev's label and the repository check of its text
|
|
699
|
+
const r = notes.addHighlight(dir, { id, from: body.from, to: body.to });
|
|
700
|
+
if (!r.ok) return done(r);
|
|
701
|
+
const note = stillOpen(dir, id);
|
|
702
|
+
const w = note && note.ws ? ctx.getWs(note.ws) : null;
|
|
703
|
+
const g = gatesFor(ctx, w ? w.root : null);
|
|
704
|
+
let h = r.highlight;
|
|
705
|
+
if (g.ask) {
|
|
706
|
+
const l = await jevNotes.label({ ask: g.ask, key: g.key, askOpts: g.askOpts, text: h.text, root: g.root });
|
|
707
|
+
if (l.cat && notes.meetsBar(l.cat, h.text)) { const u = notes.updateHighlight(dir, { id, hid: h.id, patch: { cat: l.cat } }); if (u.ok) h = u.highlight; }
|
|
708
|
+
}
|
|
709
|
+
if (g.root && !(h.check && h.check.verdict === 'differs')) {
|
|
710
|
+
const pass = notesRepo.closest(g.root, h.text, { exclude: g.exclude });
|
|
711
|
+
if (pass) {
|
|
712
|
+
let check = { doc: pass.path, line: pass.line, verdict: 'related', by: 'local' };
|
|
713
|
+
if (g.ask && g.check) {
|
|
714
|
+
const c = await jevNotes.compare({ ask: g.ask, key: g.key, askOpts: g.askOpts, text: h.text, passage: pass, root: g.root });
|
|
715
|
+
if (c.verdict) check = { doc: pass.path, line: pass.line, verdict: c.verdict, words: c.words || null, by: 'jev' };
|
|
716
|
+
}
|
|
717
|
+
const u = notes.updateHighlight(dir, { id, hid: h.id, patch: { check } });
|
|
718
|
+
if (u.ok) h = u.highlight;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return done({ ok: true, highlight: h, checked: !!g.root, jev: !!g.ask });
|
|
703
722
|
}
|
|
723
|
+
if (p === '/api/notes/highlight-remove') return done(notes.removeHighlight(dir, { id, hid: body.hid }));
|
|
724
|
+
if (p === '/api/notes/final-edit') return done(notes.editFinal(dir, { id, sec: body.sec, i: body.i, text: body.text }));
|
|
704
725
|
// summarize-agent: the handoff file first (refused for a private note), then the PM's own agent session
|
|
705
726
|
let w = null;
|
|
706
727
|
if (body.ws != null) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.108.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pmos-workspace",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.108.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -406,7 +406,7 @@ function render() {
|
|
|
406
406
|
|
|
407
407
|
const FRAME = !FIXTURE && !LEGACY_LAYOUT_FLAG;
|
|
408
408
|
const FRAME_KEY = 'pmos.workspace.pm.frame';
|
|
409
|
-
const FRAME_DESTS = Object.freeze(['today', 'mywork', 'initiatives', 'board', 'reports', 'docs', 'people', 'settings']);
|
|
409
|
+
const FRAME_DESTS = Object.freeze(['today', 'mywork', 'initiatives', 'board', 'reports', 'docs', 'notes', 'people', 'settings']);
|
|
410
410
|
|
|
411
411
|
function readFrame() {
|
|
412
412
|
if (!FRAME) return {};
|
|
@@ -696,7 +696,7 @@ function pressIn(r, key) {
|
|
|
696
696
|
if (b && !b.disabled) { b.click(); return true; }
|
|
697
697
|
return false;
|
|
698
698
|
}
|
|
699
|
-
const G_KEYS = { t: 'today', i: 'initiatives', b: 'board', r: 'reports', d: 'docs', p: 'people', w: 'mywork' };
|
|
699
|
+
const G_KEYS = { t: 'today', i: 'initiatives', b: 'board', r: 'reports', d: 'docs', n: 'notes', p: 'people', w: 'mywork' };
|
|
700
700
|
|
|
701
701
|
function frameKey(e) {
|
|
702
702
|
const mod = e.metaKey || e.ctrlKey;
|