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.
- 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/docs.md +59 -6
- package/workspace/guide/initiatives-screen.md +8 -0
- package/workspace/lib/doc-library.js +131 -0
- package/workspace/package-lock.json +1 -1
- package/workspace/package.json +1 -1
- package/workspace/public/pm/components.css +307 -0
- package/workspace/public/pm/dest-docs.js +144 -44
- package/workspace/public/pm/doc-graph.js +272 -0
- package/workspace/public/pm/initiative-sheet.js +24 -2
- package/workspace/public/pm/initiatives-copy.js +8 -0
- package/workspace/server.js +21 -0
- package/workspace/test-lists/docs-library-graph.json +5 -0
|
@@ -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
|
|
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
|
-
|
|
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',
|
package/workspace/server.js
CHANGED
|
@@ -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
|
+
}
|