noteconnection 1.7.0 → 1.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 (211) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +242 -62
  3. package/dist/src/agent_workspace.contract.parity.test.js +475 -0
  4. package/dist/src/agent_workspace.frontend.test.js +9989 -0
  5. package/dist/src/agent_workspace.locale.contract.test.js +95 -0
  6. package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
  7. package/dist/src/copy.assets.contract.test.js +130 -0
  8. package/dist/src/core/PathBridge.js +28 -3
  9. package/dist/src/export/WorkspaceExportBundle.js +1130 -0
  10. package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
  11. package/dist/src/export/index.js +18 -0
  12. package/dist/src/export/types.js +2 -0
  13. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  14. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  15. package/dist/src/foundation.release.evidence.contract.test.js +406 -0
  16. package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
  17. package/dist/src/frontend/README.md +242 -62
  18. package/dist/src/frontend/agent_workspace.js +5353 -0
  19. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  20. package/dist/src/frontend/analysis.js +31 -4
  21. package/dist/src/frontend/app.js +2178 -62
  22. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  23. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  24. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  25. package/dist/src/frontend/graph_state.mjs +105 -0
  26. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  27. package/dist/src/frontend/i18n.mjs +186 -0
  28. package/dist/src/frontend/index.html +641 -266
  29. package/dist/src/frontend/layout_gpu.js +12 -4
  30. package/dist/src/frontend/locales/en.json +721 -5
  31. package/dist/src/frontend/locales/zh.json +721 -5
  32. package/dist/src/frontend/main.mjs +60 -0
  33. package/dist/src/frontend/markdown_runtime.js +827 -0
  34. package/dist/src/frontend/notemd.css +49 -0
  35. package/dist/src/frontend/notemd.html +7 -1
  36. package/dist/src/frontend/notemd.js +64 -0
  37. package/dist/src/frontend/path.html +107 -0
  38. package/dist/src/frontend/path_app.js +2108 -130
  39. package/dist/src/frontend/path_layout.mjs +143 -0
  40. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  41. package/dist/src/frontend/path_modules_bridge.js +486 -0
  42. package/dist/src/frontend/path_state.mjs +118 -0
  43. package/dist/src/frontend/path_styles.css +146 -0
  44. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  45. package/dist/src/frontend/reader.js +393 -33
  46. package/dist/src/frontend/runtime_bridge.js +67 -54
  47. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  48. package/dist/src/frontend/settings.js +130 -12
  49. package/dist/src/frontend/source_manager.js +190 -21
  50. package/dist/src/frontend/styles.css +2720 -72
  51. package/dist/src/frontend/workbench_state.mjs +101 -0
  52. package/dist/src/frontend/workspace_panes.js +10168 -0
  53. package/dist/src/frontend.locale.contract.test.js +62 -0
  54. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  55. package/dist/src/indexing/IndexLifecycle.js +195 -0
  56. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  57. package/dist/src/indexing/SegmentBuilder.js +64 -0
  58. package/dist/src/indexing/UnitBuilder.js +48 -0
  59. package/dist/src/indexing/types.js +2 -0
  60. package/dist/src/knowledge.api.contract.test.js +170 -0
  61. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  65. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  67. package/dist/src/learning/answerReleaseReview.js +4319 -0
  68. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  69. package/dist/src/learning/api.js +2 -0
  70. package/dist/src/learning/conversationComposer.js +1480 -0
  71. package/dist/src/learning/conversationComposer.test.js +1817 -0
  72. package/dist/src/learning/domains/ConversationManager.js +53 -0
  73. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  74. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  75. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  76. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  77. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  78. package/dist/src/learning/domains/TutorRouter.js +313 -0
  79. package/dist/src/learning/domains/index.js +33 -0
  80. package/dist/src/learning/domains/types.js +7 -0
  81. package/dist/src/learning/errors.js +29 -0
  82. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  83. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  84. package/dist/src/learning/graphContextAssembler.js +870 -0
  85. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  86. package/dist/src/learning/index.js +28 -0
  87. package/dist/src/learning/queryBackend.js +1898 -0
  88. package/dist/src/learning/queryBackend.test.js +955 -0
  89. package/dist/src/learning/ragContextPack.js +257 -0
  90. package/dist/src/learning/ragContextPack.test.js +160 -0
  91. package/dist/src/learning/ragPublicText.js +38 -0
  92. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  93. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  94. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  96. package/dist/src/learning/requestNormalization.js +198 -0
  97. package/dist/src/learning/runtimeCapability.js +4677 -0
  98. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  99. package/dist/src/learning/store.js +1240 -0
  100. package/dist/src/learning/store.test.js +1126 -0
  101. package/dist/src/learning/tutorAdapter.js +2 -0
  102. package/dist/src/learning/types.js +2 -0
  103. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  105. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  106. package/dist/src/license.policy.contract.test.js +66 -0
  107. package/dist/src/memory/MemoryGovernance.js +74 -0
  108. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  109. package/dist/src/memory/types.js +2 -0
  110. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  111. package/dist/src/middleware/auth.js +17 -0
  112. package/dist/src/middleware/body-parser.js +45 -0
  113. package/dist/src/middleware/cors.js +44 -0
  114. package/dist/src/middleware/index.js +21 -0
  115. package/dist/src/middleware/request-trace.js +96 -0
  116. package/dist/src/notemd/AppConfigToml.js +6 -4
  117. package/dist/src/notemd/MermaidProcessor.js +400 -50
  118. package/dist/src/notemd/NotemdService.js +498 -13
  119. package/dist/src/notemd/PromptManager.js +15 -0
  120. package/dist/src/notemd/cli/commands.js +357 -0
  121. package/dist/src/notemd/cli/dispatcher.js +225 -0
  122. package/dist/src/notemd/cli/index.js +169 -0
  123. package/dist/src/notemd/cli/parser.js +68 -0
  124. package/dist/src/notemd/cli/types.js +2 -0
  125. package/dist/src/notemd/constants.js +43 -0
  126. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  127. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  128. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  129. package/dist/src/notemd/diagram/intent.js +95 -0
  130. package/dist/src/notemd/diagram/planner.js +71 -0
  131. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  132. package/dist/src/notemd/diagram/types.js +18 -0
  133. package/dist/src/notemd/index.js +26 -0
  134. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  135. package/dist/src/notemd/operations/cliContracts.js +17 -0
  136. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  137. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  138. package/dist/src/notemd/operations/registry.js +991 -0
  139. package/dist/src/notemd/operations/types.js +2 -0
  140. package/dist/src/notemd/providerDiagnostics.js +220 -0
  141. package/dist/src/notemd/providerProfiles.js +42 -0
  142. package/dist/src/notemd/providerTemplates.js +231 -0
  143. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  144. package/dist/src/notemd/search/SearchManager.js +13 -0
  145. package/dist/src/notemd/search/SearchProvider.js +2 -0
  146. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  147. package/dist/src/notemd.agent.manifest.test.js +85 -0
  148. package/dist/src/notemd.api.contract.test.js +14 -1
  149. package/dist/src/notemd.app_config_toml.test.js +2 -1
  150. package/dist/src/notemd.batch.workflow.test.js +117 -0
  151. package/dist/src/notemd.cli.e2e.test.js +136 -0
  152. package/dist/src/notemd.core.test.js +51 -0
  153. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  154. package/dist/src/notemd.providerTemplates.test.js +34 -0
  155. package/dist/src/notemd.server.integration.test.js +143 -35
  156. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  157. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  158. package/dist/src/pathmode.background.contract.test.js +69 -0
  159. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  160. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  161. package/dist/src/platform/ExportProfile.js +58 -0
  162. package/dist/src/platform/PlatformCapabilities.js +45 -0
  163. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  164. package/dist/src/platform/RenderMaterializer.js +33 -0
  165. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  166. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  167. package/dist/src/reader_renderer.js +404 -3
  168. package/dist/src/reader_renderer.test.js +87 -0
  169. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  170. package/dist/src/resources/ResourceRegistry.js +223 -0
  171. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  172. package/dist/src/resources/types.js +2 -0
  173. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  174. package/dist/src/routes/data.js +267 -0
  175. package/dist/src/routes/diagnostics.js +51 -0
  176. package/dist/src/routes/index.js +23 -0
  177. package/dist/src/routes/knowledge.js +968 -0
  178. package/dist/src/routes/markdown.js +287 -0
  179. package/dist/src/routes/notemd.js +565 -0
  180. package/dist/src/routes/registry.contract.test.js +130 -0
  181. package/dist/src/routes/render.js +285 -0
  182. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  184. package/dist/src/routes/settings.js +6 -0
  185. package/dist/src/routes/staticFiles.js +94 -0
  186. package/dist/src/routes/types.js +2 -0
  187. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  188. package/dist/src/server.js +11090 -1549
  189. package/dist/src/server.migration.test.js +193 -21
  190. package/dist/src/server.port.fallback.contract.test.js +63 -0
  191. package/dist/src/session/SessionStateStore.js +81 -0
  192. package/dist/src/session/SessionStateStore.test.js +58 -0
  193. package/dist/src/session/types.js +2 -0
  194. package/dist/src/settings.runtime.contract.test.js +50 -0
  195. package/dist/src/shared/types.contract.test.js +107 -0
  196. package/dist/src/shared/types.js +22 -0
  197. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  198. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  199. package/dist/src/source_manager.loadflow.test.js +46 -0
  200. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  201. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  202. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  203. package/dist/src/utils/RuntimePaths.js +4 -13
  204. package/dist/src/utils/platform.js +153 -0
  205. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  207. package/dist/src/workflows/types.js +2 -0
  208. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  209. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  210. package/dist/src/workspace/types.js +2 -0
  211. package/package.json +53 -10
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Agent Manifest contract test — validates that the NotemdAgentManifest
5
+ * endpoint produces correct data for Agent Workspace consumption.
6
+ */
7
+ const registry_1 = require("./notemd/operations/registry");
8
+ function buildAgentManifest() {
9
+ const defs = (0, registry_1.listOperationDefinitions)();
10
+ const operations = defs.map(def => ({
11
+ operationId: def.id,
12
+ description: def.commandBindings[0]?.commandId ?? def.id,
13
+ automationLevel: def.automationLevel,
14
+ requiredContext: def.requiredContext,
15
+ sideEffectClass: def.sideEffectClass,
16
+ agentAutoExecutable: def.automationLevel === 'safe',
17
+ requiredParams: def.inputSchema
18
+ ? (def.inputSchema.required ?? [])
19
+ : [],
20
+ }));
21
+ return {
22
+ version: 1,
23
+ generatedAt: new Date().toISOString(),
24
+ totalOperations: operations.length,
25
+ agentExecutableCount: operations.filter(o => o.agentAutoExecutable).length,
26
+ operations,
27
+ };
28
+ }
29
+ describe('NotemdAgentManifest', () => {
30
+ let manifest;
31
+ beforeAll(() => {
32
+ manifest = buildAgentManifest();
33
+ });
34
+ test('manifest version is 1', () => {
35
+ expect(manifest.version).toBe(1);
36
+ });
37
+ test('generatedAt is a valid ISO date string', () => {
38
+ expect(() => new Date(manifest.generatedAt)).not.toThrow();
39
+ expect(new Date(manifest.generatedAt).getTime()).toBeGreaterThan(0);
40
+ });
41
+ test('totalOperations matches operations array length', () => {
42
+ expect(manifest.totalOperations).toBe(manifest.operations.length);
43
+ });
44
+ test('all operations are present (27 with workflow.batch)', () => {
45
+ expect(manifest.operations.length).toBe(27);
46
+ });
47
+ test('agentExecutableCount matches auto-executable count', () => {
48
+ const count = manifest.operations.filter(o => o.agentAutoExecutable).length;
49
+ expect(manifest.agentExecutableCount).toBe(count);
50
+ });
51
+ test('safe operations are agent-auto-executable', () => {
52
+ const safeOps = manifest.operations.filter(o => o.automationLevel === 'safe');
53
+ expect(safeOps.length).toBeGreaterThan(0);
54
+ safeOps.forEach(op => {
55
+ expect(op.agentAutoExecutable).toBe(true);
56
+ });
57
+ });
58
+ test('every operation has an operationId', () => {
59
+ manifest.operations.forEach(op => {
60
+ expect(typeof op.operationId).toBe('string');
61
+ expect(op.operationId.length).toBeGreaterThan(0);
62
+ });
63
+ });
64
+ test('every operation has a valid automation level', () => {
65
+ const valid = ['safe', 'requires-active-file', 'requires-selection', 'interactive-ui'];
66
+ manifest.operations.forEach(op => {
67
+ expect(valid).toContain(op.automationLevel);
68
+ });
69
+ });
70
+ test('interactive-ui operations are NOT agent-auto-executable', () => {
71
+ const uiOps = manifest.operations.filter(o => o.automationLevel === 'interactive-ui');
72
+ uiOps.forEach(op => {
73
+ expect(op.agentAutoExecutable).toBe(false);
74
+ });
75
+ });
76
+ test('known critical operations are present', () => {
77
+ const ids = manifest.operations.map(o => o.operationId);
78
+ expect(ids).toContain('file.process-add-links');
79
+ expect(ids).toContain('diagram.generate');
80
+ expect(ids).toContain('translate.file');
81
+ expect(ids).toContain('provider.diagnostic.run');
82
+ expect(ids).toContain('workflow.extract-and-generate');
83
+ expect(ids).toContain('mermaid.batch-fix');
84
+ });
85
+ });
@@ -37,7 +37,12 @@ const fs = __importStar(require("fs"));
37
37
  const path = __importStar(require("path"));
