create-pmos 0.123.0 → 0.124.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 +1 -0
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/dest-notes.js +25 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.124.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-09T03:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-09T03:19:19Z"}
|
|
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-09T03:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-09T03:19:19Z"}
|
|
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-09T03:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-09T03:19:19Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/guide/notes.md
CHANGED
|
@@ -63,6 +63,7 @@ The Notes view shows one meeting, top to bottom. A bar at the top shows whether
|
|
|
63
63
|
1. Press **Listen**. Select what to listen to:
|
|
64
64
|
- **My microphone**: what you say.
|
|
65
65
|
- **The meeting**: the other people in an online meeting. The meeting can be in any app, such as Zoom, or in any browser tab. In the sharing window, choose **Entire screen** and tick **Also share system audio**, so that the sound of every app is recorded. To record one browser tab, choose that tab and tick **Share tab audio**. Chrome offers system audio on Windows and ChromeOS; on macOS use a tab.
|
|
66
|
+
- **Skipping the sharing window.** A browser always asks before a page records the screen, and a page cannot turn that off. To pick **Entire screen** without being asked, start Chrome with the switch `--auto-select-desktop-capture-source="Entire screen"` (a Chrome on your own machine, for example from a shortcut). System audio is then shared when the browser and the system offer it (see above). This is a Chrome setting on your machine, not a PMOS setting.
|
|
66
67
|
2. Press **Start**. The header shows a red dot, the time, and the sources. Tell people that you are taking notes. If the meeting stays completely silent for 20 seconds while your microphone works, a red message says that the other people are not being recorded. Most often the sharing window had the wrong source. Press **Stop**, press **Listen** again, choose **Entire screen** (or the meeting's tab), and tick **Also share system audio** (or **Share tab audio**). The message goes away when sound arrives or when you press **Stop**.
|
|
67
68
|
3. Each line shows in the transcript a few seconds after it is said, with who said it, the time and its label. Your lines are **You**. The meeting's lines are **Meeting**: click the name to change it. The transcript cannot tell the people in the meeting apart.
|
|
68
69
|
4. Press **Stop** to end. The summary shows at the top of your notes. To carry on, press **Listen again**: the clock carries on from where it stopped, and the time between does not count. Line times and the kept audio use the same clock.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.124.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.124.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -207,7 +207,7 @@ async function refresh(inst) {
|
|
|
207
207
|
if (seq !== inst.seq) return;
|
|
208
208
|
if (r && r.ok) { inst.rows = r.notes || []; inst.error = null; } else { inst.rows = inst.rows || []; inst.error = (r && r.error) || COPY.readError; }
|
|
209
209
|
if (!inst.open && inst.rows.length) inst.open = inst.rows[0].id;
|
|
210
|
-
if (one && one.ok && one.note && one.note.id === inst.open) inst.note = one.note;
|
|
210
|
+
if (one && one.ok && one.note && one.note.id === inst.open) { inst.note = one.note; inst.rec = one.recording || null; }
|
|
211
211
|
else if (inst.open && !one) loadOpen(inst);
|
|
212
212
|
drawLeft(inst);
|
|
213
213
|
drawRight(inst);
|
|
@@ -217,8 +217,25 @@ async function loadOpen(inst) {
|
|
|
217
217
|
const r = await pmGet('/api/notes/one?id=' + encodeURIComponent(id));
|
|
218
218
|
if (inst.open !== id) return;
|
|
219
219
|
inst.note = r && r.ok ? r.note : null;
|
|
220
|
+
inst.rec = r && r.ok ? r.recording || null : null;
|
|
220
221
|
drawRight(inst);
|
|
221
222
|
}
|
|
223
|
+
/** The replay of the note's kept audio: one <audio> per note, kept across redraws so playback goes on. Null when no audio is kept. */
|
|
224
|
+
function replayFor(inst, n) {
|
|
225
|
+
const rec = inst.rec;
|
|
226
|
+
if (!rec || n.private) { inst.audio = null; return null; }
|
|
227
|
+
const key = n.id + ':' + rec.bytes + ':' + (rec.cleaned ? 1 : 0);
|
|
228
|
+
if (inst.audio && inst.audio.key === key) return inst.audio.el;
|
|
229
|
+
if (inst.audio && !inst.audio.el.paused) return inst.audio.el; // never cut a replay that is playing
|
|
230
|
+
const a = el('audio', { controls: true, preload: 'metadata', class: 'nt-audio', 'data-ntm-audio': '', src: '/api/notes/recording?id=' + encodeURIComponent(n.id) + '&b=' + rec.bytes + (rec.cleaned ? '&c=1' : '') });
|
|
231
|
+
inst.audio = { key, el: a };
|
|
232
|
+
return a;
|
|
233
|
+
}
|
|
234
|
+
function seekReplay(inst, ms) {
|
|
235
|
+
const a = inst.audio && inst.audio.el;
|
|
236
|
+
if (!a) return;
|
|
237
|
+
try { a.currentTime = Math.max(0, ms / 1000); a.play(); } catch { /* the audio is not ready */ }
|
|
238
|
+
}
|
|
222
239
|
function choose(inst, id) {
|
|
223
240
|
if (inst.open === id) return;
|
|
224
241
|
inst.open = id; inst.note = null; inst.ctxOpen = null; inst.info = false;
|
|
@@ -259,7 +276,7 @@ function drawRight(inst, force) {
|
|
|
259
276
|
const n = inst.note;
|
|
260
277
|
const live = dock.recordState();
|
|
261
278
|
const held = !!n && live.mode !== 'idle' && live.id === n.id;
|
|
262
|
-
const sig = JSON.stringify([inst.open, n && [n.updated_at, n.title, n.final_notes && n.final_notes.at, (n.transcript || []).length, (n.highlights || []).length, n.agent && n.agent.markdown, iniName(n.initiative), n.state, n.resume_until, n.ended_at, (n.sessions || []).length, n.meeting && n.meeting.context && n.meeting.context.taken_at], live.mode, live.id, !!inst.rows, inst.ctxOpen, inst.info]);
|
|
279
|
+
const sig = JSON.stringify([inst.open, n && [n.updated_at, n.title, n.final_notes && n.final_notes.at, (n.transcript || []).length, (n.highlights || []).length, n.agent && n.agent.markdown, iniName(n.initiative), n.state, n.resume_until, n.ended_at, (n.sessions || []).length, n.meeting && n.meeting.context && n.meeting.context.taken_at], inst.rec && [inst.rec.bytes, inst.rec.cleaned], live.mode, live.id, !!inst.rows, inst.ctxOpen, inst.info]);
|
|
263
280
|
// a held note draws again on every render: the clock, the lines and the paused minutes move. Nothing else draws without a change.
|
|
264
281
|
if (!force && !held && sig === inst.rightSig) return;
|
|
265
282
|
// a field being typed in is not redrawn under the cursor, except in the live block, whose fields are kept across redraws
|
|
@@ -387,6 +404,11 @@ function noteParts(inst, n, live) {
|
|
|
387
404
|
if (V.renderLive(liveHost, { state: inst.ctx.state, setState: inst.ctx.setState, FIXTURE: inst.ctx.FIXTURE }, n.id)) out.push(liveHost);
|
|
388
405
|
}
|
|
389
406
|
|
|
407
|
+
const replay = held ? null : replayFor(inst, n);
|
|
408
|
+
if (replay) out.push(el('section', { class: 'nt-pg-sec nt-pg-sec--first nt-pg-replay', 'data-ntm': 'replay' },
|
|
409
|
+
el('h3', { class: 'nt-pg-sec__h' }, el('span', null, 'Replay')), replay,
|
|
410
|
+
el('p', { class: 'nt-hint' }, 'Kept on this machine. Press a time in the discussion to play from there.')));
|
|
411
|
+
|
|
390
412
|
const cf = conflictsOf(n);
|
|
391
413
|
if (cf.length) {
|
|
392
414
|
out.push(el('section', { class: 'nt-pg-conflicts', 'data-ntm': 'conflicts' },
|
|
@@ -438,7 +460,7 @@ function noteParts(inst, n, live) {
|
|
|
438
460
|
out.push(el('details', { class: 'nt-pg-sec nt-pg-fold', 'data-ntm': 'discussion' },
|
|
439
461
|
el('summary', { class: 'nt-pg-fold__sum' }, COPY.discussion + ' · ' + bits),
|
|
440
462
|
p.topics.map((tp) => el('div', { class: 'nt-pg-topic' }, el('h4', { class: 'nt-pg-topic__h' }, tp.title), (tp.points || []).map((pt) => el('p', { class: 'nt-pg-topic__p' }, pt)))),
|
|
441
|
-
tr.length ? el('div', { class: 'nt-pg-trans' }, tr.map((x) => el('div', { class: 'nt-pg-li' }, el('span', { class: 'nt-pg-li__at' }, V.fmtTime(x.t || 0)), el('span', { class: 'nt-pg-li__text' }, x.text)))) : null));
|
|
463
|
+
tr.length ? el('div', { class: 'nt-pg-trans' }, tr.map((x) => el('div', { class: 'nt-pg-li' }, inst.audio && !held ? button({ label: V.fmtTime(x.t || 0), variant: 'ghost', size: 'sm', onClick: () => seekReplay(inst, x.t || 0), attrs: { 'data-ntm-seek': '', title: 'Play from here' } }) : el('span', { class: 'nt-pg-li__at' }, V.fmtTime(x.t || 0)), el('span', { class: 'nt-pg-li__text' }, x.text)))) : null));
|
|
442
464
|
}
|
|
443
465
|
return out.filter(Boolean);
|
|
444
466
|
}
|