@justyork/repo-mind 0.7.0 → 0.7.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.
Files changed (67) hide show
  1. package/dist/ui/draft-api.js +33 -1
  2. package/dist/ui/fs-operations.d.ts +13 -0
  3. package/dist/ui/fs-operations.js +139 -0
  4. package/dist/ui/fs-tree.d.ts +6 -1
  5. package/dist/ui/fs-tree.js +62 -36
  6. package/package.json +3 -1
  7. package/ui/dist/assets/{arc-DufwQU06.js → arc-B9D1Pvqi.js} +1 -1
  8. package/ui/dist/assets/{architectureDiagram-3BPJPVTR-CI2fjjSo.js → architectureDiagram-3BPJPVTR-Bvug0Ca5.js} +1 -1
  9. package/ui/dist/assets/{blockDiagram-GPEHLZMM-DVBIs-Z6.js → blockDiagram-GPEHLZMM-Ckmilsu4.js} +1 -1
  10. package/ui/dist/assets/{c4Diagram-AAUBKEIU-rhAX_HrB.js → c4Diagram-AAUBKEIU-BT9pbmn0.js} +1 -1
  11. package/ui/dist/assets/channel-TAOLyHdK.js +1 -0
  12. package/ui/dist/assets/{chunk-2J33WTMH-CxWSQi5S.js → chunk-2J33WTMH-dgxq-Xfu.js} +1 -1
  13. package/ui/dist/assets/{chunk-4BX2VUAB-BEM-vVbU.js → chunk-4BX2VUAB-DmM9cIh_.js} +1 -1
  14. package/ui/dist/assets/{chunk-55IACEB6-TcrLDHYx.js → chunk-55IACEB6-DsB88bPS.js} +1 -1
  15. package/ui/dist/assets/{chunk-727SXJPM-D8g4OcIW.js → chunk-727SXJPM-BYuDClxC.js} +1 -1
  16. package/ui/dist/assets/{chunk-AQP2D5EJ-CSvrR8rQ.js → chunk-AQP2D5EJ-RO0Jvaof.js} +1 -1
  17. package/ui/dist/assets/{chunk-FMBD7UC4-C14JNc29.js → chunk-FMBD7UC4-DqY1unJF.js} +1 -1
  18. package/ui/dist/assets/{chunk-ND2GUHAM-Cnb0VFKm.js → chunk-ND2GUHAM-BAURH6-3.js} +1 -1
  19. package/ui/dist/assets/{chunk-QZHKN3VN-D2pqmAXN.js → chunk-QZHKN3VN-BiCWufLo.js} +1 -1
  20. package/ui/dist/assets/classDiagram-4FO5ZUOK-0RJZpoIN.js +1 -0
  21. package/ui/dist/assets/classDiagram-v2-Q7XG4LA2-0RJZpoIN.js +1 -0
  22. package/ui/dist/assets/{cose-bilkent-S5V4N54A-Cli2Sbvt.js → cose-bilkent-S5V4N54A-Bol8tJZ0.js} +1 -1
  23. package/ui/dist/assets/{dagre-BM42HDAG-gUsVuy4s.js → dagre-BM42HDAG-ziNCIqra.js} +1 -1
  24. package/ui/dist/assets/{diagram-2AECGRRQ-DTKi_1b6.js → diagram-2AECGRRQ-Dh2G8IOg.js} +1 -1
  25. package/ui/dist/assets/{diagram-5GNKFQAL-8Bj0k_-k.js → diagram-5GNKFQAL-C_26Fq9O.js} +1 -1
  26. package/ui/dist/assets/{diagram-KO2AKTUF-CKOX2fpv.js → diagram-KO2AKTUF-CPS1B4bk.js} +1 -1
  27. package/ui/dist/assets/{diagram-LMA3HP47-B12JvpF3.js → diagram-LMA3HP47-DoL5Nx28.js} +1 -1
  28. package/ui/dist/assets/{diagram-OG6HWLK6-DKeTI9IH.js → diagram-OG6HWLK6-DOy-TY5D.js} +1 -1
  29. package/ui/dist/assets/editor-CTtDusro.js +211 -0
  30. package/ui/dist/assets/{erDiagram-TEJ5UH35-oDb31edH.js → erDiagram-TEJ5UH35-C2whsoWw.js} +1 -1
  31. package/ui/dist/assets/{flowDiagram-I6XJVG4X-SPf3KLS-.js → flowDiagram-I6XJVG4X-D7-1XdVh.js} +1 -1
  32. package/ui/dist/assets/{ganttDiagram-6RSMTGT7-D8oPEcrt.js → ganttDiagram-6RSMTGT7-Dduumk4v.js} +1 -1
  33. package/ui/dist/assets/{gitGraphDiagram-PVQCEYII-eI9Yp7d7.js → gitGraphDiagram-PVQCEYII-CJfZihUe.js} +1 -1
  34. package/ui/dist/assets/{graph-DqCH9VKs.js → graph-CFkZObJQ.js} +1 -1
  35. package/ui/dist/assets/{infoDiagram-5YYISTIA-CQ6dtyj3.js → infoDiagram-5YYISTIA-B8IsGW31.js} +1 -1
  36. package/ui/dist/assets/{ishikawaDiagram-YF4QCWOH-BHnOZ8-9.js → ishikawaDiagram-YF4QCWOH-CwaI1VjO.js} +1 -1
  37. package/ui/dist/assets/{journeyDiagram-JHISSGLW-0t985reH.js → journeyDiagram-JHISSGLW-jo8e3Vri.js} +1 -1
  38. package/ui/dist/assets/{kanban-definition-UN3LZRKU-vPlhgwPS.js → kanban-definition-UN3LZRKU-DsFcvv5g.js} +1 -1
  39. package/ui/dist/assets/main-DEdNYNxf.js +274 -0
  40. package/ui/dist/assets/{mermaid.core-C2wn2wM5.js → mermaid.core-CIyMeT5H.js} +4 -4
  41. package/ui/dist/assets/{mindmap-definition-RKZ34NQL-COKv8bQN.js → mindmap-definition-RKZ34NQL-Bl3ML0fg.js} +1 -1
  42. package/ui/dist/assets/{pieDiagram-4H26LBE5-CgVeawD6.js → pieDiagram-4H26LBE5-rCj5-rCc.js} +1 -1
  43. package/ui/dist/assets/{quadrantDiagram-W4KKPZXB-Bwpr8PxN.js → quadrantDiagram-W4KKPZXB-tF5l8Pj1.js} +1 -1
  44. package/ui/dist/assets/{requirementDiagram-4Y6WPE33-Cc8HFBxl.js → requirementDiagram-4Y6WPE33-CtAsbYzh.js} +1 -1
  45. package/ui/dist/assets/{sankeyDiagram-5OEKKPKP-B_1nSAK5.js → sankeyDiagram-5OEKKPKP-CYgylTyv.js} +1 -1
  46. package/ui/dist/assets/{sequenceDiagram-3UESZ5HK-hsYrUJza.js → sequenceDiagram-3UESZ5HK-D2FeAk8O.js} +1 -1
  47. package/ui/dist/assets/{stateDiagram-AJRCARHV-BMf4FL5O.js → stateDiagram-AJRCARHV-BhNt372S.js} +1 -1
  48. package/ui/dist/assets/stateDiagram-v2-BHNVJYJU-hesflu-P.js +1 -0
  49. package/ui/dist/assets/theme-CgUWrIXo.css +1 -0
  50. package/ui/dist/assets/theme-DBoBdw62.js +1 -0
  51. package/ui/dist/assets/{timeline-definition-PNZ67QCA-Cf3u1h2_.js → timeline-definition-PNZ67QCA-DYrj-JqE.js} +1 -1
  52. package/ui/dist/assets/{vennDiagram-CIIHVFJN-2uKO3x22.js → vennDiagram-CIIHVFJN-DAdImqHv.js} +1 -1
  53. package/ui/dist/assets/visual-editor-BHZk1CI4.js +220 -0
  54. package/ui/dist/assets/{wardley-L42UT6IY-BjLaoVtm.js → wardley-L42UT6IY-BfiflBRy.js} +1 -1
  55. package/ui/dist/assets/{wardleyDiagram-YWT4CUSO-DlrUKeJk.js → wardleyDiagram-YWT4CUSO-5HZM-go9.js} +1 -1
  56. package/ui/dist/assets/{xychartDiagram-2RQKCTM6-Bzbb3cqD.js → xychartDiagram-2RQKCTM6-ozNCw5n2.js} +1 -1
  57. package/ui/dist/graph.html +3 -3
  58. package/ui/dist/index.html +4 -4
  59. package/ui/dist/assets/channel-Q_-BqwUT.js +0 -1
  60. package/ui/dist/assets/classDiagram-4FO5ZUOK-CPrZx4R4.js +0 -1
  61. package/ui/dist/assets/classDiagram-v2-Q7XG4LA2-CPrZx4R4.js +0 -1
  62. package/ui/dist/assets/editor-DZbRkvnr.js +0 -211
  63. package/ui/dist/assets/main-Io6jccwi.js +0 -232
  64. package/ui/dist/assets/stateDiagram-v2-BHNVJYJU-BpaPdu-O.js +0 -1
  65. package/ui/dist/assets/theme-Dl8H1UuW.js +0 -1
  66. package/ui/dist/assets/theme-PE9lQ4bz.css +0 -1
  67. package/ui/dist/assets/visual-editor-D1uYYGOW.js +0 -47
