@mmnto/cli 1.5.9 → 1.5.11

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 (97) hide show
  1. package/dist/adapters/github-cli-pr.d.ts +7 -0
  2. package/dist/adapters/github-cli-pr.d.ts.map +1 -1
  3. package/dist/adapters/github-cli-pr.js +31 -1
  4. package/dist/adapters/github-cli-pr.js.map +1 -1
  5. package/dist/adapters/pr-adapter.d.ts +7 -0
  6. package/dist/adapters/pr-adapter.d.ts.map +1 -1
  7. package/dist/commands/check.d.ts +6 -0
  8. package/dist/commands/check.d.ts.map +1 -0
  9. package/dist/commands/check.js +34 -0
  10. package/dist/commands/check.js.map +1 -0
  11. package/dist/commands/check.test.d.ts +2 -0
  12. package/dist/commands/check.test.d.ts.map +1 -0
  13. package/dist/commands/check.test.js +60 -0
  14. package/dist/commands/check.test.js.map +1 -0
  15. package/dist/commands/docs.d.ts +8 -1
  16. package/dist/commands/docs.d.ts.map +1 -1
  17. package/dist/commands/docs.js +13 -10
  18. package/dist/commands/docs.js.map +1 -1
  19. package/dist/commands/docs.test.js +22 -2
  20. package/dist/commands/docs.test.js.map +1 -1
  21. package/dist/commands/extract.d.ts +1 -0
  22. package/dist/commands/extract.d.ts.map +1 -1
  23. package/dist/commands/extract.js +1 -1
  24. package/dist/commands/extract.js.map +1 -1
  25. package/dist/commands/ledger-analyzer.d.ts.map +1 -1
  26. package/dist/commands/ledger-analyzer.js +2 -0
  27. package/dist/commands/ledger-analyzer.js.map +1 -1
  28. package/dist/commands/review-learn.d.ts.map +1 -1
  29. package/dist/commands/review-learn.js +38 -1
  30. package/dist/commands/review-learn.js.map +1 -1
  31. package/dist/commands/shield-incremental.test.d.ts +2 -0
  32. package/dist/commands/shield-incremental.test.d.ts.map +1 -0
  33. package/dist/commands/shield-incremental.test.js +164 -0
  34. package/dist/commands/shield-incremental.test.js.map +1 -0
  35. package/dist/commands/shield.d.ts +9 -0
  36. package/dist/commands/shield.d.ts.map +1 -1
  37. package/dist/commands/shield.js +145 -13
  38. package/dist/commands/shield.js.map +1 -1
  39. package/dist/commands/status.d.ts +2 -0
  40. package/dist/commands/status.d.ts.map +1 -0
  41. package/dist/commands/status.js +88 -0
  42. package/dist/commands/status.js.map +1 -0
  43. package/dist/commands/status.test.d.ts +2 -0
  44. package/dist/commands/status.test.d.ts.map +1 -0
  45. package/dist/commands/status.test.js +131 -0
  46. package/dist/commands/status.test.js.map +1 -0
  47. package/dist/commands/triage-pr.d.ts +4 -1
  48. package/dist/commands/triage-pr.d.ts.map +1 -1
  49. package/dist/commands/triage-pr.js +163 -3
  50. package/dist/commands/triage-pr.js.map +1 -1
  51. package/dist/exemptions/__tests__/exemption-engine.test.d.ts +2 -0
  52. package/dist/exemptions/__tests__/exemption-engine.test.d.ts.map +1 -0
  53. package/dist/exemptions/__tests__/exemption-engine.test.js +317 -0
  54. package/dist/exemptions/__tests__/exemption-engine.test.js.map +1 -0
  55. package/dist/exemptions/exemption-engine.d.ts +27 -0
  56. package/dist/exemptions/exemption-engine.d.ts.map +1 -0
  57. package/dist/exemptions/exemption-engine.js +193 -0
  58. package/dist/exemptions/exemption-engine.js.map +1 -0
  59. package/dist/exemptions/exemption-schema.d.ts +127 -0
  60. package/dist/exemptions/exemption-schema.d.ts.map +1 -0
  61. package/dist/exemptions/exemption-schema.js +32 -0
  62. package/dist/exemptions/exemption-schema.js.map +1 -0
  63. package/dist/exemptions/exemption-store.d.ts +22 -0
  64. package/dist/exemptions/exemption-store.d.ts.map +1 -0
  65. package/dist/exemptions/exemption-store.js +80 -0
  66. package/dist/exemptions/exemption-store.js.map +1 -0
  67. package/dist/git.d.ts +25 -0
  68. package/dist/git.d.ts.map +1 -1
  69. package/dist/git.js +60 -0
  70. package/dist/git.js.map +1 -1
  71. package/dist/git.test.js +98 -2
  72. package/dist/git.test.js.map +1 -1
  73. package/dist/index.js +37 -4
  74. package/dist/index.js.map +1 -1
  75. package/dist/parsers/bot-review-parser.d.ts +8 -0
  76. package/dist/parsers/bot-review-parser.d.ts.map +1 -1
  77. package/dist/parsers/bot-review-parser.js +48 -11
  78. package/dist/parsers/bot-review-parser.js.map +1 -1
  79. package/dist/parsers/bot-review-parser.test.js +108 -1
  80. package/dist/parsers/bot-review-parser.test.js.map +1 -1
  81. package/dist/services/__tests__/deferred-issuer.test.d.ts +2 -0
  82. package/dist/services/__tests__/deferred-issuer.test.d.ts.map +1 -0
  83. package/dist/services/__tests__/deferred-issuer.test.js +114 -0
  84. package/dist/services/__tests__/deferred-issuer.test.js.map +1 -0
  85. package/dist/services/deferred-issuer.d.ts +17 -0
  86. package/dist/services/deferred-issuer.d.ts.map +1 -0
  87. package/dist/services/deferred-issuer.js +64 -0
  88. package/dist/services/deferred-issuer.js.map +1 -0
  89. package/dist/utils/__tests__/milestone-inference.test.d.ts +2 -0
  90. package/dist/utils/__tests__/milestone-inference.test.d.ts.map +1 -0
  91. package/dist/utils/__tests__/milestone-inference.test.js +35 -0
  92. package/dist/utils/__tests__/milestone-inference.test.js.map +1 -0
  93. package/dist/utils/milestone-inference.d.ts +6 -0
  94. package/dist/utils/milestone-inference.d.ts.map +1 -0
  95. package/dist/utils/milestone-inference.js +15 -0
  96. package/dist/utils/milestone-inference.js.map +1 -0
  97. package/package.json +2 -2
