create-pmos 0.84.0 → 0.85.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.84.0",
3
+ "version": "0.85.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": {
@@ -1,3 +1,3 @@
1
- kernel-source: pmos@fea3b24
1
+ kernel-source: pmos@2d5583a
2
2
  assembled: 2026-10-07
3
3
  mode: vendor
@@ -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-07T16:12:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-07T16:18:36Z"}
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-07T16:12:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-07T16:18:36Z"}
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-07T16:12:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-07T16:18:36Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -2,16 +2,18 @@
2
2
  type: Guide
3
3
  title: "Command palette"
4
4
  description: "How to use the ⌘K palette: run a command, see its command-line form, keep a note, ask the bound session, search Knowledge, Memory and the Guide, and use the compose line on a phone."
5
- timestamp: 2026-10-05
5
+ timestamp: 2026-10-07
6
6
  ---
7
7
 
8
8
  # Command palette
9
9
 
10
10
  The command palette is one box for three jobs: open something, run a command, or keep a note. Press **⌘K** (Ctrl+K on Windows and Linux) to open it. Press **Esc** to close it.
11
11
 
12
+ On a desktop, the palette is a dialog in the middle of the screen. The search box is at the top with a search icon, the scope switch is under it, and a footer at the bottom shows a hint and the keys. On a phone it is a sheet (see "On a phone").
13
+
12
14
  ## Rows
13
15
 
14
- Type in the box. The rows change as you type. They always come in this order:
16
+ Type in the box. The rows change as you type. Rows of one kind sit under one small label, for example Commands or Initiatives, and each row has an icon for its kind. The selected row has a ring and an arrow. The rows come in this order:
15
17
 
16
18
  1. **Matched rows.** A command, an initiative, a repository, a session, a tab or a view whose name matches what you typed. A command shows its command-line form next to its name, for example `pmos item`. You can run the same command from a terminal.
17
19
  2. **Capture as note.** Keeps what you typed as a note.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pmos-workspace",
9
- "version": "0.84.0",
9
+ "version": "0.85.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@xterm/addon-fit": "^0.11.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "private": true,
5
5
  "description": "PMOS Terminal Workspace — local companion hosting real PTYs beside the library and gates (D62)",
6
6
  "license": "MIT",
@@ -696,6 +696,14 @@
696
696
  height: auto;
697
697
  max-height: calc(var(--pmc-vvh, 100dvh) - 24px);
698
698
  }
699
+ .pmc-sheet--center {
700
+ inset: auto;
701
+ top: 84px;
702
+ left: 50%;
703
+ width: 640px;
704
+ max-height: 70vh;
705
+ transform: translateX(-50%);
706
+ }
699
707
  .pmc-sheet__handle {
700
708
  flex: none;
701
709
  width: 36px;
@@ -723,11 +731,33 @@
723
731
  letter-spacing: -0.01em;
724
732
  overflow-wrap: anywhere;
725
733
  }
734
+ .pmc-sheet__title--hidden {
735
+ position: absolute;
736
+ width: 1px;
737
+ height: 1px;
738
+ padding: 0;
739
+ margin: -1px;
740
+ overflow: hidden;
741
+ clip: rect(0, 0, 0, 0);
742
+ white-space: nowrap;
743
+ border-width: 0;
744
+ }
726
745
  .pmc-sheet__sub {
727
746
  font-size: 12px;
728
747
  line-height: 16px;
729
748
  color: var(--muted-foreground);
730
749
  }
750
+ .pmc-sheet__sub--hidden {
751
+ position: absolute;
752
+ width: 1px;
753
+ height: 1px;
754
+ padding: 0;
755
+ margin: -1px;
756
+ overflow: hidden;
757
+ clip: rect(0, 0, 0, 0);
758
+ white-space: nowrap;
759
+ border-width: 0;
760
+ }
731
761
  .pmc-sheet__head .pmc-btn--icon {
732
762
  margin: -4px -8px 0 0;
733
763
  }
@@ -741,6 +771,16 @@
741
771
  overscroll-behavior: contain;
742
772
  padding: 16px;