38
38
  describe('NoteMD API contract wiring', () => {
39
39
  const serverSourcePath = path.join(__dirname, 'server.ts');
40
+ const routesSourcePath = path.join(__dirname, 'routes', 'notemd.ts');
40
41
  const serverSource = fs.readFileSync(serverSourcePath, 'utf8');
42
+ const routesSource = fs.existsSync(routesSourcePath) ? fs.readFileSync(routesSourcePath, 'utf8') : '';
43
+ function endpointExistsInSource(endpoint) {
44
+ return serverSource.includes(endpoint) || routesSource.includes(endpoint);
45
+ }
41
46
  test('server exposes all planned /api/notemd endpoints', () => {
42
47
  const endpoints = [
43
48
  '/api/notemd/settings',
@@ -52,9 +57,17 @@ describe('NoteMD API contract wiring', () => {
52
57
  '/api/notemd/check-duplicates',
53
58
  '/api/notemd/extract-concepts',
54
59
  '/api/notemd/cancel',
60
+ // ── obsidian-notemd v1.8.4 additions ──
61
+ '/api/notemd/generate-diagram',
62
+ '/api/notemd/preview-diagram',
63
+ '/api/notemd/export-diagram',
64
+ '/api/notemd/search',
65
+ '/api/notemd/progress',
66
+ '/api/notemd/diagnose-llm',
67
+ '/api/notemd/extract-original-text',
55
68
  ];
56
69
  endpoints.forEach((endpoint) => {
57
- expect(serverSource).toContain(endpoint);
70
+ expect(endpointExistsInSource(endpoint)).toBe(true);
58
71
  });
59
72
  });
60
73
  test('server persists NoteMD settings through app_config.toml helpers', () => {
@@ -77,6 +77,7 @@ describe('app_config.toml adapters', () => {
77
77
  expect(extracted.workspaceOutputFolderPath).toBe('E:/Knowledge_Base/math/topic');
78
78
  });
79
79
  test('path_mode settings are normalized and clamped', () => {
80
+ expect(AppConfigToml_1.DEFAULT_PATH_MODE_SETTINGS.background).toBe('');
80
81
  const nextConfig = (0, AppConfigToml_1.applyPathModeSettingsToAppConfig)({}, {
81
82
  ...AppConfigToml_1.DEFAULT_PATH_MODE_SETTINGS,
82
83
  bg_brightness: 99,
@@ -86,7 +87,7 @@ describe('app_config.toml adapters', () => {
86
87
  reader_render_mode: 'invalid',
87
88
  });
88
89
  const extracted = (0, AppConfigToml_1.extractPathModeSettingsFromAppConfig)(nextConfig);
89
- expect(extracted.bg_brightness).toBe(10);
90
+ expect(extracted.bg_brightness).toBe(0.1);
90
91
  expect(extracted.reader_media_scale).toBe(0.1);
91
92
  expect(extracted.node_spacing).toBe(600);
92
93
  expect(extracted.reading_mode).toBe('window');
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ /**
37
+ * Batch workflow contract test — validates request structure,
38
+ * file filtering, and error handling for runBatchWorkflow.
39
+ */
40
+ const fs = __importStar(require("fs"));
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ const NotemdService_1 = require("./notemd/NotemdService");
44
+ class TempDir {
45
+ constructor(prefix) { this.root = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), prefix)); }
46
+ file(rel, content) {
47
+ const p = path.join(this.root, rel);
48
+ fs.mkdirSync(path.dirname(p), { recursive: true });
49
+ fs.writeFileSync(p, content, 'utf8');
50
+ return p;
51
+ }
52
+ cleanup() { fs.rmSync(this.root, { recursive: true, force: true }); }
53
+ }
54
+ function defaultSettings() {
55
+ return {
56
+ activeProvider: 'OpenAI', searchProvider: 'duckduckgo', language: 'en',
57
+ providers: [], enableStableApiCall: true, enableApiErrorDebugMode: false,
58
+ maxRetries: 0, retryDelayMs: 0, maxTokens: 256, chunkWordCount: 100,
59
+ enableDuplicateDetection: false, enableExperimentalDiagramPipeline: false,
60
+ enableBatchParallelism: false, developerMode: false,
61
+ developerDiagnosticCallMode: 'runtime-stable', developerDiagnosticTimeoutMs: 60000,
62
+ developerDiagnosticStabilityRuns: 3,
63
+ experimentalDiagramCompatibilityMode: 'legacy-mermaid',
64
+ autoMermaidFixAfterGenerate: true, tavilyApiKey: '', tavilyMaxResults: 5,
65
+ tavilySearchDepth: 'basic', ddgFetchTimeout: 10000,
66
+ useCustomConceptNoteFolder: false, conceptNoteFolder: '',
67
+ };
68
+ }
69
+ describe('Notemd Batch Workflow', () => {
70
+ let temp;
71
+ let service;
72
+ beforeEach(() => { temp = new TempDir('notemd-batch-'); service = new NotemdService_1.NotemdService(); });
73
+ afterEach(() => { temp.cleanup(); });
74
+ test('runBatchWorkflow processes matching .md files in a folder', async () => {
75
+ temp.file('notes/a.md', '# Note A\n\nContent A.');
76
+ temp.file('notes/b.md', '# Note B\n\nContent B.');
77
+ temp.file('notes/c.txt', '# Note C\n\nContent C.');
78
+ const folder = path.join(temp.root, 'notes');
79
+ const result = await service.runBatchWorkflow({ folderPath: folder, fileExtensions: ['.md'] }, defaultSettings());
80
+ expect(result.totalFiles).toBe(2);
81
+ expect(result.completedFiles).toBeGreaterThanOrEqual(0);
82
+ expect(result.failedFiles).toBeLessThanOrEqual(2);
83
+ expect(result.filter?.extensions).toEqual(['.md']);
84
+ expect(fs.existsSync(result.outputBasePath)).toBe(true);
85
+ expect(result.totalElapsedMs).toBeGreaterThan(0);
86
+ });
87
+ test('runBatchWorkflow filters by regex pattern', async () => {
88
+ temp.file('notes/lecture-01.md', '# L1');
89
+ temp.file('notes/lecture-02.md', '# L2');
90
+ temp.file('notes/other.md', '# Other');
91
+ const result = await service.runBatchWorkflow({ folderPath: path.join(temp.root, 'notes'), filePattern: 'lecture-.*' }, defaultSettings());
92
+ expect(result.totalFiles).toBe(2);
93
+ expect(result.filter?.pattern).toBe('lecture-.*');
94
+ });
95
+ test('runBatchWorkflow respects maxFiles limit', async () => {
96
+ temp.file('notes/a.md', '# A');
97
+ temp.file('notes/b.md', '# B');
98
+ temp.file('notes/c.md', '# C');
99
+ temp.file('notes/d.md', '# D');
100
+ const result = await service.runBatchWorkflow({ folderPath: path.join(temp.root, 'notes'), maxFiles: 2 }, defaultSettings());
101
+ expect(result.totalFiles).toBe(2);
102
+ });
103
+ test('runBatchWorkflow rejects invalid folder path', async () => {
104
+ await expect(service.runBatchWorkflow({ folderPath: path.join(temp.root, 'nonexistent') }, defaultSettings())).rejects.toThrow();
105
+ });
106
+ test('runBatchWorkflow rejects invalid regex pattern', async () => {
107
+ temp.file('notes/a.md', '# A');
108
+ await expect(service.runBatchWorkflow({ folderPath: path.join(temp.root, 'notes'), filePattern: '[invalid' }, defaultSettings())).rejects.toThrow('Invalid regex pattern');
109
+ });
110
+ test('runBatchWorkflow output base path is created', async () => {
111
+ temp.file('notes/a.md', '# A');
112
+ const customOutput = path.join(temp.root, 'custom_output');
113
+ const result = await service.runBatchWorkflow({ folderPath: path.join(temp.root, 'notes'), outputBasePath: customOutput }, defaultSettings());
114
+ expect(result.outputBasePath).toBe(customOutput);
115
+ expect(fs.existsSync(customOutput)).toBe(true);
116
+ });
117
+ });
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * CLI E2E pipeline test — validates the full CLI chain:
5
+ * parseCliArgs → findCommand → dispatchCommand → service method
6
+ */
7
+ const parser_1 = require("./notemd/cli/parser");
8
+ const commands_1 = require("./notemd/cli/commands");
9
+ describe('CLI Parser', () => {
10
+ test('parseCliArgs extracts command name', () => {
11
+ const args = (0, parser_1.parseCliArgs)(['process-file', '--path=notes/test.md']);
12
+ expect(args.command).toBe('process-file');
13
+ });
14
+ test('parseCliArgs extracts --key=value params', () => {
15
+ const args = (0, parser_1.parseCliArgs)(['translate', '--path=notes/doc.md', '--language=zh']);
16
+ expect((0, parser_1.resolveParam)(args.params, ['path', 'p'])).toBe('notes/doc.md');
17
+ expect((0, parser_1.resolveParam)(args.params, ['language', 'l'])).toBe('zh');
18
+ });
19
+ test('parseCliArgs extracts --key value params', () => {
20
+ const args = (0, parser_1.parseCliArgs)(['search', '--query', 'machine learning']);
21
+ expect((0, parser_1.resolveParam)(args.params, ['query', 'q'])).toBe('machine learning');
22
+ });
23
+ test('parseCliArgs extracts boolean flags', () => {
24
+ const args = (0, parser_1.parseCliArgs)(['process-file', '--path=notes/test.md', '--dry-run', '--no-concepts']);
25
+ expect((0, parser_1.resolveBool)(args.params, args.flags, ['dry-run', 'n'])).toBe(true);
26
+ expect((0, parser_1.resolveBool)(args.params, args.flags, ['no-concepts', 'nc'])).toBe(true);
27
+ expect((0, parser_1.resolveBool)(args.params, args.flags, ['nonexistent'])).toBe(false);
28
+ });
29
+ test('parseCliArgs resolves param aliases', () => {
30
+ const args = (0, parser_1.parseCliArgs)(['process-file', '-p', 'notes/test.md', '-o', 'out/']);
31
+ expect((0, parser_1.resolveParam)(args.params, ['path', 'p', 'file'])).toBe('notes/test.md');
32
+ expect((0, parser_1.resolveParam)(args.params, ['output', 'o'])).toBe('out/');
33
+ });
34
+ test('parseCliArgs handles positional args', () => {
35
+ const args = (0, parser_1.parseCliArgs)(['search', 'machine learning', 'AI']);
36
+ expect(args.command).toBe('search');
37
+ expect(args.positional).toEqual(['machine learning', 'AI']);
38
+ });
39
+ test('parseCliArgs empty args returns empty command', () => {
40
+ const args = (0, parser_1.parseCliArgs)([]);
41
+ expect(args.command).toBe('');
42
+ });
43
+ });
44
+ describe('CLI Commands Registry', () => {
45
+ test('listCommands returns all 23 commands', () => {
46
+ const cmds = (0, commands_1.listCommands)();
47
+ expect(cmds.length).toBeGreaterThanOrEqual(22);
48
+ });
49
+ test('findCommand finds by name', () => {
50
+ const cmd = (0, commands_1.findCommand)('workflow');
51
+ expect(cmd).toBeDefined();
52
+ expect(cmd.operationId).toBe('workflow.extract-and-generate');
53
+ expect(cmd.automationLevel).toBe('requires-active-file');
54
+ });
55
+ test('findCommand finds by alias', () => {
56
+ const cmd = (0, commands_1.findCommand)('wf');
57
+ expect(cmd).toBeDefined();
58
+ expect(cmd.name).toBe('workflow');
59
+ });
60
+ test('findCommand returns undefined for unknown', () => {
61
+ expect((0, commands_1.findCommand)('nonexistent')).toBeUndefined();
62
+ });
63
+ test('getOperationForCommand resolves operation from registry', () => {
64
+ const op = (0, commands_1.getOperationForCommand)('process-file');
65
+ expect(op).toBeDefined();
66
+ expect(op.id).toBe('file.process-add-links');
67
+ expect(op.inputSchema).toBeDefined();
68
+ });
69
+ test('all commands have valid operationIds', () => {
70
+ const cmds = (0, commands_1.listCommands)();
71
+ for (const cmd of cmds) {
72
+ const op = (0, commands_1.getOperationForCommand)(cmd.name);
73
+ expect(op).toBeDefined();
74
+ expect(op.id).toBe(cmd.operationId);
75
+ }
76
+ });
77
+ test('critical workflow commands exist', () => {
78
+ expect((0, commands_1.findCommand)('workflow')).toBeDefined();
79
+ expect((0, commands_1.findCommand)('batch-workflow')).toBeDefined();
80
+ expect((0, commands_1.findCommand)('process-file')).toBeDefined();
81
+ expect((0, commands_1.findCommand)('search')).toBeDefined();
82
+ expect((0, commands_1.findCommand)('generate-diagram')).toBeDefined();
83
+ expect((0, commands_1.findCommand)('translate')).toBeDefined();
84
+ });
85
+ test('all commands have params and flags defined', () => {
86
+ const cmds = (0, commands_1.listCommands)();
87
+ for (const cmd of cmds) {
88
+ expect(Array.isArray(cmd.params)).toBe(true);
89
+ expect(Array.isArray(cmd.flags)).toBe(true);
90
+ if (cmd.params.length > 0) {
91
+ const required = cmd.params.filter(p => p.required);
92
+ expect(required.length).toBeGreaterThanOrEqual(0);
93
+ }
94
+ }
95
+ });
96
+ test('all commands have unique names', () => {
97
+ const cmds = (0, commands_1.listCommands)();
98
+ const names = cmds.map(c => c.name);
99
+ const unique = new Set(names);
100
+ expect(unique.size).toBe(names.length);
101
+ });
102
+ test('command aliases do not collide with command names', () => {
103
+ const cmds = (0, commands_1.listCommands)();
104
+ const names = new Set(cmds.map(c => c.name));
105
+ for (const cmd of cmds) {
106
+ for (const alias of cmd.aliases) {
107
+ expect(names.has(alias)).toBe(false);
108
+ }
109
+ }
110
+ });
111
+ });
112
+ describe('CLI Help Output', () => {
113
+ test('every command has a non-empty description', () => {
114
+ for (const cmd of (0, commands_1.listCommands)()) {
115
+ expect(cmd.description.length).toBeGreaterThan(0);
116
+ }
117
+ });
118
+ test('every command has at least one alias or uses the primary name', () => {
119
+ for (const cmd of (0, commands_1.listCommands)()) {
120
+ expect(cmd.name.length).toBeGreaterThan(0);
121
+ expect(Array.isArray(cmd.aliases)).toBe(true);
122
+ }
123
+ });
124
+ test('automation level is valid for all commands', () => {
125
+ const valid = ['safe', 'requires-active-file', 'requires-selection', 'interactive-ui'];
126
+ for (const cmd of (0, commands_1.listCommands)()) {
127
+ expect(valid).toContain(cmd.automationLevel);
128
+ }
129
+ });
130
+ test('side effect class is valid for all commands', () => {
131
+ const valid = ['read-only', 'write-file', 'batch-write', 'preview-ui', 'destructive'];
132
+ for (const cmd of (0, commands_1.listCommands)()) {
133
+ expect(valid).toContain(cmd.sideEffectClass);
134
+ }
135
+ });
136
+ });
@@ -100,6 +100,57 @@ describe('NoteMD core primitives', () => {
100
100
  expect(result.content).toContain('NoteTorque["Angular acceleration insight"]');
101
101
  expect(result.content).toContain('Torque -.- NoteTorque');
102
102
  });
103
+ test('fixMermaidSyntax repairs displaced quoted labels and legacy double-dash edge labels', () => {
104
+ const source = [
105
+ '```mermaid',
106
+ 'graph TD;',
107
+ 'A --> B; "Interface Normal"',
108
+ 'P -- "theta_i" -- N;',
109
+ '```',
110
+ ].join('\n');
111
+ const result = (0, MermaidProcessor_1.fixMermaidSyntax)(source);
112
+ expect(result.changed).toBe(true);
113
+ expect(result.content).toContain('A -- "Interface Normal" --> B');
114
+ expect(result.content).toContain('P -- "theta_i" --> N;');
115
+ });
116
+ test('fixMermaidSyntax wraps bare html-break node labels into Mermaid node syntax', () => {
117
+ const source = [
118
+ '```mermaid',
119
+ 'flowchart TD',
120
+ 'A[Incident Wave] --> I Interface<br/>Boundary;',
121
+ 'I --> R Reflected<br/>Wave;',
122
+ '```',
123
+ ].join('\n');
124
+ const result = (0, MermaidProcessor_1.fixMermaidSyntax)(source);
125
+ expect(result.changed).toBe(true);
126
+ expect(result.content).toContain('I["Interface<br/>Boundary"]');
127
+ expect(result.content).toContain('R["Reflected<br/>Wave"]');
128
+ });
129
+ test('fixMermaidSyntax closes Mermaid node labels that end with a stray opening bracket', () => {
130
+ const source = [
131
+ '```mermaid',
132
+ 'graph LR',
133
+ 'InteractionPoint["作用点 Interaction Point["',
134
+ 'InteractionPoint --> Absorption["吸收 Absorption"]',
135
+ '```',
136
+ ].join('\n');
137
+ const result = (0, MermaidProcessor_1.fixMermaidSyntax)(source);
138
+ expect(result.changed).toBe(true);
139
+ expect(result.content).toContain('InteractionPoint["作用点 Interaction Point"]');
140
+ });
141
+ test('fixMermaidSyntax escapes stray quotes inside Mermaid bracketed node labels', () => {
142
+ const source = [
143
+ '```mermaid',
144
+ 'flowchart TD',
145
+ 'HQWG["High Quality Water Glass"] -->|"&gt;Superior Overall"| FS["Final Score "&gt;Superior Overall"| FS"]',
146
+ '```',
147
+ ].join('\n');
148
+ const result = (0, MermaidProcessor_1.fixMermaidSyntax)(source);
149
+ expect(result.changed).toBe(true);
150
+ expect(result.content).toContain('High Quality Water Glass');
151
+ expect(result.content).toContain('Final Score #quot;&gt;Superior Overall#quot;| FS');
152
+ expect(result.fixes).toContain('Escaped stray double quotes inside Mermaid node labels.');
153
+ });
103
154
  test('DuplicateDetector reports repeated terms and wiki-links', () => {
104
155
  const detector = new DuplicateDetector_1.DuplicateDetector();
105
156
  const content = 'Graph graph graph [[Node]] [[Node]] [[Edge]]';