@justyork/repo-mind 0.7.2 → 0.8.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.
Files changed (76) hide show
  1. package/.cursor/skills/repomind-docs/SKILL.md +160 -0
  2. package/.cursor/skills/repomind-docs/examples.md +429 -0
  3. package/.cursor/skills/repomind-docs/reference.md +209 -0
  4. package/.cursor/skills/repomind-docs/structure.md +132 -0
  5. package/dist/cli.js +17 -0
  6. package/dist/commands/install-skill.d.ts +9 -0
  7. package/dist/commands/install-skill.js +35 -0
  8. package/dist/commands/ui.js +5 -1
  9. package/dist/ui/auth.d.ts +21 -0
  10. package/dist/ui/auth.js +117 -0
  11. package/dist/ui/fs-operations.d.ts +1 -1
  12. package/dist/ui/fs-operations.js +36 -4
  13. package/dist/ui/fs-tree.d.ts +5 -4
  14. package/dist/ui/fs-tree.js +60 -56
  15. package/dist/ui/server.d.ts +2 -0
  16. package/dist/ui/server.js +63 -2
  17. package/package.json +3 -2
  18. package/ui/dist/assets/{arc-C1R_fyW-.js → arc-D8hdO1rw.js} +1 -1
  19. package/ui/dist/assets/{architectureDiagram-3BPJPVTR-CcuF3FYc.js → architectureDiagram-3BPJPVTR-DD8y2xkc.js} +1 -1
  20. package/ui/dist/assets/{blockDiagram-GPEHLZMM-BZJKe3Ei.js → blockDiagram-GPEHLZMM-CC_b0LTp.js} +1 -1
  21. package/ui/dist/assets/{c4Diagram-AAUBKEIU-D_0fRNGS.js → c4Diagram-AAUBKEIU-D0xWINWL.js} +1 -1
  22. package/ui/dist/assets/channel-BlZV_pp1.js +1 -0
  23. package/ui/dist/assets/{chunk-2J33WTMH-0LcH4vIA.js → chunk-2J33WTMH-BU6b81sJ.js} +1 -1
  24. package/ui/dist/assets/{chunk-4BX2VUAB-DU1h67AD.js → chunk-4BX2VUAB-DZAlHkZW.js} +1 -1
  25. package/ui/dist/assets/{chunk-55IACEB6-BCx7dyYV.js → chunk-55IACEB6-AiC0yewf.js} +1 -1
  26. package/ui/dist/assets/{chunk-727SXJPM-CRO5yOrB.js → chunk-727SXJPM-DThb082L.js} +1 -1
  27. package/ui/dist/assets/{chunk-AQP2D5EJ-BayHdr67.js → chunk-AQP2D5EJ-BPfxgjqM.js} +1 -1
  28. package/ui/dist/assets/{chunk-FMBD7UC4-CJemLien.js → chunk-FMBD7UC4-BPN8p0X8.js} +1 -1
  29. package/ui/dist/assets/{chunk-ND2GUHAM-B_z-tw44.js → chunk-ND2GUHAM-Fg3EXATp.js} +1 -1
  30. package/ui/dist/assets/{chunk-QZHKN3VN-D40rAh_S.js → chunk-QZHKN3VN-BfNeOxQX.js} +1 -1
  31. package/ui/dist/assets/classDiagram-4FO5ZUOK-DeRilEwX.js +1 -0
  32. package/ui/dist/assets/classDiagram-v2-Q7XG4LA2-DeRilEwX.js +1 -0
  33. package/ui/dist/assets/{cose-bilkent-S5V4N54A-Cqthtrrq.js → cose-bilkent-S5V4N54A-BajfXpNb.js} +1 -1
  34. package/ui/dist/assets/{dagre-BM42HDAG-CrxsyXEa.js → dagre-BM42HDAG-BOr2m0CZ.js} +1 -1
  35. package/ui/dist/assets/{diagram-2AECGRRQ-DTYA7Rrn.js → diagram-2AECGRRQ-0NMPI4vh.js} +1 -1
  36. package/ui/dist/assets/{diagram-5GNKFQAL-CXs7RV13.js → diagram-5GNKFQAL-CTuUvbHc.js} +1 -1
  37. package/ui/dist/assets/{diagram-KO2AKTUF-Bf8EeV_S.js → diagram-KO2AKTUF-BQMPycKH.js} +1 -1
  38. package/ui/dist/assets/{diagram-LMA3HP47-C2XWB0cI.js → diagram-LMA3HP47-0EqSTR8-.js} +1 -1
  39. package/ui/dist/assets/{diagram-OG6HWLK6-BNMXob9h.js → diagram-OG6HWLK6-Cn7vgxcz.js} +1 -1
  40. package/ui/dist/assets/{erDiagram-TEJ5UH35-CnSczOvb.js → erDiagram-TEJ5UH35-DCHJaPi9.js} +1 -1
  41. package/ui/dist/assets/{flowDiagram-I6XJVG4X-DwZBJ2fr.js → flowDiagram-I6XJVG4X-GcCMnaHc.js} +1 -1
  42. package/ui/dist/assets/{ganttDiagram-6RSMTGT7-rxwvD8xn.js → ganttDiagram-6RSMTGT7-4NzDSonj.js} +1 -1
  43. package/ui/dist/assets/{gitGraphDiagram-PVQCEYII-DjJPiQ2K.js → gitGraphDiagram-PVQCEYII-BXLfDhC_.js} +1 -1
  44. package/ui/dist/assets/graph-CxZcS9dt.js +2 -0
  45. package/ui/dist/assets/{infoDiagram-5YYISTIA-BHgtQpBK.js → infoDiagram-5YYISTIA-DLHHvQ1b.js} +1 -1
  46. package/ui/dist/assets/{ishikawaDiagram-YF4QCWOH-BZ38ndde.js → ishikawaDiagram-YF4QCWOH-BtqY11Uk.js} +1 -1
  47. package/ui/dist/assets/{journeyDiagram-JHISSGLW-CNaZf5VN.js → journeyDiagram-JHISSGLW-lBwhxW7E.js} +1 -1
  48. package/ui/dist/assets/{kanban-definition-UN3LZRKU-4h1Jmt_X.js → kanban-definition-UN3LZRKU-C4Q8frPQ.js} +1 -1
  49. package/ui/dist/assets/main-BuVawP8Y.js +269 -0
  50. package/ui/dist/assets/{mermaid.core-BZZdRyuR.js → mermaid.core-DdiilVI8.js} +4 -4
  51. package/ui/dist/assets/{mindmap-definition-RKZ34NQL-DG_NciLX.js → mindmap-definition-RKZ34NQL-CzSxcpUz.js} +1 -1
  52. package/ui/dist/assets/{pieDiagram-4H26LBE5-B9FyPCLY.js → pieDiagram-4H26LBE5-VQ1Y2aMu.js} +1 -1
  53. package/ui/dist/assets/{quadrantDiagram-W4KKPZXB-B89WFfQu.js → quadrantDiagram-W4KKPZXB-DHxOr_Bb.js} +1 -1
  54. package/ui/dist/assets/{requirementDiagram-4Y6WPE33-BYHKePgB.js → requirementDiagram-4Y6WPE33-IruBFJXP.js} +1 -1
  55. package/ui/dist/assets/{sankeyDiagram-5OEKKPKP-BW6YuBQM.js → sankeyDiagram-5OEKKPKP-J4usxNW5.js} +1 -1
  56. package/ui/dist/assets/{sequenceDiagram-3UESZ5HK-BnojqKwf.js → sequenceDiagram-3UESZ5HK-C6C-kFaI.js} +1 -1
  57. package/ui/dist/assets/{stateDiagram-AJRCARHV-C_x9t2o6.js → stateDiagram-AJRCARHV-CJsdQK0K.js} +1 -1
  58. package/ui/dist/assets/stateDiagram-v2-BHNVJYJU-CnDT2Btz.js +1 -0
  59. package/ui/dist/assets/theme-BoL8lq0C.css +1 -0
  60. package/ui/dist/assets/theme-PLJ1jPdr.js +21 -0
  61. package/ui/dist/assets/{timeline-definition-PNZ67QCA-CI8Ngfzv.js → timeline-definition-PNZ67QCA-DR-akfNv.js} +1 -1
  62. package/ui/dist/assets/{vennDiagram-CIIHVFJN-CPRKGc2P.js → vennDiagram-CIIHVFJN-HohIp6U7.js} +1 -1
  63. package/ui/dist/assets/{visual-editor-DUVPRlY3.js → visual-editor-DeCtedTT.js} +12 -12
  64. package/ui/dist/assets/{wardley-L42UT6IY-CZY42jFT.js → wardley-L42UT6IY-DI0ohKT0.js} +1 -1
  65. package/ui/dist/assets/{wardleyDiagram-YWT4CUSO-CGKyWKnw.js → wardleyDiagram-YWT4CUSO-C1s7Fzl1.js} +1 -1
  66. package/ui/dist/assets/{xychartDiagram-2RQKCTM6-Cq-onAc3.js → xychartDiagram-2RQKCTM6-BbPEPR4c.js} +1 -1
  67. package/ui/dist/graph.html +3 -3
  68. package/ui/dist/index.html +3 -3
  69. package/ui/dist/assets/channel-BlWnHbuq.js +0 -1
  70. package/ui/dist/assets/classDiagram-4FO5ZUOK-D3OdfPqd.js +0 -1
  71. package/ui/dist/assets/classDiagram-v2-Q7XG4LA2-D3OdfPqd.js +0 -1
  72. package/ui/dist/assets/graph-D_tIaE_I.js +0 -2
  73. package/ui/dist/assets/main-BeHrzLrS.js +0 -273
  74. package/ui/dist/assets/stateDiagram-v2-BHNVJYJU-CBZx8gKT.js +0 -1
  75. package/ui/dist/assets/theme-CFHWfrn1.css +0 -1
  76. package/ui/dist/assets/theme-DS1fBM5L.js +0 -1