@@ -0,0 +1,114 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { createDeferredIssue, isAlreadyDeferred } from '../deferred-issuer.js';
3
+ // ─── Helpers ───────────────────────────────────────────
4
+ function makeMockAdapter(issueUrl = 'https://github.com/owner/repo/issues/42') {
5
+ return {
6
+ fetchOpenPRs: () => [],
7
+ fetchPr: () => ({
8
+ number: 1,
9
+ title: '',
10
+ body: '',
11
+ state: 'open',
12
+ comments: [],
13
+ reviews: [],
14
+ }),
15
+ fetchReviewComments: () => [],
16
+ createIssue: vi.fn().mockReturnValue(issueUrl),
17
+ replyToComment: vi.fn(),
18
+ };
19
+ }
20
+ function makeThread(botBody, humanReplies = [], opts) {
21
+ return {
22
+ path: opts?.path ?? 'src/foo.ts',
23
+ diffHunk: opts?.diffHunk ?? '@@ -1,3 +1,5 @@',
24
+ comments: [
25
+ { id: opts?.rootId ?? 100, author: 'coderabbitai[bot]', body: botBody },
26
+ ...humanReplies.map((body) => ({ author: 'dev', body })),
27
+ ],
28
+ };
29
+ }
30
+ // ─── isAlreadyDeferred ─────────────────────────────────
31
+ describe('isAlreadyDeferred', () => {
32
+ it('returns true when a comment contains "Deferred to #123"', () => {
33
+ const thread = makeThread('some finding', ['Deferred to #123']);
34
+ expect(isAlreadyDeferred(thread)).toBe(true);
35
+ });
36
+ it('returns true when a comment contains "Deferred to issue #456"', () => {
37
+ const thread = makeThread('some finding', ['Deferred to issue #456']);
38
+ expect(isAlreadyDeferred(thread)).toBe(true);
39
+ });
40
+ it('returns false when no deferred marker present', () => {
41
+ const thread = makeThread('some finding', ['Will fix later']);
42
+ expect(isAlreadyDeferred(thread)).toBe(false);
43
+ });
44
+ it('returns false for empty thread', () => {
45
+ const thread = {
46
+ path: 'src/foo.ts',
47
+ diffHunk: '@@ -1,3 +1,5 @@',
48
+ comments: [],
49
+ };
50
+ expect(isAlreadyDeferred(thread)).toBe(false);
51
+ });
52
+ });
53
+ // ─── createDeferredIssue ───────────────────────────────
54
+ describe('createDeferredIssue', () => {
55
+ it('creates issue with correct title, body, and labels', () => {
56
+ const adapter = makeMockAdapter();
57
+ const thread = makeThread('Missing error handler in async route');
58
+ createDeferredIssue(adapter, 99, thread, '1.6.0');
59
+ expect(adapter.createIssue).toHaveBeenCalledOnce();
60
+ const call = adapter.createIssue.mock.calls[0][0];
61
+ expect(call.title).toBe('Deferred: Missing error handler in async route');
62
+ expect(call.body).toContain('src/foo.ts');
63
+ expect(call.body).toContain('#99');
64
+ expect(call.body).toContain('coderabbitai[bot]');
65
+ expect(call.labels).toEqual(['tech-debt', 'deferred']);
66
+ expect(call.milestone).toBe('1.7.0');
67
+ });
68
+ it('replies on thread with "Deferred to #NNN"', () => {
69
+ const adapter = makeMockAdapter();
70
+ const thread = makeThread('Some finding', [], { rootId: 200 });
71
+ createDeferredIssue(adapter, 99, thread, '1.6.0');
72
+ expect(adapter.replyToComment).toHaveBeenCalledWith(99, 200, 'Deferred to #42');
73
+ });
74
+ it('skips if thread already deferred (idempotent)', () => {
75
+ const adapter = makeMockAdapter();
76
+ const thread = makeThread('Finding', ['Deferred to #10']);
77
+ const result = createDeferredIssue(adapter, 99, thread, '1.6.0');
78
+ expect(result.skipped).toBe(true);
79
+ expect(adapter.createIssue).not.toHaveBeenCalled();
80
+ expect(adapter.replyToComment).not.toHaveBeenCalled();
81
+ });
82
+ it('skips if no bot comment (empty thread)', () => {
83
+ const adapter = makeMockAdapter();
84
+ const thread = {
85
+ path: 'src/foo.ts',
86
+ diffHunk: '@@ -1,3 +1,5 @@',
87
+ comments: [],
88
+ };
89
+ const result = createDeferredIssue(adapter, 99, thread, '1.6.0');
90
+ expect(result.skipped).toBe(true);
91
+ expect(adapter.createIssue).not.toHaveBeenCalled();
92
+ });
93
+ it('infers next milestone from current', () => {
94
+ const adapter = makeMockAdapter();
95
+ const thread = makeThread('Finding');
96
+ createDeferredIssue(adapter, 99, thread, 'v2.0.0');
97
+ const call = adapter.createIssue.mock.calls[0][0];
98
+ expect(call.milestone).toBe('v2.1.0');
99
+ });
100
+ it('handles replyToComment failure gracefully (issue still created)', () => {
101
+ const adapter = makeMockAdapter();
102
+ adapter.replyToComment.mockImplementation(() => {
103
+ throw new Error('network timeout');
104
+ });
105
+ const thread = makeThread('Finding');
106
+ const logs = [];
107
+ const result = createDeferredIssue(adapter, 99, thread, '1.6.0', (msg) => logs.push(msg));
108
+ expect(result.skipped).toBe(false);
109
+ expect(result.issueUrl).toBe('https://github.com/owner/repo/issues/42');
110
+ expect(adapter.createIssue).toHaveBeenCalledOnce();
111
+ expect(logs.some((l) => l.includes('Failed to reply on thread'))).toBe(true);
112
+ });
113
+ });
114
+ //# sourceMappingURL=deferred-issuer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferred-issuer.test.js","sourceRoot":"","sources":["../../../src/services/__tests__/deferred-issuer.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { PrAdapter } from '../adapters/pr-adapter.js';
2
+ import type { CommentThread } from '../parsers/bot-review-parser.js';
3
+ export interface DeferredIssueResult {
4
+ issueUrl: string;
5
+ issueNumber: string;
6
+ skipped: boolean;
7
+ }
8
+ /**
9
+ * Check if a thread already has a deferred issue link.
10
+ */
11
+ export declare function isAlreadyDeferred(thread: CommentThread): boolean;
12
+ /**
13
+ * Create a GitHub issue for a deferred bot review finding and reply on the thread.
14
+ * Returns the issue URL, or skips if already deferred (idempotent).
15
+ */
16
+ export declare function createDeferredIssue(adapter: PrAdapter, prNumber: number, thread: CommentThread, currentMilestone: string | undefined, onLog?: (msg: string) => void): DeferredIssueResult;
17
+ //# sourceMappingURL=deferred-issuer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferred-issuer.d.ts","sourceRoot":"","sources":["../../src/services/deferred-issuer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAKrE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAEhE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,EACrB,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC5B,mBAAmB,CA0DrB"}
@@ -0,0 +1,64 @@
1
+ import { inferNextMilestone } from '../utils/milestone-inference.js';
2
+ const DEFERRED_MARKER = /Deferred to (?:issue )?#(\d+)/i;
3
+ /**
4
+ * Check if a thread already has a deferred issue link.
5
+ */
6
+ export function isAlreadyDeferred(thread) {
7
+ return thread.comments.some((c) => DEFERRED_MARKER.test(c.body));
8
+ }
9
+ /**
10
+ * Create a GitHub issue for a deferred bot review finding and reply on the thread.
11
+ * Returns the issue URL, or skips if already deferred (idempotent).
12
+ */
13
+ export function createDeferredIssue(adapter, prNumber, thread, currentMilestone, onLog) {
14
+ // Idempotency check
15
+ if (isAlreadyDeferred(thread)) {
16
+ onLog?.(`Thread on ${thread.path} already deferred — skipping`);
17
+ return { issueUrl: '', issueNumber: '', skipped: true };
18
+ }
19
+ const botComment = thread.comments[0];
20
+ if (!botComment) {
21
+ return { issueUrl: '', issueNumber: '', skipped: true };
22
+ }
23
+ // Build issue content
24
+ const summary = botComment.body.slice(0, 120).replace(/\n/g, ' ').trim();
25
+ const title = `Deferred: ${summary}`;
26
+ const hunkMatch = thread.diffHunk.match(/@@ .+?\+(\d+)/);
27
+ const lineRef = hunkMatch ? `:${hunkMatch[1]}` : '';
28
+ const body = [
29
+ `## Deferred Bot Review Finding`,
30
+ '',
31
+ `**File:** \`${thread.path}${lineRef}\``,
32
+ `**PR:** #${prNumber}`,
33
+ `**Bot:** ${botComment.author}`,
34
+ '',
35
+ '### Finding',
36
+ '',
37
+ botComment.body,
38
+ ].join('\n');
39
+ const nextMilestone = inferNextMilestone(currentMilestone);
40
+ // Create the issue
41
+ const issueUrl = adapter.createIssue({
42
+ title,
43
+ body,
44
+ labels: ['tech-debt', 'deferred'],
45
+ milestone: nextMilestone,
46
+ });
47
+ // Extract issue number from URL (e.g., "https://github.com/owner/repo/issues/123")
48
+ const issueNumMatch = issueUrl.match(/\/issues\/(\d+)/);
49
+ const issueNumber = issueNumMatch ? issueNumMatch[1] : '';
50
+ // Reply on the thread with the issue link
51
+ const rootCommentId = thread.comments[0].id;
52
+ if (rootCommentId !== undefined && issueNumber) {
53
+ try {
54
+ adapter.replyToComment(prNumber, rootCommentId, `Deferred to #${issueNumber}`);
55
+ }
56
+ catch (err) {
57
+ // Non-fatal — issue was created, reply is best-effort
58
+ const msg = err instanceof Error ? err.message : String(err);
59
+ onLog?.(`Failed to reply on thread (issue created): ${msg}`);
60
+ }
61
+ }
62
+ return { issueUrl, issueNumber, skipped: false };
63
+ }
64
+ //# sourceMappingURL=deferred-issuer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferred-issuer.js","sourceRoot":"","sources":["../../src/services/deferred-issuer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=milestone-inference.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone-inference.test.d.ts","sourceRoot":"","sources":["../../../src/utils/__tests__/milestone-inference.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { inferNextMilestone } from '../milestone-inference.js';
3
+ describe('inferNextMilestone', () => {
4
+ it('bumps minor version: 1.6.0 → 1.7.0', () => {
5
+ expect(inferNextMilestone('1.6.0')).toBe('1.7.0');
6
+ });
7
+ it('preserves v prefix: v1.6.0 → v1.7.0', () => {
8
+ expect(inferNextMilestone('v1.6.0')).toBe('v1.7.0');
9
+ });
10
+ it('strips title suffix: 1.6.0 — Pipeline Maturity → 1.7.0', () => {
11
+ expect(inferNextMilestone('1.6.0 — Pipeline Maturity')).toBe('1.7.0');
12
+ });
13
+ it('bumps major milestone: v2.0.0 → v2.1.0', () => {
14
+ expect(inferNextMilestone('v2.0.0')).toBe('v2.1.0');
15
+ });
16
+ it('bumps from 0.0.1 → 0.1.0', () => {
17
+ expect(inferNextMilestone('0.0.1')).toBe('0.1.0');
18
+ });
19
+ it('returns undefined for non-semver string: Sprint 4', () => {
20
+ expect(inferNextMilestone('Sprint 4')).toBeUndefined();
21
+ });
22
+ it('returns undefined for non-semver string: MVP', () => {
23
+ expect(inferNextMilestone('MVP')).toBeUndefined();
24
+ });
25
+ it('returns undefined for undefined input', () => {
26
+ expect(inferNextMilestone(undefined)).toBeUndefined();
27
+ });
28
+ it('returns undefined for null input', () => {
29
+ expect(inferNextMilestone(null)).toBeUndefined();
30
+ });
31
+ it('returns undefined for empty string', () => {
32
+ expect(inferNextMilestone('')).toBeUndefined();
33
+ });
34
+ });
35
+ //# sourceMappingURL=milestone-inference.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone-inference.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/milestone-inference.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Infer the next milestone from the current one by bumping the minor version.
3
+ * Returns undefined for non-semver strings.
4
+ */
5
+ export declare function inferNextMilestone(current: string | undefined | null): string | undefined;
6
+ //# sourceMappingURL=milestone-inference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone-inference.d.ts","sourceRoot":"","sources":["../../src/utils/milestone-inference.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAOzF"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Infer the next milestone from the current one by bumping the minor version.
3
+ * Returns undefined for non-semver strings.
4
+ */
5
+ export function inferNextMilestone(current) {
6
+ if (!current)
7
+ return undefined;
8
+ // Match milestone titles like "1.6.0", "v1.6.0", "1.6.0 — Pipeline Maturity"
9
+ const match = current.match(/^(v?)(\d+)\.(\d+)\.(\d+)/);
10
+ if (!match)
11
+ return undefined;
12
+ const [, prefix, major, minor, _patch] = match;
13
+ return `${prefix}${major}.${Number(minor) + 1}.0`;
14
+ }
15
+ //# sourceMappingURL=milestone-inference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone-inference.js","sourceRoot":"","sources":["../../src/utils/milestone-inference.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAkC;IACnE,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,6EAA6E;IAC7E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IAC/C,OAAO,GAAG,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmnto/cli",
3
- "version": "1.5.9",
3
+ "version": "1.5.11",
4
4
  "description": "CLI for Totem — AI persistent memory and context layer",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "smol-toml": "^1.6.1",
24
24
  "yaml": "^2.4.0",
25
25
  "zod": "^3.24.0",
26
- "@mmnto/totem": "1.5.9"
26
+ "@mmnto/totem": "1.5.11"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@anthropic-ai/sdk": "^0.78.0",