@mono-labs/tracker 0.1.269

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 (126) hide show
  1. package/README.md +1196 -0
  2. package/bin/tracker.js +2 -0
  3. package/dist/dashboard/cli.d.ts +2 -0
  4. package/dist/dashboard/cli.d.ts.map +1 -0
  5. package/dist/dashboard/cli.js +38 -0
  6. package/dist/dashboard/index.d.ts +3 -0
  7. package/dist/dashboard/index.d.ts.map +1 -0
  8. package/dist/dashboard/index.js +5 -0
  9. package/dist/dashboard/server.d.ts +3 -0
  10. package/dist/dashboard/server.d.ts.map +1 -0
  11. package/dist/dashboard/server.js +130 -0
  12. package/dist/dashboard/types.d.ts +13 -0
  13. package/dist/dashboard/types.d.ts.map +1 -0
  14. package/dist/dashboard/types.js +2 -0
  15. package/dist/dashboard/watcher.d.ts +7 -0
  16. package/dist/dashboard/watcher.d.ts.map +1 -0
  17. package/dist/dashboard/watcher.js +44 -0
  18. package/dist/executor/action-executor.d.ts +10 -0
  19. package/dist/executor/action-executor.d.ts.map +1 -0
  20. package/dist/executor/action-executor.js +19 -0
  21. package/dist/executor/actions/index.d.ts +4 -0
  22. package/dist/executor/actions/index.d.ts.map +1 -0
  23. package/dist/executor/actions/index.js +9 -0
  24. package/dist/executor/actions/remove-action.d.ts +4 -0
  25. package/dist/executor/actions/remove-action.d.ts.map +1 -0
  26. package/dist/executor/actions/remove-action.js +10 -0
  27. package/dist/executor/actions/rename-action.d.ts +4 -0
  28. package/dist/executor/actions/rename-action.d.ts.map +1 -0
  29. package/dist/executor/actions/rename-action.js +10 -0
  30. package/dist/executor/actions/replace-action.d.ts +4 -0
  31. package/dist/executor/actions/replace-action.d.ts.map +1 -0
  32. package/dist/executor/actions/replace-action.js +10 -0
  33. package/dist/executor/index.d.ts +4 -0
  34. package/dist/executor/index.d.ts.map +1 -0
  35. package/dist/executor/index.js +10 -0
  36. package/dist/index.d.ts +20 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +58 -0
  39. package/dist/manager/index.d.ts +7 -0
  40. package/dist/manager/index.d.ts.map +1 -0
  41. package/dist/manager/index.js +19 -0
  42. package/dist/manager/notation-manager.d.ts +18 -0
  43. package/dist/manager/notation-manager.d.ts.map +1 -0
  44. package/dist/manager/notation-manager.js +137 -0
  45. package/dist/manager/notation-manager.test.d.ts +2 -0
  46. package/dist/manager/notation-manager.test.d.ts.map +1 -0
  47. package/dist/manager/notation-manager.test.js +211 -0
  48. package/dist/manager/notation-updater.d.ts +6 -0
  49. package/dist/manager/notation-updater.d.ts.map +1 -0
  50. package/dist/manager/notation-updater.js +20 -0
  51. package/dist/manager/relationship-manager.d.ts +5 -0
  52. package/dist/manager/relationship-manager.d.ts.map +1 -0
  53. package/dist/manager/relationship-manager.js +46 -0
  54. package/dist/manager/stats.d.ts +3 -0
  55. package/dist/manager/stats.d.ts.map +1 -0
  56. package/dist/manager/stats.js +41 -0
  57. package/dist/manager/validator.d.ts +9 -0
  58. package/dist/manager/validator.d.ts.map +1 -0
  59. package/dist/manager/validator.js +62 -0
  60. package/dist/scanner/action-parser.d.ts +3 -0
  61. package/dist/scanner/action-parser.d.ts.map +1 -0
  62. package/dist/scanner/action-parser.js +97 -0
  63. package/dist/scanner/action-parser.test.d.ts +2 -0
  64. package/dist/scanner/action-parser.test.d.ts.map +1 -0
  65. package/dist/scanner/action-parser.test.js +94 -0
  66. package/dist/scanner/attribute-parser.d.ts +15 -0
  67. package/dist/scanner/attribute-parser.d.ts.map +1 -0
  68. package/dist/scanner/attribute-parser.js +183 -0
  69. package/dist/scanner/attribute-parser.test.d.ts +2 -0
  70. package/dist/scanner/attribute-parser.test.d.ts.map +1 -0
  71. package/dist/scanner/attribute-parser.test.js +93 -0
  72. package/dist/scanner/file-scanner.d.ts +3 -0
  73. package/dist/scanner/file-scanner.d.ts.map +1 -0
  74. package/dist/scanner/file-scanner.js +58 -0
  75. package/dist/scanner/index.d.ts +6 -0
  76. package/dist/scanner/index.d.ts.map +1 -0
  77. package/dist/scanner/index.js +11 -0
  78. package/dist/scanner/notation-parser.d.ts +3 -0
  79. package/dist/scanner/notation-parser.d.ts.map +1 -0
  80. package/dist/scanner/notation-parser.js +88 -0
  81. package/dist/scanner/notation-parser.test.d.ts +2 -0
  82. package/dist/scanner/notation-parser.test.d.ts.map +1 -0
  83. package/dist/scanner/notation-parser.test.js +153 -0
  84. package/dist/storage/config-loader.d.ts +3 -0
  85. package/dist/storage/config-loader.d.ts.map +1 -0
  86. package/dist/storage/config-loader.js +55 -0
  87. package/dist/storage/index.d.ts +3 -0
  88. package/dist/storage/index.d.ts.map +1 -0
  89. package/dist/storage/index.js +7 -0
  90. package/dist/storage/jsonl-storage.d.ts +11 -0
  91. package/dist/storage/jsonl-storage.d.ts.map +1 -0
  92. package/dist/storage/jsonl-storage.js +88 -0
  93. package/dist/storage/jsonl-storage.test.d.ts +2 -0
  94. package/dist/storage/jsonl-storage.test.d.ts.map +1 -0
  95. package/dist/storage/jsonl-storage.test.js +126 -0
  96. package/dist/types/action.d.ts +57 -0
  97. package/dist/types/action.d.ts.map +1 -0
  98. package/dist/types/action.js +13 -0
  99. package/dist/types/config.d.ts +11 -0
  100. package/dist/types/config.d.ts.map +1 -0
  101. package/dist/types/config.js +11 -0
  102. package/dist/types/enums.d.ts +40 -0
  103. package/dist/types/enums.d.ts.map +1 -0
  104. package/dist/types/enums.js +37 -0
  105. package/dist/types/index.d.ts +7 -0
  106. package/dist/types/index.d.ts.map +1 -0
  107. package/dist/types/index.js +13 -0
  108. package/dist/types/notation.d.ts +64 -0
  109. package/dist/types/notation.d.ts.map +1 -0
  110. package/dist/types/notation.js +2 -0
  111. package/dist/utils/date-parser.d.ts +3 -0
  112. package/dist/utils/date-parser.d.ts.map +1 -0
  113. package/dist/utils/date-parser.js +51 -0
  114. package/dist/utils/date-parser.test.d.ts +2 -0
  115. package/dist/utils/date-parser.test.d.ts.map +1 -0
  116. package/dist/utils/date-parser.test.js +34 -0
  117. package/dist/utils/id-generator.d.ts +3 -0
  118. package/dist/utils/id-generator.d.ts.map +1 -0
  119. package/dist/utils/id-generator.js +12 -0
  120. package/dist/utils/id-generator.test.d.ts +2 -0
  121. package/dist/utils/id-generator.test.d.ts.map +1 -0
  122. package/dist/utils/id-generator.test.js +30 -0
  123. package/dist/utils/index.d.ts +3 -0
  124. package/dist/utils/index.d.ts.map +1 -0
  125. package/dist/utils/index.js +9 -0
  126. package/package.json +60 -0
