notion-doc-sync 1.0.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 (47) hide show
  1. package/README.md +203 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.js +80 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/analyze.d.ts +6 -0
  6. package/dist/commands/analyze.js +58 -0
  7. package/dist/commands/analyze.js.map +1 -0
  8. package/dist/commands/fetch.d.ts +2 -0
  9. package/dist/commands/fetch.js +26 -0
  10. package/dist/commands/fetch.js.map +1 -0
  11. package/dist/commands/init.d.ts +2 -0
  12. package/dist/commands/init.js +21 -0
  13. package/dist/commands/init.js.map +1 -0
  14. package/dist/commands/stamp.d.ts +2 -0
  15. package/dist/commands/stamp.js +57 -0
  16. package/dist/commands/stamp.js.map +1 -0
  17. package/dist/commands/sync.d.ts +6 -0
  18. package/dist/commands/sync.js +125 -0
  19. package/dist/commands/sync.js.map +1 -0
  20. package/dist/lib/config.d.ts +32 -0
  21. package/dist/lib/config.js +85 -0
  22. package/dist/lib/config.js.map +1 -0
  23. package/dist/lib/doc-mapper.d.ts +111 -0
  24. package/dist/lib/doc-mapper.js +390 -0
  25. package/dist/lib/doc-mapper.js.map +1 -0
  26. package/dist/lib/git-analyzer.d.ts +14 -0
  27. package/dist/lib/git-analyzer.js +132 -0
  28. package/dist/lib/git-analyzer.js.map +1 -0
  29. package/dist/lib/local-docs-reader.d.ts +24 -0
  30. package/dist/lib/local-docs-reader.js +163 -0
  31. package/dist/lib/local-docs-reader.js.map +1 -0
  32. package/dist/lib/md-to-notion-converter.d.ts +5 -0
  33. package/dist/lib/md-to-notion-converter.js +14 -0
  34. package/dist/lib/md-to-notion-converter.js.map +1 -0
  35. package/dist/lib/notion-client.d.ts +29 -0
  36. package/dist/lib/notion-client.js +183 -0
  37. package/dist/lib/notion-client.js.map +1 -0
  38. package/dist/lib/notion-md-converter.d.ts +7 -0
  39. package/dist/lib/notion-md-converter.js +17 -0
  40. package/dist/lib/notion-md-converter.js.map +1 -0
  41. package/dist/lib/timestamp-utils.d.ts +7 -0
  42. package/dist/lib/timestamp-utils.js +47 -0
  43. package/dist/lib/timestamp-utils.js.map +1 -0
  44. package/dist/types/doc-sync.d.ts +103 -0
  45. package/dist/types/doc-sync.js +6 -0
  46. package/dist/types/doc-sync.js.map +1 -0
  47. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # notion-doc-sync
