create-pmos 0.65.0 → 0.65.1

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.
@@ -3,7 +3,8 @@
3
3
  * About 70% of the width on a desktop and the whole screen on a phone. Top to bottom:
4
4
  * line 1 the title, the horizon, the health reason (only when not on track), and a meta line: lane · anchor · id
5
5
  * line 2 the stage stepper, then "Waiting on you: approval [Open gate ▸]" or "Waiting on agent: build [Hand to agent ▸]"
6
- * tabs Breakdown (the Run 1 pane, embedded: Tree · Board, and Timeline once Run 4 registers it), Files, History,
6
+ * tabs Breakdown (the Run 1 pane, embedded: Tree · Board, and Timeline once Run 4 registers it), Files (List · Graph:
7
+ * the graph is doc-graph.js, initiative docs-library-graph), History,
7
8
  * Jira tickets (sheet-jira.js, initiative jira-wbs: the tickets drafted from the PRD and their status in Jira),
8
9
  * People (people-link.js, initiative people-directory: who owns its items or is named in interactions about it)
9
10
  * ⋯ horizon, check-by, lifecycle and the anchor (both PM-only: the server's guard answers), Copy CLI, Export breakdown
@@ -22,6 +23,7 @@ import { mountBreakdown, unmountBreakdown } from './panel-breakdown.js';
22
23
  import { mountJira, unmountJira } from './sheet-jira.js';
23
24
  import { initiativePeople } from './people-link.js';
24
25
  import { openDoc } from './panel-initiatives.js';
26
+ import { getLibrary, initiativeGraph } from './doc-graph.js';
25
27
  import { openNewSession } from './panel-newsess.js';
26
28
  import { wsOf, readDoc, saveDoc } from './data.js';
27
29
  import { state, setState } from './state.js';
@@ -67,6 +69,8 @@ export function openSheet(repo, slug) {
67
69
  ensureSprite();
68
70
  let data = null;
69
71
  let tab = 'breakdown';
72
+ let fview = 'list'; // the Files tab: 'list' or 'graph' (docs-library-graph R-N1)
73
+ let lib = null; // the library, read on the first press on Graph
70
74
  let closed = false;
71
75
  const tabId = 'initiative:' + slug;
72
76
  const crumb = el('div', { class: 'in-sheet__crumb' });
@@ -277,9 +281,27 @@ export function openSheet(repo, slug) {
277
281
  onOpen: () => { s.close(); openDoc(slug, f.path, f.label, null, f.source === 'git' ? { readonly: true } : undefined); },
278
282
  attrs: { 'data-in-file': f.kind, 'data-in-file-source': f.source || 'record' }, openAttrs: { 'data-in-file-open': f.kind },
279
283
  }));