743
773
  }
774
+ .pmc-sheet--center .pmc-sheet__body {
775
+ gap: 0;
776
+ padding: 0;
777
+ overflow: hidden;
778
+ min-height: 0;
779
+ }
780
+ .pmc-sheet--center {
781
+ display: flex;
782
+ flex-direction: column;
783
+ }
744
784
  .pmc-sheet__body > * {
745
785
  flex: none;
746
786
  }
@@ -3672,13 +3712,49 @@
3672
3712
  overscroll-behavior: contain;
3673
3713
  }
3674
3714
  .pl-item[aria-selected="true"] {
3675
- background: var(--accent);
3715
+ background: var(--accent) !important;
3716
+ box-shadow: 0 0 0 1px var(--ring);
3717
+ }
3718
+ .pl-icon-tile {
3719
+ display: flex;
3720
+ align-items: center;
3721
+ justify-content: center;
3722
+ width: 28px;
3723
+ height: 28px;
3724
+ border-radius: 6px;
3725
+ background: var(--muted);
3726
+ color: var(--muted-foreground);
3727
+ flex: none;
3728
+ }
3729
+ .pl-item[aria-selected="true"] .pl-icon-tile {
3730
+ color: var(--foreground);
3731
+ }
3732
+ .pl-enter-icon {
3733
+ display: flex;
3734
+ align-items: center;
3735
+ justify-content: center;
3736
+ width: 16px;
3737
+ height: 16px;
3738
+ color: var(--foreground);
3739
+ }
3740
+ .pl-eyebrow {
3741
+ font-family: var(--mono);
3742
+ font-size: 11px;
3743
+ line-height: 16px;
3744
+ letter-spacing: 0.04em;
3745
+ text-transform: uppercase;
3746
+ color: var(--muted-foreground);
3747
+ padding: 10px 8px 4px;
3676
3748
  }
3677
3749
  .pl-cli {
3678
3750
  font-family: var(--mono);
3679
3751
  font-size: 12px;
3680
3752
  line-height: 16px;
3681
3753
  color: var(--muted-foreground);
3754
+ padding: 2px 6px;
3755
+ border-radius: 6px;
3756
+ background: var(--background);
3757
+ box-shadow: 0 0 0 1px var(--border);
3682
3758
  }