@@ -0,0 +1,137 @@
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
+ exports.NotationManager = void 0;
37
+ const path = __importStar(require("path"));
38
+ const storage_1 = require("../storage");
39
+ const utils_1 = require("../utils");
40
+ const relationship_manager_1 = require("./relationship-manager");
41
+ const validator_1 = require("./validator");
42
+ const stats_1 = require("./stats");
43
+ class NotationManager {
44
+ storage;
45
+ notations = [];
46
+ config;
47
+ constructor(config) {
48
+ this.config = config;
49
+ const storagePath = path.isAbsolute(config.storagePath)
50
+ ? config.storagePath
51
+ : path.join(config.rootDir, config.storagePath);
52
+ this.storage = new storage_1.JsonlStorage(storagePath);
53
+ }
54
+ async load() {
55
+ this.notations = await this.storage.readAll();
56
+ }
57
+ async save() {
58
+ await this.storage.writeAll(this.notations);
59
+ }
60
+ getAll() {
61
+ return [...this.notations];
62
+ }
63
+ setAll(notations) {
64
+ this.notations = [...notations];
65
+ }
66
+ getById(id) {
67
+ return this.notations.find((n) => n.id === id);
68
+ }
69
+ query(q) {
70
+ return this.notations.filter((n) => {
71
+ if (q.type) {
72
+ const types = Array.isArray(q.type) ? q.type : [q.type];
73
+ if (!types.includes(n.type))
74
+ return false;
75
+ }
76
+ if (q.tags && q.tags.length > 0) {
77
+ if (!q.tags.some((t) => n.tags.includes(t)))
78
+ return false;
79
+ }
80
+ if (q.priority) {
81
+ const priorities = Array.isArray(q.priority) ? q.priority : [q.priority];
82
+ if (!n.priority || !priorities.includes(n.priority))
83
+ return false;
84
+ }
85
+ if (q.status) {
86
+ const statuses = Array.isArray(q.status) ? q.status : [q.status];
87
+ if (!statuses.includes(n.status))
88
+ return false;
89
+ }
90
+ if (q.file) {
91
+ if (!n.location.file.includes(q.file))
92
+ return false;
93
+ }
94
+ if (q.assignee) {
95
+ if (n.assignee !== q.assignee)
96
+ return false;
97
+ }
98
+ if (q.overdue === true) {
99
+ if (!n.dueDate || !(0, utils_1.isOverdue)(n.dueDate) || n.status === 'resolved')
100
+ return false;
101
+ }
102
+ if (q.blocked === true) {
103
+ if (!(0, relationship_manager_1.isBlocked)(n, this.notations))
104
+ return false;
105
+ }
106
+ if (q.search) {
107
+ const lower = q.search.toLowerCase();
108
+ const searchable = `${n.description} ${n.body.join(' ')} ${n.tags.join(' ')}`.toLowerCase();
109
+ if (!searchable.includes(lower))
110
+ return false;
111
+ }
112
+ if (q.dueBefore) {
113
+ if (!n.dueDate || n.dueDate > q.dueBefore)
114
+ return false;
115
+ }
116
+ if (q.dueAfter) {
117
+ if (!n.dueDate || n.dueDate < q.dueAfter)
118
+ return false;
119
+ }
120
+ return true;
121
+ });
122
+ }
123
+ update(id, updates) {
124
+ const idx = this.notations.findIndex((n) => n.id === id);
125
+ if (idx === -1)
126
+ return false;
127
+ this.notations[idx] = { ...this.notations[idx], ...updates };
128
+ return true;
129
+ }
130
+ validate() {
131
+ return (0, validator_1.validateAll)(this.notations);
132
+ }
133
+ stats() {
134
+ return (0, stats_1.computeStats)(this.notations);
135
+ }
136
+ }
137
+ exports.NotationManager = NotationManager;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=notation-manager.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notation-manager.test.d.ts","sourceRoot":"","sources":["../../src/manager/notation-manager.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const notation_manager_1 = require("./notation-manager");
5
+ const stats_1 = require("./stats");
6
+ const relationship_manager_1 = require("./relationship-manager");
7
+ const notation_updater_1 = require("./notation-updater");
8
+ const validator_1 = require("./validator");
9
+ function makeNotation(overrides = {}) {
10
+ return {
11
+ id: 'N-test001',
12
+ type: 'TODO',
13
+ description: 'Test notation',
14
+ body: [],
15
+ codeContext: [],
16
+ location: { file: 'test.ts', line: 1, column: 1 },
17
+ status: 'open',
18
+ tags: [],
19
+ actions: [],
20
+ relationships: [],
21
+ rawBlock: '// TODO: Test',
22
+ scannedAt: '2026-01-01T00:00:00.000Z',
23
+ ...overrides,
24
+ };
25
+ }
26
+ const testConfig = {
27
+ rootDir: '/tmp/test-project',
28
+ include: ['**/*.ts'],
29
+ exclude: ['**/node_modules/**'],
30
+ markers: ['TODO', 'FIXME', 'BUG', 'HACK', 'NOTE', 'OPTIMIZE', 'SECURITY'],
31
+ storagePath: '/tmp/test-tracker/notations.jsonl',
32
+ idPrefix: 'N',
33
+ };
34
+ (0, vitest_1.describe)('NotationManager', () => {
35
+ let manager;
36
+ (0, vitest_1.beforeEach)(() => {
37
+ manager = new notation_manager_1.NotationManager(testConfig);
38
+ });
39
+ (0, vitest_1.it)('starts empty', () => {
40
+ (0, vitest_1.expect)(manager.getAll()).toEqual([]);
41
+ });
42
+ (0, vitest_1.it)('sets and gets notations', () => {
43
+ const notations = [makeNotation({ id: 'N-1' }), makeNotation({ id: 'N-2' })];
44
+ manager.setAll(notations);
45
+ (0, vitest_1.expect)(manager.getAll()).toHaveLength(2);
46
+ });
47
+ (0, vitest_1.it)('gets by id', () => {
48
+ manager.setAll([makeNotation({ id: 'N-1', description: 'First' })]);
49
+ (0, vitest_1.expect)(manager.getById('N-1')?.description).toBe('First');
50
+ (0, vitest_1.expect)(manager.getById('N-999')).toBeUndefined();
51
+ });
52
+ (0, vitest_1.it)('updates a notation', () => {
53
+ manager.setAll([makeNotation({ id: 'N-1' })]);
54
+ const updated = manager.update('N-1', { description: 'Updated' });
55
+ (0, vitest_1.expect)(updated).toBe(true);
56
+ (0, vitest_1.expect)(manager.getById('N-1')?.description).toBe('Updated');
57
+ });
58
+ (0, vitest_1.it)('returns false when updating non-existent id', () => {
59
+ (0, vitest_1.expect)(manager.update('N-999', { description: 'nope' })).toBe(false);
60
+ });
61
+ (0, vitest_1.describe)('query', () => {
62
+ (0, vitest_1.beforeEach)(() => {
63
+ manager.setAll([
64
+ makeNotation({ id: 'N-1', type: 'TODO', priority: 'high', tags: ['ui'], assignee: 'Alice', status: 'open' }),
65
+ makeNotation({ id: 'N-2', type: 'FIXME', priority: 'low', tags: ['api'], assignee: 'Bob', status: 'open' }),
66
+ makeNotation({ id: 'N-3', type: 'BUG', priority: 'critical', tags: ['ui', 'api'], status: 'resolved' }),
67
+ makeNotation({ id: 'N-4', type: 'TODO', dueDate: '2020-01-01', status: 'open' }),
68
+ makeNotation({ id: 'N-5', type: 'TODO', description: 'Search me please', status: 'open' }),
69
+ ]);
70
+ });
71
+ (0, vitest_1.it)('filters by type', () => {
72
+ (0, vitest_1.expect)(manager.query({ type: 'TODO' })).toHaveLength(3);
73
+ });
74
+ (0, vitest_1.it)('filters by multiple types', () => {
75
+ (0, vitest_1.expect)(manager.query({ type: ['TODO', 'BUG'] })).toHaveLength(4);
76
+ });
77
+ (0, vitest_1.it)('filters by tags', () => {
78
+ (0, vitest_1.expect)(manager.query({ tags: ['ui'] })).toHaveLength(2);
79
+ });
80
+ (0, vitest_1.it)('filters by priority', () => {
81
+ (0, vitest_1.expect)(manager.query({ priority: 'high' })).toHaveLength(1);
82
+ });
83
+ (0, vitest_1.it)('filters by status', () => {
84
+ (0, vitest_1.expect)(manager.query({ status: 'resolved' })).toHaveLength(1);
85
+ });
86
+ (0, vitest_1.it)('filters by assignee', () => {
87
+ (0, vitest_1.expect)(manager.query({ assignee: 'Alice' })).toHaveLength(1);
88
+ });
89
+ (0, vitest_1.it)('filters overdue', () => {
90
+ const overdue = manager.query({ overdue: true });
91
+ (0, vitest_1.expect)(overdue).toHaveLength(1);
92
+ (0, vitest_1.expect)(overdue[0].id).toBe('N-4');
93
+ });
94
+ (0, vitest_1.it)('filters by search text', () => {
95
+ const results = manager.query({ search: 'search me' });
96
+ (0, vitest_1.expect)(results).toHaveLength(1);
97
+ (0, vitest_1.expect)(results[0].id).toBe('N-5');
98
+ });
99
+ (0, vitest_1.it)('filters by file', () => {
100
+ manager.setAll([
101
+ makeNotation({ id: 'N-1', location: { file: 'src/app.ts', line: 1, column: 1 } }),
102
+ makeNotation({ id: 'N-2', location: { file: 'src/utils.ts', line: 1, column: 1 } }),
103
+ ]);
104
+ (0, vitest_1.expect)(manager.query({ file: 'app.ts' })).toHaveLength(1);
105
+ });
106
+ });
107
+ });
108
+ (0, vitest_1.describe)('computeStats', () => {
109
+ (0, vitest_1.it)('computes correct totals', () => {
110
+ const notations = [
111
+ makeNotation({ id: 'N-1', type: 'TODO', priority: 'high', tags: ['ui'], status: 'open' }),
112
+ makeNotation({ id: 'N-2', type: 'FIXME', priority: 'low', tags: ['api'], status: 'open', debt: { hours: 4, compounding: 'low' } }),
113
+ makeNotation({ id: 'N-3', type: 'TODO', status: 'resolved', assignee: 'Alice', debt: { hours: 8, compounding: 'high' } }),
114
+ ];
115
+ const stats = (0, stats_1.computeStats)(notations);
116
+ (0, vitest_1.expect)(stats.total).toBe(3);
117
+ (0, vitest_1.expect)(stats.byType['TODO']).toBe(2);
118
+ (0, vitest_1.expect)(stats.byType['FIXME']).toBe(1);
119
+ (0, vitest_1.expect)(stats.byPriority['high']).toBe(1);
120
+ (0, vitest_1.expect)(stats.byStatus['open']).toBe(2);
121
+ (0, vitest_1.expect)(stats.byTag['ui']).toBe(1);
122
+ (0, vitest_1.expect)(stats.byAssignee['Alice']).toBe(1);
123
+ (0, vitest_1.expect)(stats.totalDebtHours).toBe(12);
124
+ });
125
+ });
126
+ (0, vitest_1.describe)('relationship-manager', () => {
127
+ (0, vitest_1.it)('detects blocked notations', () => {
128
+ const all = [
129
+ makeNotation({ id: 'N-1', status: 'open' }),
130
+ makeNotation({ id: 'N-2', relationships: ['N-1'], status: 'open' }),
131
+ ];
132
+ (0, vitest_1.expect)((0, relationship_manager_1.isBlocked)(all[1], all)).toBe(true);
133
+ (0, vitest_1.expect)((0, relationship_manager_1.isBlocked)(all[0], all)).toBe(false);
134
+ });
135
+ (0, vitest_1.it)('not blocked if dependency is resolved', () => {
136
+ const all = [
137
+ makeNotation({ id: 'N-1', status: 'resolved' }),
138
+ makeNotation({ id: 'N-2', relationships: ['N-1'], status: 'open' }),
139
+ ];
140
+ (0, vitest_1.expect)((0, relationship_manager_1.isBlocked)(all[1], all)).toBe(false);
141
+ });
142
+ (0, vitest_1.it)('detects circular dependencies', () => {
143
+ const all = [
144
+ makeNotation({ id: 'N-1', relationships: ['N-2'] }),
145
+ makeNotation({ id: 'N-2', relationships: ['N-1'] }),
146
+ ];
147
+ const cycles = (0, relationship_manager_1.detectCircularDependencies)(all);
148
+ (0, vitest_1.expect)(cycles.length).toBeGreaterThan(0);
149
+ });
150
+ (0, vitest_1.it)('returns empty for no cycles', () => {
151
+ const all = [
152
+ makeNotation({ id: 'N-1', relationships: [] }),
153
+ makeNotation({ id: 'N-2', relationships: ['N-1'] }),
154
+ ];
155
+ const cycles = (0, relationship_manager_1.detectCircularDependencies)(all);
156
+ (0, vitest_1.expect)(cycles).toHaveLength(0);
157
+ });
158
+ });
159
+ (0, vitest_1.describe)('notation-updater', () => {
160
+ (0, vitest_1.it)('updates status', () => {
161
+ const n = makeNotation({ status: 'open' });
162
+ const updated = (0, notation_updater_1.updateStatus)(n, 'resolved');
163
+ (0, vitest_1.expect)(updated.status).toBe('resolved');
164
+ });
165
+ (0, vitest_1.it)('adds tag', () => {
166
+ const n = makeNotation({ tags: ['a'] });
167
+ const updated = (0, notation_updater_1.addTag)(n, 'b');
168
+ (0, vitest_1.expect)(updated.tags).toEqual(['a', 'b']);
169
+ });
170
+ (0, vitest_1.it)('does not duplicate tag', () => {
171
+ const n = makeNotation({ tags: ['a'] });
172
+ const updated = (0, notation_updater_1.addTag)(n, 'a');
173
+ (0, vitest_1.expect)(updated.tags).toEqual(['a']);
174
+ });
175
+ (0, vitest_1.it)('removes tag', () => {
176
+ const n = makeNotation({ tags: ['a', 'b'] });
177
+ const updated = (0, notation_updater_1.removeTag)(n, 'a');
178
+ (0, vitest_1.expect)(updated.tags).toEqual(['b']);
179
+ });
180
+ (0, vitest_1.it)('sets assignee', () => {
181
+ const n = makeNotation();
182
+ const updated = (0, notation_updater_1.setAssignee)(n, 'Alice');
183
+ (0, vitest_1.expect)(updated.assignee).toBe('Alice');
184
+ });
185
+ });
186
+ (0, vitest_1.describe)('validator', () => {
187
+ (0, vitest_1.it)('validates a correct notation', () => {
188
+ const errors = (0, validator_1.validateNotation)(makeNotation());
189
+ (0, vitest_1.expect)(errors).toHaveLength(0);
190
+ });
191
+ (0, vitest_1.it)('catches missing id', () => {
192
+ const errors = (0, validator_1.validateNotation)(makeNotation({ id: '' }));
193
+ (0, vitest_1.expect)(errors.some((e) => e.field === 'id')).toBe(true);
194
+ });
195
+ (0, vitest_1.it)('catches invalid type', () => {
196
+ const errors = (0, validator_1.validateNotation)(makeNotation({ type: 'INVALID' }));
197
+ (0, vitest_1.expect)(errors.some((e) => e.field === 'type')).toBe(true);
198
+ });
199
+ (0, vitest_1.it)('catches missing description', () => {
200
+ const errors = (0, validator_1.validateNotation)(makeNotation({ description: '' }));
201
+ (0, vitest_1.expect)(errors.some((e) => e.field === 'description')).toBe(true);
202
+ });
203
+ (0, vitest_1.it)('detects duplicate ids in validateAll', () => {
204
+ const errors = (0, validator_1.validateAll)([makeNotation({ id: 'N-1' }), makeNotation({ id: 'N-1' })]);
205
+ (0, vitest_1.expect)(errors.some((e) => e.message.includes('Duplicate'))).toBe(true);
206
+ });
207
+ (0, vitest_1.it)('detects unknown references', () => {
208
+ const errors = (0, validator_1.validateAll)([makeNotation({ id: 'N-1', relationships: ['N-999'] })]);
209
+ (0, vitest_1.expect)(errors.some((e) => e.message.includes('Unknown reference'))).toBe(true);
210
+ });
211
+ });
@@ -0,0 +1,6 @@
1
+ import type { Notation, Status } from '../types';
2
+ export declare function updateStatus(notation: Notation, status: Status): Notation;
3
+ export declare function addTag(notation: Notation, tag: string): Notation;
4
+ export declare function removeTag(notation: Notation, tag: string): Notation;
5
+ export declare function setAssignee(notation: Notation, assignee: string): Notation;
6
+ //# sourceMappingURL=notation-updater.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notation-updater.d.ts","sourceRoot":"","sources":["../../src/manager/notation-updater.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,CAEzE;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAGhE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAEnE;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAE1E"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateStatus = updateStatus;
4
+ exports.addTag = addTag;
5
+ exports.removeTag = removeTag;
6
+ exports.setAssignee = setAssignee;
7
+ function updateStatus(notation, status) {
8
+ return { ...notation, status };
9
+ }
10
+ function addTag(notation, tag) {
11
+ if (notation.tags.includes(tag))
12
+ return notation;
13
+ return { ...notation, tags: [...notation.tags, tag] };
14
+ }
15
+ function removeTag(notation, tag) {
16
+ return { ...notation, tags: notation.tags.filter((t) => t !== tag) };
17
+ }
18
+ function setAssignee(notation, assignee) {
19
+ return { ...notation, assignee };
20
+ }
@@ -0,0 +1,5 @@
1
+ import type { Notation } from '../types';
2
+ export declare function getBlockers(notation: Notation, allNotations: Notation[]): Notation[];
3
+ export declare function isBlocked(notation: Notation, allNotations: Notation[]): boolean;
4
+ export declare function detectCircularDependencies(notations: Notation[]): string[][];
5
+ //# sourceMappingURL=relationship-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-manager.d.ts","sourceRoot":"","sources":["../../src/manager/relationship-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAGpF;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,OAAO,CAE/E;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE,CAuC5E"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBlockers = getBlockers;
4
+ exports.isBlocked = isBlocked;
5
+ exports.detectCircularDependencies = detectCircularDependencies;
6
+ function getBlockers(notation, allNotations) {
7
+ const idSet = new Set(notation.relationships);
8
+ return allNotations.filter((n) => idSet.has(n.id) && n.status !== 'resolved');
9
+ }
10
+ function isBlocked(notation, allNotations) {
11
+ return getBlockers(notation, allNotations).length > 0;
12
+ }
13
+ function detectCircularDependencies(notations) {
14
+ const idToRelationships = new Map();
15
+ for (const n of notations) {
16
+ idToRelationships.set(n.id, n.relationships);
17
+ }
18
+ const cycles = [];
19
+ const visited = new Set();
20
+ const inStack = new Set();
21
+ function dfs(id, path) {
22
+ if (inStack.has(id)) {
23
+ const cycleStart = path.indexOf(id);
24
+ if (cycleStart !== -1) {
25
+ cycles.push(path.slice(cycleStart).concat(id));
26
+ }
27
+ return;
28
+ }
29
+ if (visited.has(id))
30
+ return;
31
+ visited.add(id);
32
+ inStack.add(id);
33
+ path.push(id);
34
+ const deps = idToRelationships.get(id) ?? [];
35
+ for (const dep of deps) {
36
+ dfs(dep, [...path]);
37
+ }
38
+ inStack.delete(id);
39
+ }
40
+ for (const n of notations) {
41
+ if (!visited.has(n.id)) {
42
+ dfs(n.id, []);
43
+ }
44
+ }
45
+ return cycles;
46
+ }
@@ -0,0 +1,3 @@
1
+ import type { Notation, NotationStats } from '../types';
2
+ export declare function computeStats(notations: Notation[]): NotationStats;
3
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/manager/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAIvD,wBAAgB,YAAY,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,aAAa,CA2CjE"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeStats = computeStats;
4
+ const utils_1 = require("../utils");
5
+ const relationship_manager_1 = require("./relationship-manager");
6
+ function computeStats(notations) {
7
+ const stats = {
8
+ total: notations.length,
9
+ byType: {},
10
+ byPriority: {},
11
+ byStatus: {},
12
+ byTag: {},
13
+ byAssignee: {},
14
+ overdue: 0,
15
+ blocked: 0,
16
+ totalDebtHours: 0,
17
+ };
18
+ for (const n of notations) {
19
+ stats.byType[n.type] = (stats.byType[n.type] ?? 0) + 1;
20
+ stats.byStatus[n.status] = (stats.byStatus[n.status] ?? 0) + 1;
21
+ if (n.priority) {
22
+ stats.byPriority[n.priority] = (stats.byPriority[n.priority] ?? 0) + 1;
23
+ }
24
+ for (const tag of n.tags) {
25
+ stats.byTag[tag] = (stats.byTag[tag] ?? 0) + 1;
26
+ }
27
+ if (n.assignee) {
28
+ stats.byAssignee[n.assignee] = (stats.byAssignee[n.assignee] ?? 0) + 1;
29
+ }
30
+ if (n.dueDate && (0, utils_1.isOverdue)(n.dueDate) && n.status !== 'resolved') {
31
+ stats.overdue++;
32
+ }
33
+ if ((0, relationship_manager_1.isBlocked)(n, notations)) {
34
+ stats.blocked++;
35
+ }
36
+ if (n.debt) {
37
+ stats.totalDebtHours += n.debt.hours;
38
+ }
39
+ }
40
+ return stats;
41
+ }
@@ -0,0 +1,9 @@
1
+ import type { Notation } from '../types';
2
+ export interface ValidationError {
3
+ notationId: string;
4
+ field: string;
5
+ message: string;
6
+ }
7
+ export declare function validateNotation(notation: Notation): ValidationError[];
8
+ export declare function validateAll(notations: Notation[]): ValidationError[];
9
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/manager/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAIxC,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CACf;AAOD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,eAAe,EAAE,CA0BtE;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,eAAe,EAAE,CA+BpE"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateNotation = validateNotation;
4
+ exports.validateAll = validateAll;
5
+ const enums_1 = require("../types/enums");
6
+ const relationship_manager_1 = require("./relationship-manager");
7
+ const VALID_TYPES = new Set(Object.values(enums_1.MarkerType));
8
+ const VALID_PRIORITIES = new Set(Object.values(enums_1.Priority));
9
+ const VALID_RISKS = new Set(Object.values(enums_1.RiskLevel));
10
+ const VALID_STATUSES = new Set(Object.values(enums_1.Status));
11
+ function validateNotation(notation) {
12
+ const errors = [];
13
+ if (!notation.id) {
14
+ errors.push({ notationId: notation.id, field: 'id', message: 'Missing id' });
15
+ }
16
+ if (!VALID_TYPES.has(notation.type)) {
17
+ errors.push({ notationId: notation.id, field: 'type', message: `Invalid type: ${notation.type}` });
18
+ }
19
+ if (!notation.description) {
20
+ errors.push({ notationId: notation.id, field: 'description', message: 'Missing description' });
21
+ }
22
+ if (!notation.location?.file) {
23
+ errors.push({ notationId: notation.id, field: 'location', message: 'Missing file location' });
24
+ }
25
+ if (!VALID_STATUSES.has(notation.status)) {
26
+ errors.push({ notationId: notation.id, field: 'status', message: `Invalid status: ${notation.status}` });
27
+ }
28
+ if (notation.priority && !VALID_PRIORITIES.has(notation.priority)) {
29
+ errors.push({ notationId: notation.id, field: 'priority', message: `Invalid priority: ${notation.priority}` });
30
+ }
31
+ if (notation.risk && !VALID_RISKS.has(notation.risk)) {
32
+ errors.push({ notationId: notation.id, field: 'risk', message: `Invalid risk: ${notation.risk}` });
33
+ }
34
+ return errors;
35
+ }
36
+ function validateAll(notations) {
37
+ const errors = [];
38
+ const idSet = new Set();
39
+ for (const n of notations) {
40
+ errors.push(...validateNotation(n));
41
+ if (idSet.has(n.id)) {
42
+ errors.push({ notationId: n.id, field: 'id', message: `Duplicate id: ${n.id}` });
43
+ }
44
+ idSet.add(n.id);
45
+ // Check reference integrity
46
+ for (const rel of n.relationships) {
47
+ if (!notations.some((other) => other.id === rel)) {
48
+ errors.push({ notationId: n.id, field: 'relationships', message: `Unknown reference: ${rel}` });
49
+ }
50
+ }
51
+ }
52
+ // Check circular dependencies
53
+ const cycles = (0, relationship_manager_1.detectCircularDependencies)(notations);
54
+ for (const cycle of cycles) {
55
+ errors.push({
56
+ notationId: cycle[0],
57
+ field: 'relationships',
58
+ message: `Circular dependency: ${cycle.join(' → ')}`,
59
+ });
60
+ }
61
+ return errors;
62
+ }
@@ -0,0 +1,3 @@
1
+ import type { NotationAction } from '../types';
2
+ export declare function parseActions(bodyLines: string[]): NotationAction[];
3
+ //# sourceMappingURL=action-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-parser.d.ts","sourceRoot":"","sources":["../../src/scanner/action-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,UAAU,CAAA;AAmF1D,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAkBlE"}