@@ -2,7 +2,7 @@ import { runExport } from '../commands/export.js';
2
2
  import { isValidSlug } from '../index/slug.js';
3
3
  import { DOC_TYPES, isDocStatus, isDocType } from '../index/types.js';
4
4
  import { writeCatalogEmoji } from './catalog-meta.js';
5
- import { createFolder, createPageFile, deleteFolder, deletePageFile, movePageFile, renamePageFile } from './fs-operations.js';
5
+ import { createFolder, createPageFile, deleteFolder, deletePageFile, moveFolder, movePageFile, promotePageToFolder, renamePageFile } from './fs-operations.js';
6
6
  import { prepareDocFile, prepareAllDocs } from '../prepare/prepare-docs.js';
7
7
  import { syncAllDocLinks } from '../prepare/auto-links.js';
8
8
  import { getDoc } from '../tools/get-doc.js';
@@ -102,6 +102,7 @@ export function handleDraftApi(index, db, method, pathname, bodyRaw) {
102
102
  body: doc.body ?? '',
103
103
  tags,
104
104
  related,
105
+ forked_from: page.slug,
105
106
  target_path: page.relativePath,
106
107
  });
107
108
  return { status: 201, body: { page, draft } };
@@ -111,6 +112,21 @@ export function handleDraftApi(index, db, method, pathname, bodyRaw) {
111
112
  return jsonError(400, message);
112
113
  }
