geeto 0.6.6 → 0.9.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 (198) hide show
  1. package/README.md +23 -9
  2. package/lib/api/copilot-adapter.d.ts +14 -5
  3. package/lib/api/copilot-adapter.d.ts.map +1 -1
  4. package/lib/api/copilot-adapter.js +15 -21
  5. package/lib/api/copilot-adapter.js.map +1 -1
  6. package/lib/api/copilot-sdk.d.ts +3 -16
  7. package/lib/api/copilot-sdk.d.ts.map +1 -1
  8. package/lib/api/copilot-sdk.js +166 -456
  9. package/lib/api/copilot-sdk.js.map +1 -1
  10. package/lib/api/copilot.d.ts +3 -4
  11. package/lib/api/copilot.d.ts.map +1 -1
  12. package/lib/api/copilot.js +28 -28
  13. package/lib/api/copilot.js.map +1 -1
  14. package/lib/api/gemini-sdk.d.ts.map +1 -1
  15. package/lib/api/gemini-sdk.js +11 -77
  16. package/lib/api/gemini-sdk.js.map +1 -1
  17. package/lib/api/gemini.d.ts +2 -2
  18. package/lib/api/gemini.d.ts.map +1 -1
  19. package/lib/api/gemini.js +24 -19
  20. package/lib/api/gemini.js.map +1 -1
  21. package/lib/api/gitlab.d.ts +80 -0
  22. package/lib/api/gitlab.d.ts.map +1 -0
  23. package/lib/api/gitlab.js +192 -0
  24. package/lib/api/gitlab.js.map +1 -0
  25. package/lib/api/openrouter-sdk.d.ts.map +1 -1
  26. package/lib/api/openrouter-sdk.js +11 -76
  27. package/lib/api/openrouter-sdk.js.map +1 -1
  28. package/lib/api/openrouter.d.ts.map +1 -1
  29. package/lib/api/openrouter.js +2 -16
  30. package/lib/api/openrouter.js.map +1 -1
  31. package/lib/api/platform.d.ts +78 -0
  32. package/lib/api/platform.d.ts.map +1 -0
  33. package/lib/api/platform.js +218 -0
  34. package/lib/api/platform.js.map +1 -0
  35. package/lib/cli/input.d.ts +2 -2
  36. package/lib/cli/input.d.ts.map +1 -1
  37. package/lib/cli/input.js +23 -27
  38. package/lib/cli/input.js.map +1 -1
  39. package/lib/cli/menu.d.ts +1 -1
  40. package/lib/cli/menu.d.ts.map +1 -1
  41. package/lib/cli/menu.js +123 -100
  42. package/lib/cli/menu.js.map +1 -1
  43. package/lib/core/copilot-setup.d.ts +8 -7
  44. package/lib/core/copilot-setup.d.ts.map +1 -1
  45. package/lib/core/copilot-setup.js +59 -230
  46. package/lib/core/copilot-setup.js.map +1 -1
  47. package/lib/core/gemini-setup.js +7 -7
  48. package/lib/core/gemini-setup.js.map +1 -1
  49. package/lib/core/gitlab-setup.d.ts +5 -0
  50. package/lib/core/gitlab-setup.d.ts.map +1 -0
  51. package/lib/core/gitlab-setup.js +85 -0
  52. package/lib/core/gitlab-setup.js.map +1 -0
  53. package/lib/core/openrouter-setup.d.ts.map +1 -1
  54. package/lib/core/openrouter-setup.js +17 -0
  55. package/lib/core/openrouter-setup.js.map +1 -1
  56. package/lib/index.js +501 -704
  57. package/lib/index.js.map +1 -1
  58. package/lib/types/index.d.ts +10 -6
  59. package/lib/types/index.d.ts.map +1 -1
  60. package/lib/utils/ai-provider-helpers.d.ts +5 -0
  61. package/lib/utils/ai-provider-helpers.d.ts.map +1 -0
  62. package/lib/utils/ai-provider-helpers.js +23 -0
  63. package/lib/utils/ai-provider-helpers.js.map +1 -0
  64. package/lib/utils/ai-text.d.ts +23 -0
  65. package/lib/utils/ai-text.d.ts.map +1 -0
  66. package/lib/utils/ai-text.js +57 -0
  67. package/lib/utils/ai-text.js.map +1 -0
  68. package/lib/utils/ai-workflow.d.ts +18 -0
  69. package/lib/utils/ai-workflow.d.ts.map +1 -0
  70. package/lib/utils/ai-workflow.js +66 -0
  71. package/lib/utils/ai-workflow.js.map +1 -0
  72. package/lib/utils/branch-naming.d.ts.map +1 -1
  73. package/lib/utils/branch-naming.js +1 -3
  74. package/lib/utils/branch-naming.js.map +1 -1
  75. package/lib/utils/config.d.ts +13 -1
  76. package/lib/utils/config.d.ts.map +1 -1
  77. package/lib/utils/config.js +38 -1
  78. package/lib/utils/config.js.map +1 -1
  79. package/lib/utils/display.d.ts.map +1 -1
  80. package/lib/utils/display.js +4 -3
  81. package/lib/utils/display.js.map +1 -1
  82. package/lib/utils/git-ai.js +13 -13
  83. package/lib/utils/git-ai.js.map +1 -1
  84. package/lib/utils/git-errors.d.ts.map +1 -1
  85. package/lib/utils/git-errors.js +2 -6
  86. package/lib/utils/git-errors.js.map +1 -1
  87. package/lib/utils/git.d.ts.map +1 -1
  88. package/lib/utils/git.js +5 -0
  89. package/lib/utils/git.js.map +1 -1
  90. package/lib/utils/github-helpers.d.ts +33 -0
  91. package/lib/utils/github-helpers.d.ts.map +1 -0
  92. package/lib/utils/github-helpers.js +101 -0
  93. package/lib/utils/github-helpers.js.map +1 -0
  94. package/lib/utils/prompt-loader.d.ts +9 -0
  95. package/lib/utils/prompt-loader.d.ts.map +1 -0
  96. package/lib/utils/prompt-loader.js +42 -0
  97. package/lib/utils/prompt-loader.js.map +1 -0
  98. package/lib/utils/prompts-embedded.d.ts +2 -0
  99. package/lib/utils/prompts-embedded.d.ts.map +1 -0
  100. package/lib/utils/prompts-embedded.js +255 -0
  101. package/lib/utils/prompts-embedded.js.map +1 -0
  102. package/lib/utils/scramble.d.ts +9 -86
  103. package/lib/utils/scramble.d.ts.map +1 -1
  104. package/lib/utils/scramble.js +27 -279
  105. package/lib/utils/scramble.js.map +1 -1
  106. package/lib/version.d.ts +1 -1
  107. package/lib/version.js +1 -1
  108. package/lib/workflows/alias.d.ts.map +1 -1
  109. package/lib/workflows/alias.js +1 -0
  110. package/lib/workflows/alias.js.map +1 -1
  111. package/lib/workflows/amend.d.ts.map +1 -1
  112. package/lib/workflows/amend.js +1 -5
  113. package/lib/workflows/amend.js.map +1 -1
  114. package/lib/workflows/branch-helpers.d.ts.map +1 -1
  115. package/lib/workflows/branch-helpers.js +0 -1
  116. package/lib/workflows/branch-helpers.js.map +1 -1
  117. package/lib/workflows/commit.d.ts.map +1 -1
  118. package/lib/workflows/commit.js +160 -187
  119. package/lib/workflows/commit.js.map +1 -1
  120. package/lib/workflows/doctor.d.ts +7 -0
  121. package/lib/workflows/doctor.d.ts.map +1 -0
  122. package/lib/workflows/doctor.js +284 -0
  123. package/lib/workflows/doctor.js.map +1 -0
  124. package/lib/workflows/issue.d.ts +1 -1
  125. package/lib/workflows/issue.d.ts.map +1 -1
  126. package/lib/workflows/issue.js +28 -115
  127. package/lib/workflows/issue.js.map +1 -1
  128. package/lib/workflows/main-helpers.d.ts +34 -0
  129. package/lib/workflows/main-helpers.d.ts.map +1 -0
  130. package/lib/workflows/main-helpers.js +346 -0
  131. package/lib/workflows/main-helpers.js.map +1 -0
  132. package/lib/workflows/main-steps.d.ts.map +1 -1
  133. package/lib/workflows/main-steps.js +9 -134
  134. package/lib/workflows/main-steps.js.map +1 -1
  135. package/lib/workflows/main.d.ts +2 -6
  136. package/lib/workflows/main.d.ts.map +1 -1
  137. package/lib/workflows/main.js +44 -381
  138. package/lib/workflows/main.js.map +1 -1
  139. package/lib/workflows/pr.d.ts +2 -2
  140. package/lib/workflows/pr.d.ts.map +1 -1
  141. package/lib/workflows/pr.js +49 -137
  142. package/lib/workflows/pr.js.map +1 -1
  143. package/lib/workflows/prune.d.ts.map +1 -1
  144. package/lib/workflows/prune.js +2 -10
  145. package/lib/workflows/prune.js.map +1 -1
  146. package/lib/workflows/pull.d.ts.map +1 -1
  147. package/lib/workflows/pull.js +2 -24
  148. package/lib/workflows/pull.js.map +1 -1
  149. package/lib/workflows/release-merge.d.ts +12 -0
  150. package/lib/workflows/release-merge.d.ts.map +1 -0
  151. package/lib/workflows/release-merge.js +569 -0
  152. package/lib/workflows/release-merge.js.map +1 -0
  153. package/lib/workflows/release-notes.d.ts +13 -0
  154. package/lib/workflows/release-notes.d.ts.map +1 -0
  155. package/lib/workflows/release-notes.js +141 -0
  156. package/lib/workflows/release-notes.js.map +1 -0
  157. package/lib/workflows/release-recover.d.ts +5 -0
  158. package/lib/workflows/release-recover.d.ts.map +1 -0
  159. package/lib/workflows/release-recover.js +137 -0
  160. package/lib/workflows/release-recover.js.map +1 -0
  161. package/lib/workflows/release-sync.d.ts +7 -0
  162. package/lib/workflows/release-sync.d.ts.map +1 -0
  163. package/lib/workflows/release-sync.js +321 -0
  164. package/lib/workflows/release-sync.js.map +1 -0
  165. package/lib/workflows/release-utils.d.ts +36 -0
  166. package/lib/workflows/release-utils.d.ts.map +1 -0
  167. package/lib/workflows/release-utils.js +150 -0
  168. package/lib/workflows/release-utils.js.map +1 -0
  169. package/lib/workflows/release.d.ts.map +1 -1
  170. package/lib/workflows/release.js +92 -719
  171. package/lib/workflows/release.js.map +1 -1
  172. package/lib/workflows/repo-settings.d.ts +2 -2
  173. package/lib/workflows/repo-settings.d.ts.map +1 -1
  174. package/lib/workflows/repo-settings.js +33 -24
  175. package/lib/workflows/repo-settings.js.map +1 -1
  176. package/lib/workflows/reword.d.ts.map +1 -1
  177. package/lib/workflows/reword.js +154 -151
  178. package/lib/workflows/reword.js.map +1 -1
  179. package/lib/workflows/security-gate.d.ts.map +1 -1
  180. package/lib/workflows/security-gate.js +15 -75
  181. package/lib/workflows/security-gate.js.map +1 -1
  182. package/lib/workflows/settings.d.ts +2 -1
  183. package/lib/workflows/settings.d.ts.map +1 -1
  184. package/lib/workflows/settings.js +289 -19
  185. package/lib/workflows/settings.js.map +1 -1
  186. package/lib/workflows/trello-menu.d.ts +2 -5
  187. package/lib/workflows/trello-menu.d.ts.map +1 -1
  188. package/lib/workflows/trello-menu.js +67 -228
  189. package/lib/workflows/trello-menu.js.map +1 -1
  190. package/package.json +3 -6
  191. package/prompts/branch-name-prompt.md +4 -0
  192. package/prompts/commit-message-prompt.md +12 -0
  193. package/prompts/issue-prompt.md +19 -0
  194. package/prompts/issue-review.with-context.prompt.yml +77 -0
  195. package/prompts/pr-prompt.md +14 -0
  196. package/prompts/release-notes-prompt.md +35 -0
  197. package/prompts/repo-description-prompt.md +1 -0
  198. package/prompts/security-gate-prompt.md +80 -0
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Release notes generation — markdown formatting, RELEASE.MD and CHANGELOG.md generators
3
+ */
4
+ import { categorizeCommits, getRepoUrl } from './release-utils.js';
5
+ // ─── Markdown helpers ───
6
+ /**
7
+ * Normalize markdown spacing for consistent markdownlint-friendly output.
8
+ * Ensures: one blank line after ### and #### headings, one blank line between sections,
9
+ * no double blank lines, trailing newline.
10
+ */
11
+ export const normalizeReleaseMarkdown = (md) => {
12
+ const lines = md.split('\n');
13
+ const result = [];
14
+ for (let i = 0; i < lines.length; i++) {
15
+ const line = lines[i] ?? '';
16
+ const nextLine = lines[i + 1] ?? '';
17
+ result.push(line);
18
+ // After a heading (### or ####), ensure exactly one blank line before content
19
+ if ((line.startsWith('###') || line.startsWith('####')) && nextLine.trim() !== '') {
20
+ result.push('');
21
+ }
22
+ // After a bullet line, if next line is a heading, ensure blank line
23
+ if (line.startsWith('-') && (nextLine.startsWith('###') || nextLine.startsWith('####'))) {
24
+ result.push('');
25
+ }
26
+ }
27
+ // Collapse multiple blank lines into one
28
+ return result
29
+ .join('\n')
30
+ .replaceAll(/\n{3,}/g, '\n\n')
31
+ .trim();
32
+ };
33
+ // ─── stripConventional helpers ───
34
+ const stripFeatPrefix = (s) => {
35
+ const idx = s.indexOf(': ');
36
+ if (idx !== -1 && s.slice(0, idx).startsWith('feat'))
37
+ return s.slice(idx + 2);
38
+ return s.replace(/^feat:\s*/, '');
39
+ };
40
+ const stripFixPrefix = (s) => {
41
+ const idx = s.indexOf(': ');
42
+ if (idx !== -1 && s.slice(0, idx).startsWith('fix'))
43
+ return s.slice(idx + 2);
44
+ return s.replace(/^fix:\s*/, '');
45
+ };
46
+ const stripBreakingPrefix = (s) => {
47
+ const idx = s.indexOf('!: ');
48
+ if (idx !== -1)
49
+ return s.slice(idx + 3);
50
+ return s.replace(/BREAKING CHANGE:\s*/, '');
51
+ };
52
+ const stripConventionalPrefix = (s) => {
53
+ const idx = s.indexOf(': ');
54
+ if (idx !== -1)
55
+ return s.slice(idx + 2);
56
+ return s;
57
+ };
58
+ // ─── RELEASE.MD generator (user-facing, simple language) ───
59
+ export const generateReleaseMd = (version, commits, prevVersion) => {
60
+ const now = new Date();
61
+ const date = now.toLocaleDateString('en-US', {
62
+ year: 'numeric',
63
+ month: 'long',
64
+ day: 'numeric',
65
+ });
66
+ const cat = categorizeCommits(commits);
67
+ // Each version is a ## section so multiple versions stack in a single file
68
+ const header = [
69
+ `## v${version} — ${date}`,
70
+ '',
71
+ `> Previous version: v${prevVersion}`,
72
+ '',
73
+ "### What's New?",
74
+ '',
75
+ ];
76
+ const featureSection = cat.features.length > 0
77
+ ? ['#### New Features', '', ...cat.features.map((f) => `- ${stripFeatPrefix(f.subject)}`), '']
78
+ : [];
79
+ const fixSection = cat.fixes.length > 0
80
+ ? ['#### Bug Fixes', '', ...cat.fixes.map((f) => `- ${stripFixPrefix(f.subject)}`), '']
81
+ : [];
82
+ const breakingSection = cat.breaking.length > 0
83
+ ? [
84
+ '#### Important Changes',
85
+ '',
86
+ '> Note: Some changes in this version may require adjustments.',
87
+ '',
88
+ ...cat.breaking.map((b) => `- ${stripBreakingPrefix(b.subject)}`),
89
+ '',
90
+ ]
91
+ : [];
92
+ const otherSection = cat.other.length > 0
93
+ ? [
94
+ '#### Other Improvements',
95
+ '',
96
+ ...cat.other.map((o) => `- ${stripConventionalPrefix(o.subject)}`),
97
+ '',
98
+ ]
99
+ : [];
100
+ const empty = commits.length === 0 ? ['No significant changes in this version.', ''] : [];
101
+ return [
102
+ ...header,
103
+ ...featureSection,
104
+ ...fixSection,
105
+ ...breakingSection,
106
+ ...otherSection,
107
+ ...empty,
108
+ '---',
109
+ '',
110
+ ].join('\n');
111
+ };
112
+ // ─── CHANGELOG.md generator (developer-facing, per-commit) ───
113
+ export const generateChangelogEntry = (version, commits, prevVersion) => {
114
+ const repoUrl = getRepoUrl();
115
+ const dateStr = new Date().toISOString().slice(0, 10);
116
+ const cat = categorizeCommits(commits);
117
+ const commitLink = (c) => repoUrl ? `[${c.short}](${repoUrl}/commit/${c.short})` : c.short;
118
+ const versionLink = repoUrl
119
+ ? `[${version}](${repoUrl}/compare/v${prevVersion}...v${version})`
120
+ : version;
121
+ const header = [`## ${versionLink} (${dateStr})`, ''];
122
+ const breakingSection = cat.breaking.length > 0
123
+ ? [
124
+ '### BREAKING CHANGES',
125
+ '',
126
+ ...cat.breaking.map((c) => `* ${c.subject} (${commitLink(c)})`),
127
+ '',
128
+ ]
129
+ : [];
130
+ const featureSection = cat.features.length > 0
131
+ ? ['### Features', '', ...cat.features.map((c) => `* ${c.subject} (${commitLink(c)})`), '']
132
+ : [];
133
+ const fixSection = cat.fixes.length > 0
134
+ ? ['### Bug Fixes', '', ...cat.fixes.map((c) => `* ${c.subject} (${commitLink(c)})`), '']
135
+ : [];
136
+ const otherSection = cat.other.length > 0
137
+ ? ['### Other Changes', '', ...cat.other.map((c) => `* ${c.subject} (${commitLink(c)})`), '']
138
+ : [];
139
+ return [...header, ...breakingSection, ...featureSection, ...fixSection, ...otherSection].join('\n');
140
+ };
141
+ //# sourceMappingURL=release-notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-notes.js","sourceRoot":"","sources":["../../src/workflows/release-notes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Release recovery — recover missing tags from release commits
3
+ */
4
+ export declare const handleRecoverTags: () => Promise<void>;
5
+ //# sourceMappingURL=release-recover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-recover.d.ts","sourceRoot":"","sources":["../../src/workflows/release-recover.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CA8ItD,CAAA"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Release recovery — recover missing tags from release commits
3
+ */
4
+ import { getExistingTags } from './release-utils.js';
5
+ import { confirm } from '../cli/input.js';
6
+ import { select } from '../cli/menu.js';
7
+ import { colors } from '../utils/colors.js';
8
+ import { BOX_W } from '../utils/display.js';
9
+ import { exec, execAsync } from '../utils/exec.js';
10
+ import { log } from '../utils/logging.js';
11
+ import { ScrambleProgress } from '../utils/scramble.js';
12
+ // ─── Recover missing tags ───
13
+ export const handleRecoverTags = async () => {
14
+ const line = '─'.repeat(BOX_W);
15
+ console.log('');
16
+ const spinner = log.spinner();
17
+ spinner.start('Scanning release commits...');
18
+ // Find all release commits: "chore(release): vX.Y.Z"
19
+ let gitLog;
20
+ try {
21
+ gitLog = exec('git log --all --oneline --grep="^chore(release): v" --format="%H %s"', true);
22
+ }
23
+ catch {
24
+ spinner.fail('Failed to scan git log');
25
+ return;
26
+ }
27
+ const releasePattern = /^([a-f0-9]+) chore\(release\): v(.+)$/;
28
+ const releaseCommits = [];
29
+ for (const logLine of gitLog.split('\n').filter(Boolean)) {
30
+ const match = logLine.match(releasePattern);
31
+ if (match?.[1] && match[2]) {
32
+ releaseCommits.push({
33
+ hash: match[1],
34
+ version: match[2],
35
+ tag: `v${match[2]}`,
36
+ });
37
+ }
38
+ }
39
+ if (releaseCommits.length === 0) {
40
+ spinner.fail('No release commits found');
41
+ return;
42
+ }
43
+ // Compare with existing tags
44
+ const existingTags = new Set(getExistingTags());
45
+ const missingTags = releaseCommits.filter((rc) => !existingTags.has(rc.tag));
46
+ spinner.succeed(`Found ${releaseCommits.length} release commits, ${existingTags.size} tags`);
47
+ if (missingTags.length === 0) {
48
+ console.log('');
49
+ log.success('All release commits have matching tags! Nothing to recover.');
50
+ return;
51
+ }
52
+ // Show missing tags
53
+ console.log('');
54
+ log.info(`${colors.bright}${missingTags.length}${colors.reset} tags missing (release commit exists but no tag):`);
55
+ for (const mt of missingTags) {
56
+ console.log(` ${colors.yellow}${mt.tag}${colors.reset} ${colors.gray}← ${mt.hash.slice(0, 7)}${colors.reset}`);
57
+ }
58
+ console.log('');
59
+ const action = await select('What do you want to do?', [
60
+ { label: 'Recover all missing tags', value: 'all' },
61
+ { label: 'Select which tags to recover', value: 'select' },
62
+ { label: 'Cancel', value: 'cancel' },
63
+ ]);
64
+ if (action === 'cancel')
65
+ return;
66
+ let tagsToRecover = missingTags;
67
+ if (action === 'select') {
68
+ const { multiSelect } = await import('../cli/menu.js');
69
+ const choices = missingTags.map((mt) => ({
70
+ label: `${mt.tag} (${mt.hash.slice(0, 7)})`,
71
+ value: mt.tag,
72
+ }));
73
+ const selected = await multiSelect('Select tags to recover:', choices);
74
+ if (selected.length === 0) {
75
+ log.info('No tags selected.');
76
+ return;
77
+ }
78
+ tagsToRecover = missingTags.filter((mt) => selected.includes(mt.tag));
79
+ }
80
+ // Preview
81
+ console.log('');
82
+ console.log(`${colors.cyan}┌${line}┐${colors.reset}`);
83
+ console.log(`${colors.cyan}│${colors.reset} ${colors.bright}Recovery Plan${colors.reset}`);
84
+ console.log(`${colors.cyan}├${line}┤${colors.reset}`);
85
+ for (const mt of tagsToRecover) {
86
+ console.log(`${colors.cyan}│${colors.reset} ${colors.green}+${colors.reset} ${colors.yellow}${mt.tag}${colors.reset} → commit ${colors.gray}${mt.hash.slice(0, 7)}${colors.reset}`);
87
+ }
88
+ console.log(`${colors.cyan}└${line}┘${colors.reset}`);
89
+ console.log('');
90
+ const proceed = confirm(`Create ${tagsToRecover.length} tags?`);
91
+ if (!proceed)
92
+ return;
93
+ let successCount = 0;
94
+ for (const mt of tagsToRecover) {
95
+ console.log('');
96
+ const tagSpinner = log.spinner();
97
+ tagSpinner.start(`Creating tag ${colors.yellow}${mt.tag}${colors.reset}...`);
98
+ try {
99
+ exec(`git tag -a ${mt.tag} ${mt.hash} -m "Release ${mt.tag}"`, true);
100
+ tagSpinner.succeed(`Tag ${mt.tag} created`);
101
+ successCount++;
102
+ }
103
+ catch (error) {
104
+ const errMsg = error instanceof Error ? error.message : String(error);
105
+ tagSpinner.fail(`Failed to create ${mt.tag}`);
106
+ log.error(` ${errMsg.split('\n')[0]}`);
107
+ }
108
+ }
109
+ console.log('');
110
+ if (successCount === tagsToRecover.length) {
111
+ log.success(`All ${successCount} tags recovered!`);
112
+ }
113
+ else {
114
+ log.warn(`${successCount}/${tagsToRecover.length} tags recovered`);
115
+ }
116
+ // Offer to push tags to remote
117
+ if (successCount > 0) {
118
+ console.log('');
119
+ const pushTags = confirm('Push recovered tags to remote?');
120
+ if (pushTags) {
121
+ console.log('');
122
+ const pushSpinner = new ScrambleProgress();
123
+ pushSpinner.start(['Pushing tags to remote']);
124
+ try {
125
+ await execAsync('git push --tags --no-verify', true);
126
+ pushSpinner.succeed('Tags pushed to remote');
127
+ }
128
+ catch (error) {
129
+ const stderr = error.stderr?.trim();
130
+ pushSpinner.fail('Failed to push tags');
131
+ if (stderr)
132
+ log.error(` ${stderr.split('\n')[0]}`);
133
+ }
134
+ }
135
+ }
136
+ };
137
+ //# sourceMappingURL=release-recover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-recover.js","sourceRoot":"","sources":["../../src/workflows/release-recover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Release sync workflows — sync GitHub/GitLab Releases for existing tags, delete releases
3
+ */
4
+ export declare const getExistingGithubReleases: (cli?: string) => Promise<string[]>;
5
+ export declare const handleSyncReleases: () => Promise<void>;
6
+ export declare const handleDeleteReleases: () => Promise<void>;
7
+ //# sourceMappingURL=release-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-sync.d.ts","sourceRoot":"","sources":["../../src/workflows/release-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2BH,eAAO,MAAM,yBAAyB,GAAU,YAAU,KAAG,OAAO,CAAC,MAAM,EAAE,CAW5E,CAAA;AAID,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,IAAI,CA4QvD,CAAA;AAID,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,IAAI,CAiFzD,CAAA"}
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Release sync workflows — sync GitHub/GitLab Releases for existing tags, delete releases
3
+ */
4
+ import { writeFileSync } from 'node:fs';
5
+ import { generateReleaseMd, normalizeReleaseMarkdown } from './release-notes.js';
6
+ import { getCommitsSinceTag, getExistingTags } from './release-utils.js';
7
+ import { confirm } from '../cli/input.js';
8
+ import { select } from '../cli/menu.js';
9
+ import { colors } from '../utils/colors.js';
10
+ import { BOX_W } from '../utils/display.js';
11
+ import { execAsync, execSilent } from '../utils/exec.js';
12
+ import { chooseModelForProvider, generateReleaseNotesWithProvider, getAIProviderShortName, getModelValue, } from '../utils/git-ai.js';
13
+ import { detectPlatformFromRemote, getPlatformCLI } from '../utils/github-helpers.js';
14
+ import { log } from '../utils/logging.js';
15
+ import { ScrambleProgress } from '../utils/scramble.js';
16
+ import { loadState } from '../utils/state.js';
17
+ // ─── Release helpers ───
18
+ export const getExistingGithubReleases = async (cli = 'gh') => {
19
+ try {
20
+ const result = await execAsync(`${cli} release list --limit 100 --json tagName --jq ".[].tagName"`, true);
21
+ const output = result.stdout.trim();
22
+ return output ? output.split('\n').filter(Boolean) : [];
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ };
28
+ // ─── Sync Releases for existing tags ───
29
+ export const handleSyncReleases = async () => {
30
+ const line = '─'.repeat(BOX_W);
31
+ // Detect platform (GitHub or GitLab)
32
+ const platform = detectPlatformFromRemote();
33
+ const cli = platform ? getPlatformCLI(platform) : 'gh';
34
+ const platformName = platform === 'gitlab' ? 'GitLab' : 'GitHub';
35
+ // Check if platform CLI is available
36
+ try {
37
+ execSilent(`${cli} --version`);
38
+ }
39
+ catch {
40
+ log.error(`${platformName} CLI (${cli}) is not installed.${cli === 'gh' ? ' Install it: https://cli.github.com' : ' Install it: https://gitlab.com/gitlab-org/cli'}`);
41
+ return;
42
+ }
43
+ console.log('');
44
+ const spinner = new ScrambleProgress();
45
+ spinner.start([`Fetching tags and ${platformName} releases`]);
46
+ const localTags = getExistingTags();
47
+ const ghReleases = await getExistingGithubReleases(cli);
48
+ const missingTags = localTags.filter((t) => !ghReleases.includes(t));
49
+ spinner.succeed(`Found ${localTags.length} tags, ${ghReleases.length} ${platformName} releases`);
50
+ if (missingTags.length === 0) {
51
+ console.log('');
52
+ log.success(`All tags have ${platformName} Releases! Nothing to sync.`);
53
+ return;
54
+ }
55
+ console.log('');
56
+ log.info(`${colors.bright}${missingTags.length}${colors.reset} tags missing ${platformName} Releases:`);
57
+ for (const tag of missingTags) {
58
+ console.log(` ${colors.yellow}${tag}${colors.reset}`);
59
+ }
60
+ console.log('');
61
+ const action = await select('What do you want to do?', [
62
+ { label: 'Create releases for all missing tags', value: 'all' },
63
+ { label: 'Select which tags to release', value: 'select' },
64
+ { label: 'Cancel', value: 'cancel' },
65
+ ]);
66
+ if (action === 'cancel')
67
+ return;
68
+ let tagsToRelease = missingTags;
69
+ if (action === 'select') {
70
+ const { multiSelect } = await import('../cli/menu.js');
71
+ const choices = missingTags.map((t) => ({ label: t, value: t }));
72
+ const selected = await multiSelect('Select tags to create releases for:', choices);
73
+ if (selected.length === 0) {
74
+ log.info('No tags selected.');
75
+ return;
76
+ }
77
+ tagsToRelease = selected;
78
+ }
79
+ // Choose release notes mode: AI or template
80
+ console.log('');
81
+ const notesMode = await select('How should release notes be generated?', [
82
+ { label: 'AI-generated (recommended)', value: 'ai' },
83
+ { label: 'Auto-generate (template-based)', value: 'auto' },
84
+ ]);
85
+ // AI setup if needed
86
+ let useAI = notesMode === 'ai';
87
+ let language = 'en';
88
+ let aiProvider = 'copilot';
89
+ let copilotModel;
90
+ let openrouterModel;
91
+ let geminiModel;
92
+ if (useAI) {
93
+ language = (await select('Release notes language:', [
94
+ { label: 'English', value: 'en' },
95
+ { label: 'Indonesian (Bahasa Indonesia)', value: 'id' },
96
+ ]));
97
+ // Check saved config
98
+ const savedState = loadState();
99
+ if (savedState?.aiProvider &&
100
+ savedState.aiProvider !== 'manual' &&
101
+ (savedState.copilotModel || savedState.openrouterModel || savedState.geminiModel)) {
102
+ aiProvider = savedState.aiProvider;
103
+ copilotModel = savedState.copilotModel;
104
+ openrouterModel = savedState.openrouterModel;
105
+ geminiModel = savedState.geminiModel;
106
+ }
107
+ else {
108
+ let providerChosen = false;
109
+ while (!providerChosen) {
110
+ aiProvider = (await select('Choose AI Provider:', [
111
+ { label: 'GitHub (Recommended)', value: 'copilot' },
112
+ { label: 'Gemini', value: 'gemini' },
113
+ { label: 'OpenRouter', value: 'openrouter' },
114
+ ]));
115
+ const chosen = await chooseModelForProvider(aiProvider, undefined, 'Back to AI provider menu');
116
+ if (!chosen || chosen === 'back')
117
+ continue;
118
+ switch (aiProvider) {
119
+ case 'gemini': {
120
+ geminiModel = chosen;
121
+ break;
122
+ }
123
+ case 'copilot': {
124
+ copilotModel = chosen;
125
+ break;
126
+ }
127
+ case 'openrouter': {
128
+ openrouterModel = chosen;
129
+ break;
130
+ }
131
+ }
132
+ providerChosen = true;
133
+ }
134
+ }
135
+ }
136
+ // Preview and confirm
137
+ console.log('');
138
+ console.log(`${colors.cyan}┌${line}┐${colors.reset}`);
139
+ console.log(`${colors.cyan}│${colors.reset} ${colors.bright}Sync Plan${colors.reset}`);
140
+ console.log(`${colors.cyan}├${line}┤${colors.reset}`);
141
+ for (const tag of tagsToRelease) {
142
+ const ver = tag.replace(/^v/, '');
143
+ console.log(`${colors.cyan}│${colors.reset} ${colors.green}+${colors.reset} Create release for ${colors.yellow}${ver}${colors.reset}`);
144
+ }
145
+ console.log(`${colors.cyan}│${colors.reset} ${colors.gray}Mode: ${useAI ? 'AI-generated' : 'Template-based'}${colors.reset}`);
146
+ console.log(`${colors.cyan}└${line}┘${colors.reset}`);
147
+ console.log('');
148
+ const proceed = confirm(`Create ${tagsToRelease.length} ${platformName} Releases?`);
149
+ if (!proceed)
150
+ return;
151
+ // Create releases one by one
152
+ const allTags = getExistingTags();
153
+ let successCount = 0;
154
+ for (const tag of tagsToRelease) {
155
+ const ver = tag.replace(/^v/, '');
156
+ const tagIdx = allTags.indexOf(tag);
157
+ const prevTag = allTags[tagIdx + 1];
158
+ const commits = getCommitsSinceTag(prevTag);
159
+ const commitList = commits.map((c) => c.subject).join('\n');
160
+ let releaseBody;
161
+ if (useAI && commits.length > 0) {
162
+ console.log('');
163
+ const aiSpinner = new ScrambleProgress();
164
+ const modelDisplay = getModelValue(copilotModel ?? openrouterModel ?? geminiModel ?? '');
165
+ aiSpinner.start([
166
+ `Generating release notes with ${getAIProviderShortName(aiProvider)}${modelDisplay ? ` (${modelDisplay})` : ''}`,
167
+ ]);
168
+ const aiResult = await generateReleaseNotesWithProvider(aiProvider, commitList, language, undefined, copilotModel, openrouterModel, geminiModel);
169
+ if (aiResult) {
170
+ aiSpinner.succeed(`Notes generated for ${tag}`);
171
+ releaseBody = normalizeReleaseMarkdown(aiResult);
172
+ // Preview notes for user review
173
+ console.log('');
174
+ console.log(`${colors.cyan}┌${'─'.repeat(BOX_W)}┐${colors.reset}`);
175
+ console.log(`${colors.cyan}│${colors.reset} ${colors.bright}Release Notes — ${tag}${colors.reset}`);
176
+ console.log(`${colors.cyan}├${'─'.repeat(BOX_W)}┤${colors.reset}`);
177
+ for (const noteLine of releaseBody.split('\n')) {
178
+ console.log(`${colors.cyan}│${colors.reset} ${noteLine}`);
179
+ }
180
+ console.log(`${colors.cyan}└${'─'.repeat(BOX_W)}┘${colors.reset}`);
181
+ console.log('');
182
+ const reviewAction = await select(`Publish release for ${tag}?`, [
183
+ { label: 'Yes, publish', value: 'accept' },
184
+ { label: 'Skip this tag', value: 'skip' },
185
+ ]);
186
+ if (reviewAction === 'skip')
187
+ continue;
188
+ }
189
+ else {
190
+ aiSpinner.fail(`AI failed for ${tag}, using template`);
191
+ useAI = false;
192
+ releaseBody = generateReleaseMd(ver, commits, prevTag?.replace(/^v/, '') ?? '0.0.0')
193
+ .replace(/^## .*\n+/, '')
194
+ .replace(/\n---\n*$/, '')
195
+ .trim();
196
+ }
197
+ }
198
+ else {
199
+ releaseBody = generateReleaseMd(ver, commits, prevTag?.replace(/^v/, '') ?? '0.0.0')
200
+ .replace(/^## .*\n+/, '')
201
+ .replace(/\n---\n*$/, '')
202
+ .trim();
203
+ }
204
+ console.log('');
205
+ const releaseSpinner = new ScrambleProgress();
206
+ // Ensure tag exists on remote before creating GitHub Release
207
+ releaseSpinner.start([`Pushing tag ${tag} to remote`]);
208
+ try {
209
+ await execAsync(`git push origin ${tag} --no-verify`, true);
210
+ releaseSpinner.succeed(`Tag ${tag} pushed to remote`);
211
+ }
212
+ catch {
213
+ // Tag might already exist on remote — that's fine, continue
214
+ }
215
+ console.log('');
216
+ const createSpinner = new ScrambleProgress();
217
+ createSpinner.start([`Creating ${platformName} release`]);
218
+ const os = await import('node:os');
219
+ const tempFile = `${os.tmpdir()}/geeto-sync-${Date.now()}.md`;
220
+ writeFileSync(tempFile, releaseBody, 'utf8');
221
+ try {
222
+ await execAsync(`${cli} release create ${tag} --title "${tag}" --notes-file "${tempFile}"`, true);
223
+ createSpinner.succeed(`Release ${tag} created`);
224
+ successCount++;
225
+ }
226
+ catch (error) {
227
+ const stderr = error.stderr?.trim();
228
+ createSpinner.fail(`Failed to create release for ${tag}`);
229
+ if (stderr)
230
+ log.error(` ${stderr.split('\n')[0]}`);
231
+ }
232
+ // Cleanup temp file
233
+ try {
234
+ const { unlinkSync } = await import('node:fs');
235
+ unlinkSync(tempFile);
236
+ }
237
+ catch {
238
+ /* ignore */
239
+ }
240
+ }
241
+ console.log('');
242
+ if (successCount === tagsToRelease.length) {
243
+ log.success(`All ${successCount} ${platformName} Releases created!`);
244
+ }
245
+ else {
246
+ log.warn(`${successCount}/${tagsToRelease.length} releases created`);
247
+ }
248
+ };
249
+ // ─── Delete Releases ───
250
+ export const handleDeleteReleases = async () => {
251
+ // Detect platform (GitHub or GitLab)
252
+ const platform = detectPlatformFromRemote();
253
+ const cli = platform ? getPlatformCLI(platform) : 'gh';
254
+ const platformName = platform === 'gitlab' ? 'GitLab' : 'GitHub';
255
+ // Check if platform CLI is available
256
+ try {
257
+ execSilent(`${cli} --version`);
258
+ }
259
+ catch {
260
+ log.error(`${platformName} CLI (${cli}) is not installed.${cli === 'gh' ? ' Install it: https://cli.github.com' : ' Install it: https://gitlab.com/gitlab-org/cli'}`);
261
+ return;
262
+ }
263
+ console.log('');
264
+ const spinner = new ScrambleProgress();
265
+ spinner.start([`Fetching ${platformName} releases`]);
266
+ const ghReleases = await getExistingGithubReleases(cli);
267
+ spinner.succeed(`Found ${ghReleases.length} ${platformName} releases`);
268
+ if (ghReleases.length === 0) {
269
+ console.log('');
270
+ log.info(`No ${platformName} Releases to delete.`);
271
+ return;
272
+ }
273
+ console.log('');
274
+ const { multiSelect } = await import('../cli/menu.js');
275
+ const choices = ghReleases.map((t) => ({ label: t, value: t }));
276
+ const selected = await multiSelect('Select releases to delete:', choices);
277
+ if (selected.length === 0) {
278
+ log.info('No releases selected.');
279
+ return;
280
+ }
281
+ console.log('');
282
+ const alsoDeleteTag = confirm('Also delete the associated git tags?');
283
+ console.log('');
284
+ const proceed = confirm(`Delete ${selected.length} ${platformName} Release(s)${alsoDeleteTag ? ' + tags' : ''}?`);
285
+ if (!proceed)
286
+ return;
287
+ let successCount = 0;
288
+ for (const release of selected) {
289
+ console.log('');
290
+ const releaseSpinner = new ScrambleProgress();
291
+ releaseSpinner.start([`Deleting release ${release}`]);
292
+ try {
293
+ await execAsync(`${cli} release delete ${release} --yes`, true);
294
+ if (alsoDeleteTag) {
295
+ try {
296
+ await execAsync(`git tag -d ${release}`, true);
297
+ await execAsync(`git push origin --delete ${release} --no-verify`, true);
298
+ }
299
+ catch {
300
+ /* Tag deletion is best-effort */
301
+ }
302
+ }
303
+ releaseSpinner.succeed(`Release ${release} deleted${alsoDeleteTag ? ' + tag' : ''}`);
304
+ successCount++;
305
+ }
306
+ catch (error) {
307
+ const stderr = error.stderr?.trim();
308
+ releaseSpinner.fail(`Failed to delete ${release}`);
309
+ if (stderr)
310
+ log.error(` ${stderr.split('\n')[0]}`);
311
+ }
312
+ }
313
+ console.log('');
314
+ if (successCount === selected.length) {
315
+ log.success(`All ${successCount} releases deleted!`);
316
+ }
317
+ else {
318
+ log.warn(`${successCount}/${selected.length} releases deleted`);
319
+ }
320
+ };
321
+ //# sourceMappingURL=release-sync.js.map