280
- panel.replaceChildren(files.length ? el('div', { class: 'in-files' }, groups.map(([src, list]) => el('section', { class: 'in-files__group', 'data-in-files': src },
284
+ const listView = () => (files.length ? el('div', { class: 'in-files' }, groups.map(([src, list]) => el('section', { class: 'in-files__group', 'data-in-files': src },
281
285
  el('h3', { class: 'in-files__title' }, COPY.sheet.fileSource[src] || src),
282
286
  el('div', { class: 'in-sheet__rows', role: 'list' }, list.map(fileRow))))) : empty({ text: COPY.sheet.noFiles }));
287
+ // List · Graph (docs-library-graph R-N1): the graph is this initiative's markdown files (the ones its record names
288
+ // and the ones the library's index attributes to it; a file git changed for it is evidence, not one of its files)
289
+ // and the outside files they link to. On a phone initiativeGraph draws nothing and the list shows (R-N5).
290
+ const paths = files.filter((f) => f.kind !== 'note' && (f.source || 'record') !== 'git' && /\.md$/i.test(f.path || '')).map((f) => f.path);
291
+ const seg = tabs({ variant: 'segmented', ariaLabel: COPY.sheet.filesAs, value: fview,
292
+ items: [{ id: 'list', label: COPY.sheet.filesList }, { id: 'graph', label: COPY.sheet.filesGraph }], attrs: { 'data-in-files-view': '' },
293
+ onChange: (id) => { if (id === fview) return; fview = id; drawPanel(); const t = panel.querySelector('[data-in-files-view] [data-tab="' + id + '"]'); if (t) t.focus({ preventScroll: true }); } });
294
+ const bar = el('div', { class: 'in-files__bar' }, seg, el('span', { class: 'in-files__hint' }, fview === 'graph' ? COPY.sheet.graphHint : COPY.sheet.listHint));
295
+ if (fview === 'list' || !paths.length) { panel.replaceChildren(bar, listView()); return; }
296
+ const open = (p, title) => { s.close(); openDoc(slug, p, title); };
297
+ const graphNow = () => { const g = lib ? initiativeGraph(lib, paths, { onOpen: open }) : null; return g || el('div', null, el('p', { class: 'in-note' }, COPY.sheet.graphPhone), listView()); };
298
+ if (lib) { panel.replaceChildren(bar, graphNow()); return; }
299
+ panel.replaceChildren(bar, el('p', { class: 'in-note' }, COPY.sheet.graphReading));
300
+ getLibrary(ws).then((l) => {
301
+ if (tab !== 'files' || fview !== 'graph') return;
302
+ if (!l) { panel.replaceChildren(bar, el('p', { class: 'in-note' }, COPY.sheet.graphError), listView()); return; }
303
+ lib = l; drawPanel();
304
+ });
283
305
  return;
284
306
  }
285
307
  const hist = data.history || [];
@@ -134,6 +134,14 @@ export const COPY = {
134
134
  openGate: 'Open gate',
135
135
  handToAgent: 'Hand to agent',
136
136
  noFiles: 'No files yet.',
137
+ filesAs: 'Show files as',
138
+ filesList: 'List',
139
+ filesGraph: 'Graph',
140
+ listHint: 'Every file this initiative owns.',
141
+ graphHint: 'This initiative’s files, and the files outside it that they link to.',
142
+ graphReading: 'Reading the links…',
143
+ graphError: 'The links could not be read. The list is below.',
144
+ graphPhone: 'On a small screen the files show as a list.',
137
145
  fileSource: { record: 'Named by the record', 'knowledge-graph': 'Linked documents', git: 'Changed for it in git' },
138
146
  commits: (n) => (n === 1 ? '1 commit' : `${n} commits`),
139
147
  shared: 'shared',
@@ -131,6 +131,7 @@ const focus = require('./lib/focus'); // buildFocus(input) →
131
131
  const handoff = require('./lib/handoff'); // readLinks(parsedFrontmatterObject) — never a path
132
132
  const fourPanel = require('./lib/four-panel'); // GET /api/initiative — the one-initiative join + the declaration stores
133
133
  const docSyncStore = require('./lib/doc-sync-store');
134
+ const docLibrary = require('./lib/doc-library'); // every repository file by category, and the links the files write (docs-library-graph)
134
135
  const peopleStore = require('./lib/people-store'); // people-directory (D90) — people.jsonl, folded on read
135
136
  // The session each repository's Check now started (workspace id → session id), so a second press while it runs refuses.
136
137
  const docCheckSessions = new Map();
@@ -6549,6 +6550,7 @@ async function handleRequest(req, res) {
6549
6550
  const docs = v.docs.map((d) => ({
6550
6551
  ...d,
6551
6552
  initiative: owner.get(d.path) || (d.initiative_id ? { id: d.initiative_id, title: d.initiative_id } : null),
6553
+ category: docLibrary.categoryOf(d.path, d.type),
6552
6554
  links: d.links.map((l) => {
6553
6555
  if (l.syncing) return l;
6554
6556
  const ps = docSyncSessions.get(`${wid}|${d.path}|${l.kind}`);
@@ -6557,6 +6559,25 @@ async function handleRequest(req, res) {
6557
6559
  }));
6558
6560
  return json(res, { ...v, docs, ws: w ? w.id : null, session: live, connectors: fourPanel.connectors(os.homedir()) });
6559
6561
  }
6562
+ // All files (docs-library-graph): every tracked markdown file by category, its initiative, and the links the files
6563
+ // write (lib/doc-library.js over pmos kg). Kept in memory per repository state; nothing is written into the repository.
6564
+ if (p === '/api/docs/library' && req.method === 'GET') {
6565
+ const w = getWs(url.searchParams.get('ws'));
6566
+ const root = w ? w.root : SELF_ROOT;
6567
+ const owners = () => {
6568
+ const own = new Map();
6569
+ if (wsShape(root) !== 'pmos') return own;
6570
+ const real = (id) => fs.existsSync(path.join(root, 'planning', 'initiatives', id + '.md'));
6571
+ for (const g of initiativeIndex(root).initiatives) {
6572
+ if (!real(g.id)) continue;
6573
+ for (const a of g.artifacts) { const rel = String(a.path).split(path.sep).join('/'); if (!a.shared && !own.has(rel)) own.set(rel, { id: g.id, title: g.title || g.id }); }
6574
+ }
6575
+ return own;
6576
+ };
6577
+ const lib = docLibrary.library(root, { stateDir: STATE, owners });
6578
+ if (!lib.ok) return json(res, lib, 400);
6579
+ return json(res, { ...lib, ws: w ? w.id : null });
6580
+ }
6560
6581
  if (p === '/api/docs/files' && req.method === 'GET') {
6561
6582
  const w = getWs(url.searchParams.get('ws'));
6562
6583
  const root = w ? w.root : SELF_ROOT;
@@ -0,0 +1,5 @@
1
+ {
2
+ "_comment": "docs-library-graph: every repository file by category in Docs, the links the files write, and an initiative's files as a network. The browser half runs in pm-docs-test.js (docs-three-way-sync's fragment).",
3
+ "modules": ["doc-library.test.js"],
4
+ "seconds": { "doc-library.test.js": 2 }
5
+ }