@@ -8,6 +8,7 @@ import { buildLinkIndexForDocs } from '../tools/explore-graph.js';
8
8
  import { readPageTemplate } from './templates.js';
9
9
  import { isValidFsName, joinRelativePath, normalizeRelativePath, parentRelativePath, resolveRelativeMdPath, resolveUnderKnowledgeRoot, } from './safe-path.js';
10
10
  import { cascadeSlugDelete, cascadeSlugRename, toRelativePaths } from './link-cascade.js';
11
+ import { readmeIndexRelativePath, siblingPageIndexRelativePath } from './fs-tree.js';
11
12
  function inferTypeFromParent(parentPath) {
12
13
  return inferTypeFromRelative(`${parentPath}/page.md`);
13
14
  }
@@ -115,7 +116,7 @@ function rewritePageFrontmatter(absolutePath, newRelativePath) {
115
116
  fs.writeFileSync(absolutePath, matter.stringify(parsed.content, nextData), 'utf8');
116
117
  return { newSlug, newType };
117
118
  }
118
- /** Creates sibling folder `{parent}/{basename}/` for a leaf page (Confluence-style); page file stays in place. */
119
+ /** Creates `{parent}/{basename}/README.md` from a leaf page file. */
119
120
  export function promotePageToFolder(index, pagePath) {
120
121
  const knowledgeRoot = index.getKnowledgeRoot();
121
122
  if (!knowledgeRoot) {
@@ -151,11 +152,34 @@ export function promotePageToFolder(index, pagePath) {
151
152
  else {
152
153
  fs.mkdirSync(folderAbsolute, { recursive: true });
153
154
  }
154
- const previousSlug = readPageSlug(sourceAbsolute);
155
+ const readmeRelative = readmeIndexRelativePath(folderRelative);
156
+ const readmeAbsolute = resolveRelativeMdPath(knowledgeRoot, readmeRelative);
157
+ if (!readmeAbsolute) {
158
+ throw new Error('path escapes docs/');
159
+ }
160
+ if (fs.existsSync(readmeAbsolute)) {
161
+ if (sourceAbsolute === readmeAbsolute) {
162
+ index.refresh();
163
+ const previousSlug = readPageSlug(readmeAbsolute);
164
+ return {
165
+ relativePath: readmeRelative,
166
+ absolutePath: readmeAbsolute,
167
+ slug: previousSlug,
168
+ previousSlug,
169
+ slugChanged: false,
170
+ inboundWarnings: [],
171
+ cascadeUpdated: [],
172
+ folderPath: normalizeRelativePath(folderRelative),
173
+ };
174
+ }
175
+ throw new Error(`folder index already exists: ${readmeRelative}`);
176
+ }
177
+ fs.renameSync(sourceAbsolute, readmeAbsolute);
178
+ const previousSlug = readPageSlug(readmeAbsolute);
155
179
  index.refresh();
156
180
  return {
157
- relativePath: normalizedFrom,
158
- absolutePath: sourceAbsolute,
181
+ relativePath: readmeRelative,
182
+ absolutePath: readmeAbsolute,
159
183
  slug: previousSlug,
160
184
  previousSlug,
161
185
  slugChanged: false,
@@ -422,7 +446,12 @@ export function deleteFolder(index, folderPath) {
422
446
  throw new Error(`not a folder: ${folderPath}`);
423
447
  }
424
448
  const markdownFiles = listMarkdownFilesRecursive(absolutePath);
449
+ const siblingRelative = siblingPageIndexRelativePath(normalized);
450
+ const siblingAbsolute = siblingRelative === null ? null : resolveRelativeMdPath(knowledgeRoot, siblingRelative);
425
451
  const deletedSlugs = markdownFiles.map((filePath) => readPageSlug(filePath));
452
+ if (siblingAbsolute && fs.existsSync(siblingAbsolute)) {
453
+ deletedSlugs.push(readPageSlug(siblingAbsolute));
454
+ }
426
455
  const inboundWarnings = mergeInboundWarnings(deletedSlugs.map((slug) => collectInboundWarnings(index, slug)));
427
456
  const cascadeUpdated = new Set();
428
457
  for (const deletedSlug of deletedSlugs) {
@@ -433,6 +462,9 @@ export function deleteFolder(index, folderPath) {
433
462
  }
434
463
  }
435
464
  fs.rmSync(absolutePath, { recursive: true, force: true });
465
+ if (siblingAbsolute && fs.existsSync(siblingAbsolute)) {
466
+ fs.unlinkSync(siblingAbsolute);
467
+ }
436
468
  index.refresh();
437
469
  return {
438
470
  relativePath: normalized,
@@ -9,23 +9,24 @@ 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[];
15
12
  }
16
13
  export interface TreeFolderNode {
17
14
  kind: 'folder';
18
15
  name: string;
19
16
  relativePath: string;
20
17
  emoji: string | null;
21
- /** Slug of README.md index page for this folder. */
18
+ /** Slug of the folder index page (README.md or legacy sibling page). */
22
19
  indexPageSlug: string | null;
20
+ indexPageTitle: string | null;
21
+ indexPageRelativePath: string | null;
23
22
  indexPageType: string | null;
24
23
  indexPageContentKind: 'markdown' | 'yaml' | 'json' | null;
25
24
  children: TreeNode[];
26
25
  }
27
26
  export type TreeNode = TreePageNode | TreeFolderNode;
28
27
  export declare function readmeIndexRelativePath(folderRelativePath: string): string;
28
+ /** Legacy index path: `{parent}/{folderName}.md` next to `{folderName}/`. */
29
+ export declare function siblingPageIndexRelativePath(folderRelativePath: string): string | null;
29
30
  /** Human label for domain folders at `docs/{domain}/`; otherwise basename. */
30
31
  export declare function folderDisplayName(relativePath: string): string;
31
32
  export declare function buildDocsTree(index: DocIndex): TreeFolderNode | null;
@@ -3,25 +3,11 @@ import path from 'node:path';
3
3
  import { isKnowledgeFileName } from '../index/knowledge-file.js';
4
4
  import { DOC_DOMAINS, DOMAIN_LABELS } from '../index/types.js';
5
5
  import { catalogEmoji, readCatalogMeta } from './catalog-meta.js';
6
- import { joinRelativePath, normalizeRelativePath } from './safe-path.js';
6
+ import { joinRelativePath, normalizeRelativePath, parentRelativePath } from './safe-path.js';
7
7
  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
- }
25
11
  function listDirEntries(dirPath) {
26
12
  if (!fs.existsSync(dirPath)) {
27
13
  return [];
@@ -35,6 +21,50 @@ export function readmeIndexRelativePath(folderRelativePath) {
35
21
  const base = normalizeRelativePath(folderRelativePath);
36
22
  return base ? `${base}/README.md` : 'README.md';
37
23
  }
24
+ /** Legacy index path: `{parent}/{folderName}.md` next to `{folderName}/`. */
25
+ export function siblingPageIndexRelativePath(folderRelativePath) {
26
+ const base = normalizeRelativePath(folderRelativePath);
27
+ if (!base) {
28
+ return null;
29
+ }
30
+ const folderName = path.basename(base);
31
+ return joinRelativePath(parentRelativePath(base), `${folderName}.md`);
32
+ }
33
+ function resolveFolderIndex(relativePath, ctx) {
34
+ const empty = {
35
+ indexPageSlug: null,
36
+ indexPageTitle: null,
37
+ indexPageRelativePath: null,
38
+ indexPageType: null,
39
+ indexPageContentKind: null,
40
+ };
41
+ const readmeRel = readmeIndexRelativePath(relativePath);
42
+ const readmeDoc = ctx.docsByRelative.get(readmeRel);
43
+ if (readmeDoc) {
44
+ return {
45
+ indexPageSlug: readmeDoc.slug,
46
+ indexPageTitle: readmeDoc.title,
47
+ indexPageRelativePath: readmeRel,
48
+ indexPageType: readmeDoc.type,
49
+ indexPageContentKind: readmeDoc.contentKind,
50
+ };
51
+ }
52
+ const siblingRel = siblingPageIndexRelativePath(relativePath);
53
+ if (!siblingRel) {
54
+ return empty;
55
+ }
56
+ const siblingDoc = ctx.docsByRelative.get(siblingRel);
57
+ if (!siblingDoc) {
58
+ return empty;
59
+ }
60
+ return {
61
+ indexPageSlug: siblingDoc.slug,
62
+ indexPageTitle: siblingDoc.title,
63
+ indexPageRelativePath: siblingRel,
64
+ indexPageType: siblingDoc.type,
65
+ indexPageContentKind: siblingDoc.contentKind,
66
+ };
67
+ }
38
68
  function isDocDomain(name) {
39
69
  return DOC_DOMAINS.includes(name);
40
70
  }
@@ -52,25 +82,12 @@ export function folderDisplayName(relativePath) {
52
82
  function buildFolder(relativePath, absPath, ctx) {
53
83
  const entries = listDirEntries(absPath);
54
84
  const children = [];
55
- const dirNames = entries.filter((entry) => entry.isDirectory).map((entry) => entry.name);
56
- const readmeRel = readmeIndexRelativePath(relativePath);
57
- let indexPageSlug = null;
58
- let indexPageType = null;
59
- let indexPageContentKind = null;
60
- const readmeDoc = ctx.docsByRelative.get(readmeRel);
61
- if (readmeDoc) {
62
- indexPageSlug = readmeDoc.slug;
63
- indexPageType = readmeDoc.type;
64
- indexPageContentKind = readmeDoc.contentKind;
65
- }
85
+ const folderIndex = resolveFolderIndex(relativePath, ctx);
66
86
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
67
87
  if (entry.isDirectory) {
68
88
  if (IGNORED_DIRS.has(entry.name)) {
69
89
  continue;
70
90
  }
71
- if (hasSiblingKnowledgePage(entries, entry.name)) {
72
- continue;
73
- }
74
91
  const childRel = joinRelativePath(relativePath, entry.name);
75
92
  children.push(buildFolder(childRel, path.join(absPath, entry.name), ctx));
76
93
  continue;
@@ -79,26 +96,11 @@ function buildFolder(relativePath, absPath, ctx) {
79
96
  continue;
80
97
  }
81
98
  const fileRel = joinRelativePath(relativePath, entry.name);
82
- const doc = ctx.docsByRelative.get(fileRel);
83
- if (!doc) {
99
+ if (fileRel === folderIndex.indexPageRelativePath) {
84
100
  continue;
85
101
  }
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
+ const doc = ctx.docsByRelative.get(fileRel);
103
+ if (!doc) {
102
104
  continue;
103
105
  }
104
106
  children.push({
@@ -117,9 +119,11 @@ function buildFolder(relativePath, absPath, ctx) {
117
119
  name: folderDisplayName(relativePath),
118
120
  relativePath,
119
121
  emoji: catalogEmoji(ctx.meta, relativePath),
120
- indexPageSlug,
121
- indexPageType,
122
- indexPageContentKind,
122
+ indexPageSlug: folderIndex.indexPageSlug,
123
+ indexPageTitle: folderIndex.indexPageTitle,
124
+ indexPageRelativePath: folderIndex.indexPageRelativePath,
125
+ indexPageType: folderIndex.indexPageType,
126
+ indexPageContentKind: folderIndex.indexPageContentKind,
123
127
  children,
124
128
  };
125
129
  }
@@ -152,14 +156,11 @@ export function findTreePageSlug(node, relativePath) {
152
156
  if (child.relativePath === normalized) {
153
157
  return child.slug;
154
158
  }
155
- if (child.children) {
156
- const nested = search(child.children);
157
- if (nested) {
158
- return nested;
159
- }
160
- }
161
159
  continue;
162
160
  }
161
+ if (child.indexPageRelativePath === normalized && child.indexPageSlug) {
162
+ return child.indexPageSlug;
163
+ }
163
164
  const nested = findTreePageSlug(child, normalized);
164
165
  if (nested) {
165
166
  return nested;
@@ -167,6 +168,9 @@ export function findTreePageSlug(node, relativePath) {
167
168
  }
168
169
  return null;
169
170
  }
171
+ if (node.indexPageRelativePath === normalized && node.indexPageSlug) {
172
+ return node.indexPageSlug;
173
+ }
170
174
  return search(node.children);
171
175
  }
172
176
  export function collectParentOfEdges(_tree) {
@@ -1,5 +1,6 @@
1
1
  import http from 'node:http';
2
2
  import type { DocIndex } from '../index/doc-index.js';
3
+ import type { UiAuth } from './auth.js';
3
4
  import type { DocsWatcher } from './docs-watcher.js';
4
5
  import type { DraftsDb } from './db/drafts-db.js';
5
6
  export interface UiServerOptions {
@@ -9,6 +10,7 @@ export interface UiServerOptions {
9
10
  staticDir: string;
10
11
  draftsDb?: DraftsDb;
11
12
  docsWatcher?: DocsWatcher;
13
+ auth?: UiAuth | null;
12
14
  }
13
15
  export declare function createUiServer(options: UiServerOptions): http.Server;
14
16
  export declare function resolveUiStaticDir(): string;
package/dist/ui/server.js CHANGED
@@ -3,6 +3,7 @@ import http from 'node:http';
3
3
  import path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { routeApi, handleDocsEvents } from './api-handlers.js';
6
+ import { handleAuthApi } from './auth.js';
6
7
  import { handleDraftApi } from './draft-api.js';
7
8
  import { serveKnowledgeAsset } from './serve-asset.js';
8
9
  import { handleAssetUpload, MAX_ASSET_UPLOAD_BYTES } from './upload-asset.js';
@@ -44,14 +45,25 @@ function resolveStaticPath(staticDir, requestPath) {
44
45
  }
45
46
  return resolvedFile;
46
47
  }
47
- function sendJson(res, status, body) {
48
+ function sendJson(res, status, body, extraHeaders = {}) {
48
49
  const payload = JSON.stringify(body);
49
50
  res.writeHead(status, {
50
51
  'Content-Type': 'application/json; charset=utf-8',
51
52
  'Content-Length': Buffer.byteLength(payload),
53
+ ...extraHeaders,
52
54
  });
53
55
  res.end(payload);
54
56
  }
57
+ function sendAuthJson(res, auth, response) {
58
+ const headers = {};
59
+ if (response.setCookie) {
60
+ headers['Set-Cookie'] = response.setCookie;
61
+ }
62
+ else if (response.clearCookie) {
63
+ headers['Set-Cookie'] = auth.clearSessionCookie();
64
+ }
65
+ sendJson(res, response.status, response.body, headers);
66
+ }
55
67
  function readBody(req) {
56
68
  return new Promise((resolve, reject) => {
57
69
  const chunks = [];
@@ -94,11 +106,60 @@ function serveStatic(res, staticDir, requestPath) {
94
106
  }
95
107
  export function createUiServer(options) {
96
108
  const host = options.host ?? '127.0.0.1';
97
- const { staticDir, index, draftsDb, docsWatcher } = options;
109
+ const { staticDir, index, draftsDb, docsWatcher, auth = null } = options;
98
110
  return http.createServer((req, res) => {
99
111
  void (async () => {
100
112
  const method = req.method ?? 'GET';
101
113
  const urlPath = new URL(req.url ?? '/', `http://${host}`).pathname;
114
+ if (urlPath.startsWith('/api/auth/')) {
115
+ if (!auth) {
116
+ if (urlPath === '/api/auth/session' && method === 'GET') {
117
+ sendJson(res, 200, { authenticated: true, required: false });
118
+ return;
119
+ }
120
+ sendJson(res, 404, { error: 'not found' });
121
+ return;
122
+ }
123
+ let bodyRaw = '';
124
+ if (method === 'POST') {
125
+ try {
126
+ bodyRaw = await readBody(req);
127
+ }
128
+ catch (error) {
129
+ const message = error instanceof Error ? error.message : 'bad request';
130
+ sendJson(res, 400, { error: message });
131
+ return;
132
+ }
133
+ }
134
+ const authResponse = handleAuthApi(auth, method, urlPath, bodyRaw, req.headers);
135
+ if (authResponse) {
136
+ sendAuthJson(res, auth, authResponse);
137
+ return;
138
+ }
139
+ sendJson(res, 404, { error: 'not found' });
140
+ return;
141
+ }
142
+ if (auth && !auth.isAuthenticated(req.headers)) {
143
+ if (urlPath.startsWith('/api/')) {
144
+ sendJson(res, 401, { error: 'authentication required' });
145
+ return;
146
+ }
147
+ if (method !== 'GET' && method !== 'HEAD') {
148
+ sendJson(res, 401, { error: 'authentication required' });
149
+ return;
150
+ }
151
+ if (method === 'HEAD') {
152
+ res.writeHead(401);
153
+ res.end();
154
+ return;
155
+ }
156
+ if (!serveStatic(res, staticDir, urlPath)) {
157
+ sendJson(res, 503, {
158
+ error: 'UI assets not found — run `npm run build:ui` in the repo-mind package',
159
+ });
160
+ }
161
+ return;
162
+ }
102
163
  if (urlPath.startsWith('/api/')) {
103
164
  if (urlPath === '/api/assets/upload' && method === 'POST') {
104
165
  const knowledgeRoot = index.getKnowledgeRoot();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justyork/repo-mind",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Unified docs/ workspace for humans (UI) and AI agents (MCP) — Confluence in your repo",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "files": [
10
10
  "dist",
11
11
  "ui/dist",
12
- "templates"
12
+ "templates",
13
+ ".cursor/skills/repomind-docs"
13
14
  ],
14
15
  "scripts": {
15
16
  "build": "tsc && npm run build:ui",
@@ -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-BZZdRyuR.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-DdiilVI8.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};