3683
3759
  .pl-note {
3684
3760
  margin: 0;
@@ -3689,6 +3765,74 @@
3689
3765
  .pl-note--error {
3690
3766
  color: var(--destructive);
3691
3767
  }
3768
+ .pl-search {
3769
+ display: flex;
3770
+ align-items: center;
3771
+ gap: 10px;
3772
+ padding: 0 12px 0 16px;
3773
+ border-bottom: 1px solid var(--border);
3774
+ }
3775
+ .pl-search__icon {
3776
+ display: flex;
3777
+ color: var(--muted-foreground);
3778
+ }
3779
+ .pl-search > div {
3780
+ flex: 1 1 auto;
3781
+ }
3782
+ .pl-input.pl-input,
3783
+ .pl-input.pl-input:focus-visible {
3784
+ height: 52px;
3785
+ font-size: 16px;
3786
+ border: 0;
3787
+ outline: none;
3788
+ }
3789
+ .pl-kbd {
3790
+ font: 11px/16px var(--mono);
3791
+ color: var(--muted-foreground);
3792
+ padding: 0 6px;
3793
+ border-radius: 6px;
3794
+ box-shadow: 0 0 0 1px var(--border);
3795
+ }
3796
+ .pl-center .pl-scopes {
3797
+ align-self: flex-start;
3798
+ margin: 10px 16px 4px;
3799
+ }
3800
+ .pl-center .pl-list {
3801
+ flex: 1 1 auto;
3802
+ min-height: 0;
3803
+ max-height: none;
3804
+ padding: 4px 8px 8px;
3805
+ }
3806
+ .pl-center .pl-list .pl-item {
3807
+ border-bottom: 0;
3808
+ background: transparent;
3809
+ border-radius: 6px;
3810
+ min-height: 44px;
3811
+ padding: 6px 8px;
3812
+ }
3813
+ .pl-center .pl-list .pl-item > span:first-child {
3814
+ align-self: center;
3815
+ }
3816
+ .pl-foot {
3817
+ display: flex;
3818
+ align-items: center;
3819
+ gap: 12px;
3820
+ padding: 8px 16px;
3821
+ border-top: 1px solid var(--border);
3822
+ background: var(--background);
3823
+ }
3824
+ .pl-foot .pl-note {
3825
+ flex: 1 1 auto;
3826
+ min-width: 0;
3827
+ font-size: 12px;
3828
+ line-height: 16px;
3829
+ }
3830
+ .pl-keys {
3831
+ flex: none;
3832
+ font-size: 12px;
3833
+ line-height: 16px;
3834
+ color: var(--muted-foreground);
3835
+ }
3692
3836
  /* the terminal's compose line (phone): one message field and Send, in the bottom bar */
3693
3837
  .pl-compose {
3694
3838
  display: flex;
@@ -17,6 +17,10 @@ export const COPY = {
17
17
  command: 'Command', destination: 'Go to', initiative: 'Initiative', repository: 'Repository', session: 'Session',
18
18
  tab: 'Tab', view: 'View', knowledge: 'Document', memory: 'Memory', guide: 'Guide', capture: 'Note', ask: 'Ask',
19
19
  },
20
+ groups: {
21
+ destination: 'Go to', command: 'Commands', initiative: 'Initiatives', repository: 'Repositories', session: 'Sessions',
22
+ tab: 'Tabs', view: 'Views', knowledge: 'Knowledge', memory: 'Memory', guide: 'Guide', capture: 'Keep', ask: 'Keep',
23
+ },
20
24
  hint: 'Type to search. Press Enter to open the first row.',
21
25
  hintTyped: 'Type to search. Capture as note keeps what you typed.',
22
26
  noMatch: (q) => `Nothing matches "${q}".`,
@@ -21,8 +21,8 @@
21
21
 
22
22
  import { el } from './dom.js';
23
23
  import { state, setState, FIXTURE } from './state.js';
24
- import { sheet, field, tabs, row, badge, signal, toast, button, dialogText, ensureComponentsCss } from './primitives.js';
25
- import { ensureSprite } from './icons.js';
24
+ import { sheet, field, tabs, row, badge, signal, toast, button, dialogText, ensureComponentsCss, narrowViewport } from './primitives.js';
25
+ import { ensureSprite, icon } from './icons.js';
26
26
  import { pmGet, pmPost } from './http.js';
27
27
  import { COPY } from './palette-copy.js';
28
28
 
@@ -215,15 +215,27 @@ function choose(it) {
215
215
  try { it.run(); } catch (e) { console.warn('[palette] a row failed:', (e && e.message) || e); }
216
216
  }
217
217
 
218
+ /** Get the kind icon for a palette item. */
219
+ function kindIcon(kind) {
220
+ const iconMap = {
221
+ destination: 'arrow-right', command: 'code', initiative: 'target', repository: 'git-branch', session: 'square-terminal',
222
+ tab: 'panel-top', view: 'panel-top', knowledge: 'file-text', memory: 'history', guide: 'list', capture: 'text-quote', ask: 'bot',
223
+ };
224
+ return iconMap[kind] || 'circle';
225
+ }
226
+
218
227
  function itemRow(it, i, id) {
219
228
  const sigs = [];
220
229
  if (it.cli) sigs.push(el('code', { class: 'pl-cli' }, it.cli));
221
230
  else if (it.tag) sigs.push(signal({ text: it.tag }));
231
+ const lead = [el('span', { class: 'pl-icon-tile', 'aria-hidden': 'true' }, icon(kindIcon(it.kind)))];
232
+ const trail = i === sel ? [el('span', { class: 'pl-enter-icon', 'aria-hidden': 'true' }, icon('chevron-right'))] : [];
222
233
  return row({
223
234
  title: it.label,
224
235
  meta: [it.hint].filter(Boolean),
225
236
  signals: sigs,
226
- lead: [badge({ text: COPY.kinds[it.kind] || it.kind, tone: 'muted' })],
237
+ lead,
238
+ trail,
227
239
  onOpen: () => choose(it),
228
240
  openAttrs: { 'data-palette': 'item', 'data-kind': it.kind, tabindex: '-1' },
229
241
  attrs: { class: 'pl-item', id, role: 'option', 'aria-selected': i === sel ? 'true' : 'false' },
@@ -238,8 +250,21 @@ function refresh() {
238
250
  const sig = rows.map((it) => `${it.kind}|${it.label}|${it.hint || ''}|${it.cli || ''}`).join('\n') + '#' + sel;
239
251
  if (sig !== ui.sig) {
240
252
  ui.sig = sig;
241
- ui.list.replaceChildren(...rows.map((it, i) => itemRow(it, i, ui.listId + '-' + i)));
242
- const cur = ui.list.children[sel];
253
+ // Build the list with eyebrow labels for groups
254
+ const grouped = [];
255
+ let lastKind = null;
256
+ let itemCount = 0;
257
+ for (const it of rows) {
258
+ if (it.kind !== lastKind) {
259
+ const groupName = COPY.groups[it.kind] || COPY.kinds[it.kind] || it.kind;
260
+ grouped.push(el('div', { class: 'pl-eyebrow', 'aria-hidden': 'true' }, groupName));
261
+ lastKind = it.kind;
262
+ }
263
+ grouped.push(itemRow(it, itemCount, ui.listId + '-' + itemCount));
264
+ itemCount++;
265
+ }
266
+ ui.list.replaceChildren(...grouped);
267
+ const cur = ui.list.querySelector('[role="option"][aria-selected="true"]');
243
268
  ui.input.control.setAttribute('aria-activedescendant', cur ? cur.id : '');
244
269
  if (cur && cur.scrollIntoView) cur.scrollIntoView({ block: 'nearest' });
245
270
  }
@@ -262,7 +287,7 @@ function setScope(next) {
262
287
  ui.input.control.focus();
263
288
  }
264
289
 
265
- const scopeTabs = () => tabs({ variant: 'line', items: SCOPES.map((id) => ({ id, label: COPY.scopes[id] })), value: scope, onChange: setScope, ariaLabel: COPY.scopeLabel, attrs: { 'data-palette': 'scopes' } });
290
+ const scopeTabs = () => tabs({ variant: narrowViewport() ? 'line' : 'segmented', items: SCOPES.map((id) => ({ id, label: COPY.scopes[id] })), value: scope, onChange: setScope, ariaLabel: COPY.scopeLabel, attrs: { 'data-palette': 'scopes', class: 'pl-scopes' } });
266
291
 
267
292
  function onKey(e) {
268
293
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
@@ -282,11 +307,17 @@ function openPalette(deps) {
282
307
  q = ''; scope = 'all'; sel = 0; noteKey = ''; pinned = null; busy = false;
283
308
  const listId = 'pl-list-' + Math.random().toString(36).slice(2, 7);
284
309
  const input = field({ label: COPY.inputLabel, hideLabel: true, placeholder: COPY.placeholder,
285
- attrs: { 'data-palette': 'input', role: 'combobox', 'aria-expanded': 'true', 'aria-controls': listId, 'aria-autocomplete': 'list', spellcheck: 'false' } });
310
+ attrs: { 'data-palette': 'input', role: 'combobox', 'aria-expanded': 'true', 'aria-controls': listId, 'aria-autocomplete': 'list', spellcheck: 'false', class: narrowViewport() ? '' : 'pl-input' } });
286
311
  const list = el('div', { class: 'pl-list', role: 'listbox', id: listId, 'aria-label': COPY.title, 'data-palette': 'list' });
287
312
  const note = el('p', { class: 'pl-note', role: 'status', 'data-palette': 'note' });
288
313
  const scopes = scopeTabs();
289
- const dlg = sheet({ mode: 'auto', title: COPY.title, sub: COPY.sub, body: [scopes, input, list, note], attrs: { 'data-palette': 'overlay' },
314
+
315
+ // Center mode for palette on desktop, bottom sheet on phone
316
+ const mode = narrowViewport() ? 'bottom' : 'center';
317
+ const center = mode === 'center';
318
+ const search = center ? el('div', { class: 'pl-search' }, el('span', { class: 'pl-search__icon', 'aria-hidden': 'true' }, icon('search')), input, el('kbd', { class: 'pl-kbd', 'aria-hidden': 'true' }, 'Esc')) : input;
319
+ const foot = center ? el('div', { class: 'pl-foot' }, note, el('span', { class: 'pl-keys' }, COPY.keys)) : note;
320
+ const dlg = sheet({ mode, title: COPY.title, sub: COPY.sub, body: [search, scopes, list, foot], attrs: { 'data-palette': 'overlay', class: center ? 'pl-center' : '' },
290
321
  onClose: () => { ui = null; if (state.paletteOpen) setState({ paletteOpen: false }); } });
291
322
  ui = { sheet: dlg, input, list, note, scopes, deps, sig: null, listId };
292
323
  input.control.addEventListener('input', () => { input.control.resetCommitted(input.control.value); q = input.control.value; sel = 0; pinned = null; if (ui) { ui.sig = null; refresh(); } });
@@ -394,21 +394,23 @@ export function narrowViewport() {
394
394
  }
395
395
 
396
396
  /**
397
- * sheet({ mode: 'auto'|'side'|'bottom', title, sub, body: Node[], actions: Node[], onClose, attrs })
398
- * A modal <dialog> in the layer: 420px at the side above 760px, a bottom sheet below. Escape and the backdrop close it.
397
+ * sheet({ mode: 'auto'|'side'|'bottom'|'center', title, sub, body: Node[], actions: Node[], onClose, attrs })
398
+ * A modal <dialog> in the layer: 420px at the side above 760px, a bottom sheet below, or centered. Escape and the backdrop close it.
399
399
  * On a phone the sheet follows the on-screen keyboard through visualViewport. Returns { dialog, body, close, setHead }.
400
400
  */
401
401
  export function sheet({ mode = 'auto', title, sub, body, actions, onClose, attrs } = {}) {
402
402
  const m = mode === 'auto' ? (narrowViewport() ? 'bottom' : 'side') : mode;
403
403
  const tid = nextId('pmc-sheet-t');
404
- const titleEl = el('h2', { class: 'pmc-sheet__title', id: tid }, title || '');
405
- const subEl = el('div', { class: 'pmc-sheet__sub' }, sub || '');
404
+ const titleEl = el('h2', { class: cx('pmc-sheet__title', m === 'center' && 'pmc-sheet__title--hidden'), id: tid }, title || '');
405
+ const subEl = el('div', { class: cx('pmc-sheet__sub', m === 'center' && 'pmc-sheet__sub--hidden') }, sub || '');
406
406
  const bodyEl = el('div', { class: 'pmc-sheet__body' }, body || []);
407
407
  let closed = false;
408
+ const headEl = m === 'center' ? null : el('div', { class: 'pmc-sheet__head' }, el('div', { class: 'pmc-sheet__heading' }, titleEl, subEl),
409
+ iconButton({ icon: 'x', ariaLabel: 'Close', onClick: () => close() }));
408
410
  const dialog = el('dialog', { 'aria-modal': 'true', 'aria-labelledby': tid, tabindex: '-1', ...withClass(cx('pmc-sheet', 'pmc-sheet--' + m), attrs) },
409
411
  m === 'bottom' ? el('div', { class: 'pmc-sheet__handle', 'aria-hidden': 'true' }) : null,
410
- el('div', { class: 'pmc-sheet__head' }, el('div', { class: 'pmc-sheet__heading' }, titleEl, subEl),
411
- iconButton({ icon: 'x', ariaLabel: 'Close', onClick: () => close() })),
412
+ m === 'center' ? titleEl : headEl,
413
+ m === 'center' ? subEl : null,
412
414
  bodyEl,
413
415
  actions && actions.length ? el('div', { class: 'pmc-sheet__actions' }, actions) : null);
414
416