113
114
  }
115
+ if (pathname === '/api/fs/promote-page' && method === 'POST') {
116
+ const body = parseJsonBody(bodyRaw);
117
+ if (body === null) {
118
+ return jsonError(400, 'invalid JSON body');
119
+ }
120
+ const pagePath = typeof body.pagePath === 'string' ? body.pagePath : '';
121
+ try {
122
+ const result = promotePageToFolder(index, pagePath);
123
+ return { status: 200, body: { result } };
124
+ }
125
+ catch (error) {
126
+ const message = error instanceof Error ? error.message : String(error);
127
+ return jsonError(400, message);
128
+ }
129
+ }
114
130
  if (pathname === '/api/fs/move' && method === 'POST') {
115
131
  const body = parseJsonBody(bodyRaw);
116
132
  if (body === null) {
@@ -127,6 +143,22 @@ export function handleDraftApi(index, db, method, pathname, bodyRaw) {
127
143
  return jsonError(400, message);
128
144
  }
129
145
  }
146
+ if (pathname === '/api/fs/move-folder' && method === 'POST') {
147
+ const body = parseJsonBody(bodyRaw);
148
+ if (body === null) {
149
+ return jsonError(400, 'invalid JSON body');
150
+ }
151
+ const fromPath = typeof body.fromPath === 'string' ? body.fromPath : '';
152
+ const toParentDir = typeof body.toParentDir === 'string' ? body.toParentDir : '';
153
+ try {
154
+ const result = moveFolder(index, fromPath, toParentDir);
155
+ return { status: 200, body: { result } };
156
+ }
157
+ catch (error) {
158
+ const message = error instanceof Error ? error.message : String(error);
159
+ return jsonError(400, message);
160
+ }
161
+ }
130
162
  if (pathname === '/api/fs/rename' && method === 'POST') {
131
163
  const body = parseJsonBody(bodyRaw);
132
164
  if (body === null) {
@@ -44,9 +44,22 @@ export interface CreatePageOptions {
44
44
  title?: string;
45
45
  templateId?: string;
46
46
  }
47
+ export interface PromotePageResult extends FsPageMutationResult {
48
+ folderPath: string;
49
+ }
47
50
  export declare function createFolder(index: DocIndex, parentPath: string, name: string): CreateFolderResult;
48
51
  export declare function createPageFile(index: DocIndex, parentPath: string, name: string, options?: CreatePageOptions): CreatePageResult;
52
+ /** Creates sibling folder `{parent}/{basename}/` for a leaf page (Confluence-style); page file stays in place. */
53
+ export declare function promotePageToFolder(index: DocIndex, pagePath: string): PromotePageResult;
49
54
  export declare function movePageFile(index: DocIndex, fromPath: string, toDir: string): FsPageMutationResult;
50
55
  export declare function renamePageFile(index: DocIndex, pagePath: string, newName: string): FsPageMutationResult;
56
+ export interface FsMoveFolderResult {
57
+ relativePath: string;
58
+ previousPath: string;
59
+ siblingPagePath?: string;
60
+ cascadeUpdated: string[];
61
+ }
62
+ /** Moves a folder and its Confluence sibling page file when present. */
63
+ export declare function moveFolder(index: DocIndex, fromPath: string, toParentDir: string): FsMoveFolderResult;
51
64
  export declare function deletePageFile(index: DocIndex, pagePath: string): FsDeletePageResult;
52
65
  export declare function deleteFolder(index: DocIndex, folderPath: string): FsDeleteFolderResult;
@@ -115,6 +115,58 @@ function rewritePageFrontmatter(absolutePath, newRelativePath) {
115
115
  fs.writeFileSync(absolutePath, matter.stringify(parsed.content, nextData), 'utf8');
116
116
  return { newSlug, newType };
117
117
  }
118
+ /** Creates sibling folder `{parent}/{basename}/` for a leaf page (Confluence-style); page file stays in place. */
119
+ export function promotePageToFolder(index, pagePath) {
120
+ const knowledgeRoot = index.getKnowledgeRoot();
121
+ if (!knowledgeRoot) {
122
+ throw new Error('no docs/ directory found');
123
+ }
124
+ const normalizedFrom = normalizeRelativePath(pagePath);
125
+ if (normalizedFrom === 'README.md') {
126
+ throw new Error('cannot promote docs root index');
127
+ }
128
+ const fileName = path.posix.basename(normalizedFrom);
129
+ if (fileName.toLowerCase() === 'readme.md') {
130
+ throw new Error('page is already inside a folder');
131
+ }
132
+ const sourceAbsolute = resolveRelativeMdPath(knowledgeRoot, normalizedFrom);
133
+ if (!sourceAbsolute || !fs.existsSync(sourceAbsolute)) {
134
+ throw new Error(`page not found: ${pagePath}`);
135
+ }
136
+ const parentPath = parentRelativePath(normalizedFrom);
137
+ const baseName = knowledgeFileDisplayName(fileName);
138
+ if (!isValidFsName(baseName)) {
139
+ throw new Error(`invalid page name: ${baseName}`);
140
+ }
141
+ const folderRelative = joinRelativePath(parentPath, baseName);
142
+ const folderAbsolute = resolveUnderKnowledgeRoot(knowledgeRoot, folderRelative);
143
+ if (!folderAbsolute) {
144
+ throw new Error('path escapes docs/');
145
+ }
146
+ if (fs.existsSync(folderAbsolute)) {
147
+ if (!fs.statSync(folderAbsolute).isDirectory()) {
148
+ throw new Error(`already exists: ${folderRelative}`);
149
+ }
150
+ }
151
+ else {
152
+ fs.mkdirSync(folderAbsolute, { recursive: true });
153
+ }
154
+ const previousSlug = readPageSlug(sourceAbsolute);
155
+ index.refresh();
156
+ return {
157
+ relativePath: normalizedFrom,
158
+ absolutePath: sourceAbsolute,
159
+ slug: previousSlug,
160
+ previousSlug,
161
+ slugChanged: false,
162
+ inboundWarnings: [],
163
+ cascadeUpdated: [],
164
+ folderPath: normalizeRelativePath(folderRelative),
165
+ };
166
+ }
167
+ function knowledgeFileDisplayName(fileName) {
168
+ return fileName.replace(/\.(md|ya?ml|json)$/i, '');
169
+ }
118
170
  export function movePageFile(index, fromPath, toDir) {
119
171
  const knowledgeRoot = index.getKnowledgeRoot();
120
172
  if (!knowledgeRoot) {
@@ -244,6 +296,93 @@ function mergeInboundWarnings(lists) {
244
296
  }
245
297
  return merged;
246
298
  }
299
+ function isDescendantOrEqual(ancestor, candidate) {
300
+ const a = normalizeRelativePath(ancestor);
301
+ const c = normalizeRelativePath(candidate);
302
+ if (!a) {
303
+ return true;
304
+ }
305
+ return c === a || c.startsWith(`${a}/`);
306
+ }
307
+ function absoluteToDocsRelative(knowledgeRoot, absolutePath) {
308
+ return normalizeRelativePath(path.relative(knowledgeRoot, absolutePath));
309
+ }
310
+ /** Moves a folder and its Confluence sibling page file when present. */
311
+ export function moveFolder(index, fromPath, toParentDir) {
312
+ const knowledgeRoot = index.getKnowledgeRoot();
313
+ if (!knowledgeRoot) {
314
+ throw new Error('no docs/ directory found');
315
+ }
316
+ const normalizedFrom = normalizeRelativePath(fromPath);
317
+ if (!normalizedFrom) {
318
+ throw new Error('cannot move docs root');
319
+ }
320
+ const sourceAbsolute = resolveUnderKnowledgeRoot(knowledgeRoot, normalizedFrom);
321
+ if (!sourceAbsolute || !fs.existsSync(sourceAbsolute)) {
322
+ throw new Error(`folder not found: ${fromPath}`);
323
+ }
324
+ if (!fs.statSync(sourceAbsolute).isDirectory()) {
325
+ throw new Error(`not a folder: ${fromPath}`);
326
+ }
327
+ const destParent = normalizeRelativePath(toParentDir);
328
+ if (isDescendantOrEqual(normalizedFrom, destParent)) {
329
+ throw new Error('cannot move folder into itself');
330
+ }
331
+ const folderName = path.posix.basename(normalizedFrom);
332
+ const destRelative = joinRelativePath(destParent, folderName);
333
+ const destAbsolute = resolveUnderKnowledgeRoot(knowledgeRoot, destRelative);
334
+ if (!destAbsolute) {
335
+ throw new Error('path escapes docs/');
336
+ }
337
+ if (fs.existsSync(destAbsolute)) {
338
+ throw new Error(`already exists: ${destRelative}`);
339
+ }
340
+ const fromParent = parentRelativePath(normalizedFrom);
341
+ const siblingRelative = joinRelativePath(fromParent, `${folderName}.md`);
342
+ const siblingAbsolute = resolveRelativeMdPath(knowledgeRoot, siblingRelative);
343
+ let siblingDestRelative;
344
+ if (siblingAbsolute && fs.existsSync(siblingAbsolute)) {
345
+ siblingDestRelative = joinRelativePath(destParent, `${folderName}.md`);
346
+ const siblingDestAbsolute = resolveRelativeMdPath(knowledgeRoot, siblingDestRelative);
347
+ if (!siblingDestAbsolute) {
348
+ throw new Error('path escapes docs/');
349
+ }
350
+ if (fs.existsSync(siblingDestAbsolute)) {
351
+ throw new Error(`already exists: ${siblingDestRelative}`);
352
+ }
353
+ fs.renameSync(siblingAbsolute, siblingDestAbsolute);
354
+ }
355
+ fs.renameSync(sourceAbsolute, destAbsolute);
356
+ const movedMarkdownAbsolutes = [];
357
+ if (siblingDestRelative) {
358
+ const siblingDestAbsolute = resolveRelativeMdPath(knowledgeRoot, siblingDestRelative);
359
+ if (siblingDestAbsolute) {
360
+ movedMarkdownAbsolutes.push(siblingDestAbsolute);
361
+ }
362
+ }
363
+ movedMarkdownAbsolutes.push(...listMarkdownFilesRecursive(destAbsolute));
364
+ const cascadeUpdated = new Set();
365
+ for (const absolutePath of movedMarkdownAbsolutes) {
366
+ const relativePath = absoluteToDocsRelative(knowledgeRoot, absolutePath);
367
+ const previousSlug = readPageSlug(absolutePath);
368
+ const { newSlug } = rewritePageFrontmatter(absolutePath, relativePath);
369
+ if (newSlug === previousSlug) {
370
+ continue;
371
+ }
372
+ for (const updatedPath of cascadeSlugRename(knowledgeRoot, previousSlug, newSlug, {
373
+ excludeAbsolutePath: absolutePath,
374
+ })) {
375
+ cascadeUpdated.add(updatedPath);
376
+ }
377
+ }
378
+ index.refresh();
379
+ return {
380
+ relativePath: destRelative,
381
+ previousPath: normalizedFrom,
382
+ siblingPagePath: siblingDestRelative,
383
+ cascadeUpdated: toRelativePaths(knowledgeRoot, [...cascadeUpdated]),
384
+ };
385
+ }
247
386
  export function deletePageFile(index, pagePath) {
248
387
  const knowledgeRoot = index.getKnowledgeRoot();
249
388
  if (!knowledgeRoot) {
@@ -9,6 +9,9 @@ export interface TreePageNode {
9
9
  status: string;
10
10
  type: string;
11
11
  contentKind: 'markdown' | 'yaml' | 'json';
12
+ /** Sibling folder `{parent}/{name}/` when Confluence-style page+folder pair exists. */
13
+ childFolderPath?: string;
14
+ children?: TreeNode[];
12
15
  }
13
16
  export interface TreeFolderNode {
14
17
  kind: 'folder';
@@ -17,6 +20,8 @@ export interface TreeFolderNode {
17
20
  emoji: string | null;
18
21
  /** Slug of README.md index page for this folder. */
19
22
  indexPageSlug: string | null;
23
+ indexPageType: string | null;
24
+ indexPageContentKind: 'markdown' | 'yaml' | 'json' | null;
20
25
  children: TreeNode[];
21
26
  }
22
27
  export type TreeNode = TreePageNode | TreeFolderNode;
@@ -25,4 +30,4 @@ export declare function readmeIndexRelativePath(folderRelativePath: string): str
25
30
  export declare function folderDisplayName(relativePath: string): string;
26
31
  export declare function buildDocsTree(index: DocIndex): TreeFolderNode | null;
27
32
  export declare function findTreePageSlug(node: TreeFolderNode, relativePath: string): string | null;
28
- export declare function collectParentOfEdges(tree: TreeFolderNode | null): LinkEdge[];
33
+ export declare function collectParentOfEdges(_tree: TreeFolderNode | null): LinkEdge[];
@@ -8,6 +8,20 @@ const IGNORED_DIRS = new Set(['.repo-mind', '.worktrees']);
8
8
  function knowledgeFileDisplayName(fileName) {
9
9
  return fileName.replace(/\.(md|ya?ml|json)$/i, '');
10
10
  }
11
+ function findSiblingDirName(dirNames, fileName) {
12
+ const base = knowledgeFileDisplayName(fileName);
13
+ const exact = dirNames.find((name) => name === base);
14
+ if (exact) {
15
+ return exact;
16
+ }
17
+ const lower = base.toLowerCase();
18
+ return dirNames.find((name) => name.toLowerCase() === lower) ?? null;
19
+ }
20
+ function hasSiblingKnowledgePage(entries, dirName) {
21
+ return entries.some((entry) => !entry.isDirectory &&
22
+ isKnowledgeFileName(entry.name) &&
23
+ knowledgeFileDisplayName(entry.name).toLowerCase() === dirName.toLowerCase());
24
+ }
11
25
  function listDirEntries(dirPath) {
12
26
  if (!fs.existsSync(dirPath)) {
13
27
  return [];
@@ -38,17 +52,25 @@ export function folderDisplayName(relativePath) {
38
52
  function buildFolder(relativePath, absPath, ctx) {
39
53
  const entries = listDirEntries(absPath);
40
54
  const children = [];
55
+ const dirNames = entries.filter((entry) => entry.isDirectory).map((entry) => entry.name);
41
56
  const readmeRel = readmeIndexRelativePath(relativePath);
42
57
  let indexPageSlug = null;
58
+ let indexPageType = null;
59
+ let indexPageContentKind = null;
43
60
  const readmeDoc = ctx.docsByRelative.get(readmeRel);
44
61
  if (readmeDoc) {
45
62
  indexPageSlug = readmeDoc.slug;
63
+ indexPageType = readmeDoc.type;
64
+ indexPageContentKind = readmeDoc.contentKind;
46
65
  }
47
66
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
48
67
  if (entry.isDirectory) {
49
68
  if (IGNORED_DIRS.has(entry.name)) {
50
69
  continue;
51
70
  }
71
+ if (hasSiblingKnowledgePage(entries, entry.name)) {
72
+ continue;
73
+ }
52
74
  const childRel = joinRelativePath(relativePath, entry.name);
53
75
  children.push(buildFolder(childRel, path.join(absPath, entry.name), ctx));
54
76
  continue;
@@ -57,13 +79,28 @@ function buildFolder(relativePath, absPath, ctx) {
57
79
  continue;
58
80
  }
59
81
  const fileRel = joinRelativePath(relativePath, entry.name);
60
- if (fileRel === readmeRel && indexPageSlug) {
61
- continue;
62
- }
63
82
  const doc = ctx.docsByRelative.get(fileRel);
64
83
  if (!doc) {
65
84
  continue;
66
85
  }
86
+ const siblingDir = findSiblingDirName(dirNames, entry.name);
87
+ if (siblingDir) {
88
+ const childFolderRel = joinRelativePath(relativePath, siblingDir);
89
+ const nested = buildFolder(childFolderRel, path.join(absPath, siblingDir), ctx);
90
+ children.push({
91
+ kind: 'page',
92
+ name: knowledgeFileDisplayName(entry.name),
93
+ relativePath: fileRel,
94
+ slug: doc.slug,
95
+ title: doc.title,
96
+ status: doc.status,
97
+ type: doc.type,
98
+ contentKind: doc.contentKind,
99
+ childFolderPath: childFolderRel,
100
+ children: nested.children,
101
+ });
102
+ continue;
103
+ }
67
104
  children.push({
68
105
  kind: 'page',
69
106
  name: knowledgeFileDisplayName(entry.name),
@@ -81,6 +118,8 @@ function buildFolder(relativePath, absPath, ctx) {
81
118
  relativePath,
82
119
  emoji: catalogEmoji(ctx.meta, relativePath),
83
120
  indexPageSlug,
121
+ indexPageType,
122
+ indexPageContentKind,
84
123
  children,
85
124
  };
86
125
  }
@@ -107,42 +146,29 @@ export function buildDocsTree(index) {
107
146
  }
108
147
  export function findTreePageSlug(node, relativePath) {
109
148
  const normalized = normalizeRelativePath(relativePath);
110
- for (const child of node.children) {
111
- if (child.kind === 'page' && child.relativePath === normalized) {
112
- return child.slug;
113
- }
114
- if (child.kind === 'folder') {
115
- const found = findTreePageSlug(child, normalized);
116
- if (found) {
117
- return found;
118
- }
119
- }
120
- }
121
- return null;
122
- }
123
- export function collectParentOfEdges(tree) {
124
- if (!tree) {
125
- return [];
126
- }
127
- const edges = [];
128
- function walk(folder) {
129
- if (folder.indexPageSlug) {
130
- for (const child of folder.children ?? []) {
131
- if (child.kind === 'page' && child.slug !== folder.indexPageSlug) {
132
- edges.push({
133
- from: folder.indexPageSlug,
134
- to: child.slug,
135
- kind: 'parent_of',
136
- });
149
+ function search(nodes) {
150
+ for (const child of nodes) {
151
+ if (child.kind === 'page') {
152
+ if (child.relativePath === normalized) {
153
+ return child.slug;
137
154
  }
155
+ if (child.children) {
156
+ const nested = search(child.children);
157
+ if (nested) {
158
+ return nested;
159
+ }
160
+ }
161
+ continue;
138
162
  }
139
- }
140
- for (const child of folder.children ?? []) {
141
- if (child.kind === 'folder') {
142
- walk(child);
163
+ const nested = findTreePageSlug(child, normalized);
164
+ if (nested) {
165
+ return nested;
143
166
  }
144
167
  }
168
+ return null;
145
169
  }
146
- walk(tree);
147
- return edges;
170
+ return search(node.children);
171
+ }
172
+ export function collectParentOfEdges(_tree) {
173
+ return [];
148
174
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justyork/repo-mind",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Unified docs/ workspace for humans (UI) and AI agents (MCP) — Confluence in your repo",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,6 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@modelcontextprotocol/sdk": "^1.12.1",
40
+ "@tiptap/extension-bubble-menu": "^2.27.2",
40
41
  "better-sqlite3": "^12.11.1",
41
42
  "chokidar": "^4.0.3",
42
43
  "fast-glob": "^3.3.3",
@@ -61,6 +62,7 @@
61
62
  "@types/better-sqlite3": "^7.6.13",
62
63
  "@types/node": "^22.15.21",
63
64
  "d3": "^7.9.0",
65
+ "lucide": "^1.21.0",
64
66
  "marked": "^15.0.12",
65
67
  "typescript": "^5.8.3",
66
68
  "vite": "^6.3.5",
@@ -1 +1 @@
1
- import{$ as ln,a0 as an,a1 as G,a2 as q,a3 as z,a4 as un,a5 as y,a6 as tn,a7 as K,a8 as _,a9 as rn,aa as o,ab as on,ac as sn,ad as fn}from"./mermaid.core-C2wn2wM5.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,B,a){var O=I-l,i=D-h,n=B-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function V(l,h,I,D,v,A,B){var a=l-I,O=h-D,i=(B?A:-A)/K(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,C=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*K(on(0,T*T*R-P*P)),$=(P*g-m*S)/R,j=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=$-C,e=j-t,r=w-C,F=p-t;return x*x+e*e>r*r+F*F&&($=w,j=p),{cx:$,cy:j,x01:-n,y01:-d,x11:$*(v/T-1),y11:j*(v/T-1)}}function hn(){var l=cn,h=yn,I=z(0),D=null,v=gn,A=dn,B=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,C=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(C>tn-y)a.moveTo(s*G(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*G(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=C,S=C,$=B.apply(this,arguments)/2,j=$>y&&(D?+D.apply(this,arguments):K(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(j>y){var F=sn(j/u*q($)),L=sn(j/s*q($));(P-=F*2)>y?(F*=t?1:-1,R+=F,T-=F):(P=0,R=T=(f+c)/2),(S-=L*2)>y?(L*=t?1:-1,m+=L,g-=L):(S=0,m=g=(f+c)/2)}var H=s*G(m),J=s*q(m),M=u*G(T),N=u*q(T);if(w>y){var Q=s*G(g),U=s*q(g),W=u*G(R),X=u*q(R),E;if(C<an)if(E=pn(H,J,W,X,Q,U,M,N)){var Y=H-E[0],Z=J-E[1],b=Q-E[0],k=U-E[1],nn=1/q(fn((Y*b+Z*k)/(K(Y*Y+Z*Z)*K(b*b+k*k)))/2),en=K(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=V(W,X,H,J,s,x,t),r=V(Q,U,M,N,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(H,J),a.arc(0,0,s,m,g,!t)):a.moveTo(H,J),!(u>y)||!(P>y)?a.lineTo(M,N):p>y?(e=V(M,N,Q,U,u,-p,t),r=V(H,J,W,X,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[G(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:z(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:z(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:z(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:z(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:z(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:z(+n),i):A},i.padAngle=function(n){return arguments.length?(B=typeof n=="function"?n:z(+n),i):B},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{$ as ln,a0 as an,a1 as G,a2 as q,a3 as z,a4 as un,a5 as y,a6 as tn,a7 as K,a8 as _,a9 as rn,aa as o,ab as on,ac as sn,ad as fn}from"./mermaid.core-CIyMeT5H.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,B,a){var O=I-l,i=D-h,n=B-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function V(l,h,I,D,v,A,B){var a=l-I,O=h-D,i=(B?A:-A)/K(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,C=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*K(on(0,T*T*R-P*P)),$=(P*g-m*S)/R,j=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=$-C,e=j-t,r=w-C,F=p-t;return x*x+e*e>r*r+F*F&&($=w,j=p),{cx:$,cy:j,x01:-n,y01:-d,x11:$*(v/T-1),y11:j*(v/T-1)}}function hn(){var l=cn,h=yn,I=z(0),D=null,v=gn,A=dn,B=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,C=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(C>tn-y)a.moveTo(s*G(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*G(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=C,S=C,$=B.apply(this,arguments)/2,j=$>y&&(D?+D.apply(this,arguments):K(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(j>y){var F=sn(j/u*q($)),L=sn(j/s*q($));(P-=F*2)>y?(F*=t?1:-1,R+=F,T-=F):(P=0,R=T=(f+c)/2),(S-=L*2)>y?(L*=t?1:-1,m+=L,g-=L):(S=0,m=g=(f+c)/2)}var H=s*G(m),J=s*q(m),M=u*G(T),N=u*q(T);if(w>y){var Q=s*G(g),U=s*q(g),W=u*G(R),X=u*q(R),E;if(C<an)if(E=pn(H,J,W,X,Q,U,M,N)){var Y=H-E[0],Z=J-E[1],b=Q-E[0],k=U-E[1],nn=1/q(fn((Y*b+Z*k)/(K(Y*Y+Z*Z)*K(b*b+k*k)))/2),en=K(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=V(W,X,H,J,s,x,t),r=V(Q,U,M,N,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(H,J),a.arc(0,0,s,m,g,!t)):a.moveTo(H,J),!(u>y)||!(P>y)?a.lineTo(M,N):p>y?(e=V(M,N,Q,U,u,-p,t),r=V(H,J,W,X,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[G(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:z(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:z(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:z(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:z(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:z(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:z(+n),i):A},i.padAngle=function(n){return arguments.length?(B=typeof n=="function"?n:z(+n),i):B},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};