2
+
3
+ A CLI tool that keeps Notion documentation aligned with code changes. It fetches Notion pages as local Markdown, detects git changes, maps them to relevant docs, and supports bidirectional sync between local files and Notion.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js >= 18.0.0 (22.x recommended — use `nvm use` to auto-switch)
8
+ - npm
9
+ - A [Notion integration](https://developers.notion.com/) with access to your documentation pages
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Install globally from npm
15
+ npm install -g notion-doc-sync
16
+
17
+ # Initialise config in your project
18
+ notion-doc-sync init
19
+
20
+ # Edit .notion-doc-sync.json with your Notion API key and database ID
21
+
22
+ # Fetch docs from Notion
23
+ notion-doc-sync fetch
24
+ ```
25
+
26
+ ## CLI Commands
27
+
28
+ ### `notion-doc-sync init`
29
+
30
+ Creates a `.notion-doc-sync.json` config file in the current directory with default settings. Edit this file to add your Notion API key and database ID.
31
+
32
+ ### `notion-doc-sync fetch`
33
+
34
+ Fetches documentation pages from Notion and saves/updates them as local Markdown files in the configured docs directory (default: `./notionDocs`). Pages are matched by the page IDs stored in each local file's frontmatter.
35
+
36
+ ### `notion-doc-sync analyze`
37
+
38
+ Analyses git changes between two branches and maps them to documentation files using heuristic matching with confidence scores.
39
+
40
+ ```bash
41
+ notion-doc-sync analyze # compare main..HEAD
42
+ notion-doc-sync analyze --base-branch develop # custom base branch
43
+ notion-doc-sync analyze --target-branch feature/foo # custom target branch
44
+ ```
45
+
46
+ ### `notion-doc-sync sync`
47
+
48
+ Bidirectional sync between local docs and Notion. Compares timestamps to determine whether each file should be pulled from Notion or pushed to Notion.
49
+
50
+ ```bash
51
+ notion-doc-sync sync # execute sync
52
+ notion-doc-sync sync --dry-run # preview actions without making changes
53
+ ```
54
+
55
+ ### `notion-doc-sync stamp`
56
+
57
+ Updates the `lastUpdated` timestamp in the frontmatter of any locally modified Markdown files (detected via `git status`). Useful before pushing changes to Notion.
58
+
59
+ ## Configuration
60
+
61
+ Configuration is read from `.notion-doc-sync.json` in the project root. Run `notion-doc-sync init` to generate one.
62
+
63
+ ```json
64
+ {
65
+ "notionApiKey": "",
66
+ "notionDatabaseId": "",
67
+ "sourceDir": "./src",
68
+ "docsDir": "./notionDocs",
69
+ "excludePatterns": [
70
+ "node_modules/**",
71
+ "dist/**",
72
+ "**/*.test.ts",
73
+ "**/*.spec.ts",
74
+ "**/__tests__/**",
75
+ ".git/**"
76
+ ]
77
+ }
78
+ ```
79
+
80
+ | Field | Description | Default |
81
+ | ------------------ | -------------------------------------- | -------------- |
82
+ | `notionApiKey` | Notion integration API key | — |
83
+ | `notionDatabaseId` | Target Notion database ID | — |
84
+ | `sourceDir` | Source code directory to analyse | `./src` |
85
+ | `docsDir` | Local docs directory | `./notionDocs` |
86
+ | `excludePatterns` | Glob patterns to exclude from analysis | See above |
87
+
88
+ ### Setting up Notion
89
+
90
+ 1. Go to [Notion Developers](https://developers.notion.com/) and create a new integration.
91
+ 2. Copy the integration token into `notionApiKey` in your config file.
92
+ 3. Share your documentation database/pages with the integration.
93
+ 4. Copy the database ID into `notionDatabaseId`.
94
+
95
+ ## Repository Structure
96
+
97
+ ```
98
+ src/
99
+ ├── cli.ts # CLI entry point (commander)
100
+ ├── commands/
101
+ │ ├── fetch.ts # fetch command
102
+ │ ├── analyze.ts # analyze command
103
+ │ ├── init.ts # init command
104
+ │ ├── sync.ts # sync command
105
+ │ └── stamp.ts # stamp command
106
+ ├── lib/
107
+ │ ├── config.ts # Config loading and validation
108
+ │ ├── notion-client.ts # Notion API wrapper
109
+ │ ├── local-docs-reader.ts # Local Markdown file reader/writer
110
+ │ ├── doc-mapper.ts # Doc-to-code mapping with confidence scores
111
+ │ ├── git-analyzer.ts # Git diff extraction between branches
112
+ │ ├── md-to-notion-converter.ts # Markdown to Notion block converter
113
+ │ ├── notion-md-converter.ts # Notion to Markdown converter
114
+ │ └── timestamp-utils.ts # Timestamp comparison utilities
115
+ ├── types/
116
+ │ └── doc-sync.ts # TypeScript interfaces and types
117
+ └── __tests__/ # Tests (mirrors src structure)
118
+ ```
119
+
120
+ ## Local Development
121
+
122
+ ```bash
123
+ # Clone the repo
124
+ git clone <repo-url>
125
+ cd NotionDocFetcher
126
+
127
+ # Use the correct Node version
128
+ nvm use
129
+
130
+ # Install dependencies
131
+ npm install
132
+
133
+ # Run in development mode (via ts-node)
134
+ npm run dev -- fetch
135
+ npm run dev -- analyze --base-branch main
136
+ npm run dev -- sync --dry-run
137
+
138
+ # Build
139
+ npm run build
140
+
141
+ # Run the built CLI
142
+ node dist/cli.js fetch
143
+ ```
144
+
145
+ ### Available Scripts
146
+
147
+ | Script | Description |
148
+ | ----------------------- | ------------------------------------------ |
149
+ | `npm run build` | Compile TypeScript to `dist/` |
150
+ | `npm run dev` | Run via ts-node (pass CLI args after `--`) |
151
+ | `npm test` | Run all tests once |
152
+ | `npm run test:watch` | Run tests in watch mode |
153
+ | `npm run test:coverage` | Coverage report (90% threshold enforced) |
154
+ | `npm run lint` | ESLint |
155
+ | `npm run lint:fix` | Auto-fix lint issues |
156
+ | `npm run format` | Prettier formatting |
157
+ | `npm run type-check` | TypeScript type check without emit |
158
+ | `npm run clean` | Remove `dist/` |
159
+
160
+ ### Running a Single Test File
161
+
162
+ ```bash
163
+ npx vitest run src/__tests__/doc-mapper.test.ts
164
+ ```
165
+
166
+ ## Deployment
167
+
168
+ ### Publishing to npm
169
+
170
+ The package is configured for npm publishing with the binary name `notion-doc-sync`.
171
+
172
+ ```bash
173
+ # Build, test, and publish (prepublishOnly runs automatically)
174
+ npm publish
175
+
176
+ # Or step by step:
177
+ npm run clean
178
+ npm run build
179
+ npm test
180
+ npm publish
181
+ ```
182
+
183
+ The published package includes only the compiled JavaScript (`dist/`), type declarations, README, and LICENSE.
184
+
185
+ ### Installing from npm
186
+
187
+ Once published, users install it globally:
188
+
189
+ ```bash
190
+ npm install -g notion-doc-sync
191
+ notion-doc-sync --help
192
+ ```
193
+
194
+ Or use it as a project-local dev dependency:
195
+
196
+ ```bash
197
+ npm install --save-dev notion-doc-sync
198
+ npx notion-doc-sync fetch
199
+ ```
200
+
201
+ ## License
202
+
203
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /* eslint-disable no-console */
5
+ const commander_1 = require("commander");
6
+ const fetch_1 = require("./commands/fetch");
7
+ const analyze_1 = require("./commands/analyze");
8
+ const init_1 = require("./commands/init");
9
+ const sync_1 = require("./commands/sync");
10
+ const stamp_1 = require("./commands/stamp");
11
+ const program = new commander_1.Command();
12
+ program
13
+ .name('notion-doc-sync')
14
+ .description('CLI tool to keep Notion documentation aligned with code changes')
15
+ .version('1.0.0');
16
+ program
17
+ .command('fetch')
18
+ .description('Fetch documentation from Notion and save locally')
19
+ .action(async () => {
20
+ try {
21
+ await (0, fetch_1.fetchCommand)();
22
+ }
23
+ catch (error) {
24
+ console.error('Error:', error instanceof Error ? error.message : error);
25
+ process.exit(1);
26
+ }
27
+ });
28
+ program
29
+ .command('analyze')
30
+ .description('Analyze git changes and map to documentation files')
31
+ .option('--base-branch <branch>', 'Base branch for comparison', 'main')
32
+ .option('--target-branch <branch>', 'Target branch for comparison', 'HEAD')
33
+ .action(async (options) => {
34
+ try {
35
+ await (0, analyze_1.analyzeCommand)(options);
36
+ }
37
+ catch (error) {
38
+ console.error('Error:', error instanceof Error ? error.message : error);
39
+ process.exit(1);
40
+ }
41
+ });
42
+ program
43
+ .command('init')
44
+ .description('Create a .notion-doc-sync.json config file')
45
+ .action(() => {
46
+ try {
47
+ (0, init_1.initCommand)();
48
+ }
49
+ catch (error) {
50
+ console.error('Error:', error instanceof Error ? error.message : error);
51
+ process.exit(1);
52
+ }
53
+ });
54
+ program
55
+ .command('sync')
56
+ .description('Bidirectional sync between local docs and Notion')
57
+ .option('--dry-run', 'Preview sync actions without making changes')
58
+ .action(async (options) => {
59
+ try {
60
+ await (0, sync_1.syncCommand)(options);
61
+ }
62
+ catch (error) {
63
+ console.error('Error:', error instanceof Error ? error.message : error);
64
+ process.exit(1);
65
+ }
66
+ });
67
+ program
68
+ .command('stamp')
69
+ .description('Update timestamps on locally modified documentation files')
70
+ .action(async () => {
71
+ try {
72
+ await (0, stamp_1.stampCommand)();
73
+ }
74
+ catch (error) {
75
+ console.error('Error:', error instanceof Error ? error.message : error);
76
+ process.exit(1);
77
+ }
78
+ });
79
+ program.parse();
80
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface AnalyzeOptions {
2
+ readonly baseBranch?: string;
3
+ readonly targetBranch?: string;
4
+ }
5
+ export declare function analyzeCommand(options: AnalyzeOptions): Promise<void>;
6
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeCommand = analyzeCommand;
4
+ /* eslint-disable no-console */
5
+ const config_1 = require("../lib/config");
6
+ const git_analyzer_1 = require("../lib/git-analyzer");
7
+ const doc_mapper_1 = require("../lib/doc-mapper");
8
+ const local_docs_reader_1 = require("../lib/local-docs-reader");
9
+ const promises_1 = require("fs/promises");
10
+ async function analyzeCommand(options) {
11
+ const config = (0, config_1.resolveConfig)();
12
+ const baseBranch = options.baseBranch ?? 'main';
13
+ const targetBranch = options.targetBranch ?? 'HEAD';
14
+ console.log(`Analyzing changes between ${baseBranch} and ${targetBranch}...`);
15
+ const gitAnalyzer = new git_analyzer_1.GitAnalyzer();
16
+ const changes = gitAnalyzer.getCodeChanges(baseBranch, targetBranch);
17
+ if (changes.length === 0) {
18
+ console.log('No code changes detected between branches.');
19
+ return;
20
+ }
21
+ console.log(`Found ${changes.length} changed code file(s)`);
22
+ const localDocsReader = new local_docs_reader_1.LocalDocsReader(config.analysis.docsDir);
23
+ const localDocs = await localDocsReader.readLocalDocs();
24
+ const documentationFiles = [];
25
+ for (const doc of localDocs) {
26
+ const content = await (0, promises_1.readFile)(doc.filePath, 'utf-8');
27
+ documentationFiles.push({
28
+ filePath: doc.filePath,
29
+ content,
30
+ linkedCodeFiles: [],
31
+ mappingConfidence: 0,
32
+ });
33
+ }
34
+ const changedFilePaths = changes.map((c) => c.filePath);
35
+ const docMapper = new doc_mapper_1.DocMapper();
36
+ const enhanced = docMapper.enhanceDocumentationFiles(documentationFiles, changedFilePaths);
37
+ const affected = enhanced.filter((doc) => doc.linkedCodeFiles.length > 0);
38
+ if (affected.length === 0) {
39
+ console.log('No documentation files are linked to the changed code files.');
40
+ return;
41
+ }
42
+ console.log('\nDocumentation files potentially affected by code changes:\n');
43
+ console.log(padRight('Document', 40) + padRight('Linked Code Files', 45) + padRight('Confidence', 12));
44
+ console.log('-'.repeat(97));
45
+ for (const doc of affected) {
46
+ const docName = doc.filePath;
47
+ const linkedFiles = doc.linkedCodeFiles.join(', ');
48
+ const confidence = (doc.mappingConfidence * 100).toFixed(0) + '%';
49
+ console.log(padRight(docName, 40) + padRight(linkedFiles, 45) + padRight(confidence, 12));
50
+ }
51
+ }
52
+ function padRight(str, length) {
53
+ if (str.length >= length) {
54
+ return str.substring(0, length - 3) + '...';
55
+ }
56
+ return str + ' '.repeat(length - str.length);
57
+ }
58
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function fetchCommand(): Promise<void>;
2
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchCommand = fetchCommand;
4
+ /* eslint-disable no-console */
5
+ const config_1 = require("../lib/config");
6
+ const notion_client_1 = require("../lib/notion-client");
7
+ const local_docs_reader_1 = require("../lib/local-docs-reader");
8
+ async function fetchCommand() {
9
+ console.log('Starting documentation synchronization...');
10
+ const config = (0, config_1.resolveConfig)();
11
+ const notionClient = new notion_client_1.NotionClient(config.notion.apiKey);
12
+ const localDocsReader = new local_docs_reader_1.LocalDocsReader(config.analysis.docsDir);
13
+ console.log('Reading local documentation files...');
14
+ const pageIds = await localDocsReader.getPageIds();
15
+ if (pageIds.length === 0) {
16
+ console.log('No local docs found with page IDs. Nothing to fetch.');
17
+ return;
18
+ }
19
+ console.log('Fetching Notion documentation...');
20
+ const docs = await notionClient.fetchPagesByIds(pageIds);
21
+ console.log(`Found ${docs.length} documentation pages`);
22
+ console.log('Updating local documentation files...');
23
+ await localDocsReader.updateLocalDocs(docs);
24
+ console.log('Documentation sync completed successfully');
25
+ }
26
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/commands/fetch.ts"],"names":[],"mappings":";;AAKA,oCAwBC;AA7BD,+BAA+B;AAC/B,0CAA8C;AAC9C,wDAAoD;AACpD,gEAA2D;AAEpD,KAAK,UAAU,YAAY;IAChC,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAEzD,MAAM,MAAM,GAAG,IAAA,sBAAa,GAAE,CAAC;IAE/B,MAAM,YAAY,GAAG,IAAI,4BAAY,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAM,eAAe,GAAG,IAAI,mCAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAErE,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,CAAC;IAEnD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACpE,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,MAAM,sBAAsB,CAAC,CAAC;IAExD,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;IACrD,MAAM,eAAe,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function initCommand(): void;
2
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initCommand = initCommand;
4
+ /* eslint-disable no-console */
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const config_1 = require("../lib/config");
8
+ const CONFIG_FILENAME = '.notion-doc-sync.json';
9
+ function initCommand() {
10
+ const configPath = (0, path_1.join)(process.cwd(), CONFIG_FILENAME);
11
+ if ((0, fs_1.existsSync)(configPath)) {
12
+ console.log(`Config file already exists: ${configPath}`);
13
+ console.log('Remove it first if you want to reinitialize.');
14
+ return;
15
+ }
16
+ const defaultConfig = (0, config_1.getDefaultConfig)();
17
+ (0, fs_1.writeFileSync)(configPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf-8');
18
+ console.log(`Created config file: ${configPath}`);
19
+ console.log('Edit the file to add your Notion API key and database ID.');
20
+ }
21
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":";;AAOA,kCAcC;AArBD,+BAA+B;AAC/B,2BAA+C;AAC/C,+BAA4B;AAC5B,0CAAiD;AAEjD,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAEhD,SAAgB,WAAW;IACzB,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC,CAAC;IAExD,IAAI,IAAA,eAAU,EAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,IAAA,yBAAgB,GAAE,CAAC;IACzC,IAAA,kBAAa,EAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAElF,OAAO,CAAC,GAAG,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function stampCommand(): Promise<void>;
2
+ //# sourceMappingURL=stamp.d.ts.map
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stampCommand = stampCommand;
4
+ /* eslint-disable no-console */
5
+ const child_process_1 = require("child_process");
6
+ const promises_1 = require("fs/promises");
7
+ const path_1 = require("path");
8
+ const config_1 = require("../lib/config");
9
+ const timestamp_utils_1 = require("../lib/timestamp-utils");
10
+ async function stampCommand() {
11
+ const config = (0, config_1.resolveConfig)();
12
+ const docsDir = config.analysis.docsDir;
13
+ console.log(`Checking for modified docs in: ${docsDir}`);
14
+ const modifiedFiles = getModifiedMarkdownFiles(docsDir);
15
+ if (modifiedFiles.length === 0) {
16
+ console.log('No modified markdown files found.');
17
+ return;
18
+ }
19
+ const now = new Date();
20
+ let stamped = 0;
21
+ for (const filePath of modifiedFiles) {
22
+ try {
23
+ const fullPath = (0, path_1.join)(process.cwd(), filePath);
24
+ const content = await (0, promises_1.readFile)(fullPath, 'utf-8');
25
+ const updated = (0, timestamp_utils_1.replaceTimestampInContent)(content, now);
26
+ if (updated !== content) {
27
+ await (0, promises_1.writeFile)(fullPath, updated, 'utf-8');
28
+ console.log(` Stamped: ${filePath}`);
29
+ stamped++;
30
+ }
31
+ else {
32
+ console.log(` Skipped (no timestamp line): ${filePath}`);
33
+ }
34
+ }
35
+ catch (error) {
36
+ console.error(` Failed to stamp ${filePath}:`, error);
37
+ }
38
+ }
39
+ console.log(`\nStamped ${stamped} file(s) with timestamp: ${now.toISOString()}`);
40
+ }
41
+ function getModifiedMarkdownFiles(docsDir) {
42
+ try {
43
+ const output = (0, child_process_1.execSync)(`git status --porcelain -- ${docsDir}`, {
44
+ encoding: 'utf-8',
45
+ });
46
+ return output
47
+ .split('\n')
48
+ .filter((line) => line.trim() !== '')
49
+ .map((line) => line.slice(3).trim())
50
+ .filter((file) => file.endsWith('.md'));
51
+ }
52
+ catch {
53
+ console.error('Failed to run git status. Are you in a git repository?');
54
+ return [];
55
+ }
56
+ }
57
+ //# sourceMappingURL=stamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamp.js","sourceRoot":"","sources":["../../src/commands/stamp.ts"],"names":[],"mappings":";;AAOA,oCAmCC;AA1CD,+BAA+B;AAC/B,iDAAyC;AACzC,0CAAkD;AAClD,+BAA4B;AAC5B,0CAA8C;AAC9C,4DAAmE;AAE5D,KAAK,UAAU,YAAY;IAChC,MAAM,MAAM,GAAG,IAAA,sBAAa,GAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;IAExC,OAAO,CAAC,GAAG,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAC;IAEzD,MAAM,aAAa,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAExD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QACjD,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC/C,MAAM,OAAO,GAAG,MAAM,IAAA,mBAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,IAAA,2CAAyB,EAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAExD,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACxB,MAAM,IAAA,oBAAS,EAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC5C,OAAO,CAAC,GAAG,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;gBACtC,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,kCAAkC,QAAQ,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,qBAAqB,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,4BAA4B,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,wBAAwB,CAAC,OAAe;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAA,wBAAQ,EAAC,6BAA6B,OAAO,EAAE,EAAE;YAC9D,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAC;QAEH,OAAO,MAAM;aACV,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;aACpC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACnC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACxE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ interface SyncOptions {
2
+ readonly dryRun?: boolean;
3
+ }
4
+ export declare function syncCommand(options?: SyncOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncCommand = syncCommand;
4
+ /* eslint-disable no-console */
5
+ const config_1 = require("../lib/config");
6
+ const notion_client_1 = require("../lib/notion-client");
7
+ const md_to_notion_converter_1 = require("../lib/md-to-notion-converter");
8
+ const local_docs_reader_1 = require("../lib/local-docs-reader");
9
+ const timestamp_utils_1 = require("../lib/timestamp-utils");
10
+ async function syncCommand(options = {}) {
11
+ const config = (0, config_1.resolveConfig)();
12
+ (0, config_1.validateConfig)(config, ['notionApiKey']);
13
+ const notionClient = new notion_client_1.NotionClient(config.notion.apiKey);
14
+ const mdToNotion = new md_to_notion_converter_1.MdToNotionConverter();
15
+ const localDocsReader = new local_docs_reader_1.LocalDocsReader(config.analysis.docsDir);
16
+ console.log('Reading local documentation files...');
17
+ const localDocs = await localDocsReader.readLocalDocs();
18
+ if (localDocs.length === 0) {
19
+ console.log('No local docs found with page IDs. Nothing to sync.');
20
+ return;
21
+ }
22
+ console.log('Determining sync actions...');
23
+ const actions = [];
24
+ for (const localDoc of localDocs) {
25
+ try {
26
+ const metadata = await localDocsReader.readFullDoc(localDoc);
27
+ const notionTimestamp = await notionClient.fetchPageLastEdited(localDoc.pageId);
28
+ const direction = (0, timestamp_utils_1.compareSyncTimestamps)(metadata.lastUpdated, notionTimestamp);
29
+ actions.push({
30
+ pageId: localDoc.pageId,
31
+ filePath: localDoc.filePath,
32
+ fileName: localDoc.fileName,
33
+ direction,
34
+ localTimestamp: metadata.lastUpdated,
35
+ notionTimestamp,
36
+ });
37
+ }
38
+ catch (error) {
39
+ console.error(` Failed to check ${localDoc.fileName}:`, error);
40
+ }
41
+ }
42
+ printActionSummary(actions);
43
+ if (options.dryRun === true) {
44
+ console.log('\n--dry-run: No changes made.');
45
+ return;
46
+ }
47
+ const results = await executeActions(actions, notionClient, mdToNotion, localDocsReader);
48
+ printResults(results);
49
+ }
50
+ async function executeActions(actions, notionClient, mdToNotion, localDocsReader) {
51
+ const results = [];
52
+ for (const action of actions) {
53
+ if (action.direction === 'none') {
54
+ results.push({
55
+ pageId: action.pageId,
56
+ fileName: action.fileName,
57
+ direction: 'none',
58
+ success: true,
59
+ });
60
+ continue;
61
+ }
62
+ try {
63
+ if (action.direction === 'pull') {
64
+ const docs = await notionClient.fetchPagesByIds([action.pageId]);
65
+ if (docs.length > 0) {
66
+ await localDocsReader.updateLocalDocs(docs);
67
+ }
68
+ results.push({
69
+ pageId: action.pageId,
70
+ fileName: action.fileName,
71
+ direction: 'pull',
72
+ success: true,
73
+ synchronizedTimestamp: action.notionTimestamp,
74
+ });
75
+ }
76
+ else {
77
+ const metadata = await localDocsReader.readFullDoc({
78
+ filePath: action.filePath,
79
+ fileName: action.fileName,
80
+ pageId: action.pageId,
81
+ });
82
+ const blocks = mdToNotion.convertToBlocks(metadata.bodyContent);
83
+ await notionClient.replacePageContent(action.pageId, blocks);
84
+ const newNotionTimestamp = await notionClient.fetchPageLastEdited(action.pageId);
85
+ await localDocsReader.updateTimestampOnly(action.filePath, newNotionTimestamp);
86
+ results.push({
87
+ pageId: action.pageId,
88
+ fileName: action.fileName,
89
+ direction: 'push',
90
+ success: true,
91
+ synchronizedTimestamp: newNotionTimestamp,
92
+ });
93
+ }
94
+ }
95
+ catch (error) {
96
+ results.push({
97
+ pageId: action.pageId,
98
+ fileName: action.fileName,
99
+ direction: action.direction,
100
+ success: false,
101
+ error: error instanceof Error ? error.message : String(error),
102
+ });
103
+ }
104
+ }
105
+ return results;
106
+ }
107
+ function printActionSummary(actions) {
108
+ const pulls = actions.filter((a) => a.direction === 'pull');
109
+ const pushes = actions.filter((a) => a.direction === 'push');
110
+ const skips = actions.filter((a) => a.direction === 'none');
111
+ console.log(`\nSync plan: ${pulls.length} pull, ${pushes.length} push, ${skips.length} skip`);
112
+ for (const action of actions) {
113
+ const arrow = action.direction === 'pull' ? '<--' : action.direction === 'push' ? '-->' : '===';
114
+ console.log(` ${arrow} ${action.fileName} (${action.direction})`);
115
+ }
116
+ }
117
+ function printResults(results) {
118
+ const successes = results.filter((r) => r.success);
119
+ const failures = results.filter((r) => !r.success);
120
+ console.log(`\nSync complete: ${successes.length} succeeded, ${failures.length} failed`);
121
+ for (const failure of failures) {
122
+ console.error(` FAILED ${failure.fileName}: ${failure.error ?? 'unknown error'}`);
123
+ }
124
+ }
125
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}