golem-cc 1.0.2 → 2.1.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.
package/src/types.ts DELETED
@@ -1,152 +0,0 @@
1
- /**
2
- * Core types for golem workflow
3
- */
4
-
5
- // Ticket status across systems
6
- export type TicketStatus =
7
- | 'new' // Just created
8
- | 'spec' // Defining specs
9
- | 'planning' // Creating implementation plan
10
- | 'in-progress' // Building
11
- | 'review' // PR created, awaiting review
12
- | 'done' // Merged and deployed
13
- | 'blocked'; // Waiting on external dependency
14
-
15
- // Conventional commit types
16
- export type CommitType = 'feat' | 'fix' | 'refactor' | 'docs' | 'test' | 'chore';
17
-
18
- // The local source of truth linking Fresh ↔ Gitea ↔ Local
19
- export interface TicketState {
20
- // Identifiers
21
- id: string; // Local ID (usually Fresh ticket ID)
22
- slug: string; // Human-readable slug for branch name
23
-
24
- // Freshworks
25
- fresh: {
26
- id: string; // e.g., "INC-1234"
27
- url: string;
28
- subject: string;
29
- description: string;
30
- priority: 1 | 2 | 3 | 4; // 1=Urgent, 4=Low
31
- status: number; // Fresh status codes
32
- } | null;
33
-
34
- // Gitea
35
- gitea: {
36
- repo: string; // e.g., "CRDE/dashboard-api"
37
- issueNumber: number;
38
- url: string;
39
- prNumber?: number;
40
- prUrl?: string;
41
- } | null;
42
-
43
- // Local git
44
- git: {
45
- worktree: string; // Path to worktree
46
- branch: string; // Branch name
47
- commits: string[]; // Commit SHAs for this ticket
48
- };
49
-
50
- // Workflow state
51
- status: TicketStatus;
52
- type: CommitType;
53
- created: string; // ISO timestamp
54
- updated: string; // ISO timestamp
55
-
56
- // Spec/plan references
57
- specFile?: string; // Path to spec file
58
- planFile?: string; // Path to implementation plan
59
- }
60
-
61
- // Freshservice API types (subset we care about)
62
- export interface FreshTicket {
63
- id: number;
64
- subject: string;
65
- description: string;
66
- description_text: string;
67
- status: number;
68
- priority: 1 | 2 | 3 | 4;
69
- ticket_type: 'Incident' | 'Service Request';
70
- created_at: string;
71
- updated_at: string;
72
- requester_id: number;
73
- responder_id: number;
74
- custom_fields: Record<string, unknown>;
75
- }
76
-
77
- export interface FreshTicketCreatePayload {
78
- subject: string;
79
- description: string;
80
- priority?: 1 | 2 | 3 | 4;
81
- status?: number;
82
- source?: number;
83
- email?: string;
84
- requester_id?: number;
85
- responder_id?: number;
86
- group_id?: number;
87
- category?: string;
88
- sub_category?: string;
89
- resolution_notes?: string;
90
- custom_fields?: Record<string, unknown>;
91
- }
92
-
93
- // Gitea API types (subset we care about)
94
- export interface GiteaIssue {
95
- id: number;
96
- number: number;
97
- title: string;
98
- body: string;
99
- state: 'open' | 'closed';
100
- html_url: string;
101
- created_at: string;
102
- updated_at: string;
103
- labels: { name: string; color: string }[];
104
- milestone?: { title: string };
105
- }
106
-
107
- export interface GiteaIssueCreatePayload {
108
- title: string;
109
- body: string;
110
- labels?: string[];
111
- milestone?: number;
112
- }
113
-
114
- export interface GiteaPullRequest {
115
- id: number;
116
- number: number;
117
- title: string;
118
- body: string;
119
- state: 'open' | 'closed' | 'merged';
120
- html_url: string;
121
- head: { ref: string };
122
- base: { ref: string };
123
- merged: boolean;
124
- mergeable: boolean;
125
- }
126
-
127
- // CLI command context
128
- export interface GolemContext {
129
- projectRoot: string; // Where .golem/ lives
130
- golemDir: string; // .golem/ path
131
- ticketsDir: string; // .golem/tickets/
132
- specsDir: string; // .golem/specs/
133
- worktreesDir: string; // .golem/worktrees/
134
- currentTicket?: TicketState; // If working on a ticket
135
- }
136
-
137
- // Sync operation result
138
- export interface SyncResult {
139
- success: boolean;
140
- freshUpdated: boolean;
141
- giteaUpdated: boolean;
142
- localUpdated: boolean;
143
- error?: string;
144
- }
145
-
146
- // Build loop iteration result
147
- export interface BuildIterationResult {
148
- taskCompleted: string | null; // Task ID if completed
149
- allDone: boolean; // No more tasks
150
- commits: string[]; // Commit SHAs from this iteration
151
- error?: string;
152
- }
@@ -1,236 +0,0 @@
1
- /**
2
- * Git worktree management
3
- *
4
- * Creates isolated worktrees per ticket under .golem/worktrees/
5
- */
6
-
7
- import { execSync, exec } from 'node:child_process';
8
- import { promisify } from 'node:util';
9
- import { mkdir, rm, access } from 'node:fs/promises';
10
- import { join } from 'node:path';
11
- import type { TicketState } from '../types.js';
12
-
13
- const execAsync = promisify(exec);
14
-
15
- export interface WorktreeInfo {
16
- path: string;
17
- branch: string;
18
- commitSha: string;
19
- }
20
-
21
- /**
22
- * Run git command and return stdout
23
- */
24
- function git(command: string, cwd?: string): string {
25
- return execSync(`git ${command}`, {
26
- cwd,
27
- encoding: 'utf-8',
28
- stdio: ['pipe', 'pipe', 'pipe'],
29
- }).trim();
30
- }
31
-
32
- /**
33
- * Run git command async
34
- */
35
- async function gitAsync(command: string, cwd?: string): Promise<string> {
36
- const { stdout } = await execAsync(`git ${command}`, { cwd });
37
- return stdout.trim();
38
- }
39
-
40
- /**
41
- * Get the root of the current git repository
42
- */
43
- export function getRepoRoot(): string {
44
- return git('rev-parse --show-toplevel');
45
- }
46
-
47
- /**
48
- * Get the default branch (main or master)
49
- */
50
- export function getDefaultBranch(): string {
51
- try {
52
- // Try to get from remote HEAD
53
- const ref = git('symbolic-ref refs/remotes/origin/HEAD');
54
- return ref.replace('refs/remotes/origin/', '');
55
- } catch {
56
- // Fall back to checking if main or master exists
57
- try {
58
- git('rev-parse --verify main');
59
- return 'main';
60
- } catch {
61
- return 'master';
62
- }
63
- }
64
- }
65
-
66
- /**
67
- * List all worktrees
68
- */
69
- export function listWorktrees(): WorktreeInfo[] {
70
- const output = git('worktree list --porcelain');
71
- const worktrees: WorktreeInfo[] = [];
72
- let current: Partial<WorktreeInfo> = {};
73
-
74
- for (const line of output.split('\n')) {
75
- if (line.startsWith('worktree ')) {
76
- current.path = line.replace('worktree ', '');
77
- } else if (line.startsWith('HEAD ')) {
78
- current.commitSha = line.replace('HEAD ', '');
79
- } else if (line.startsWith('branch ')) {
80
- current.branch = line.replace('branch refs/heads/', '');
81
- } else if (line === '') {
82
- if (current.path && current.branch && current.commitSha) {
83
- worktrees.push(current as WorktreeInfo);
84
- }
85
- current = {};
86
- }
87
- }
88
-
89
- // Handle last entry
90
- if (current.path && current.branch && current.commitSha) {
91
- worktrees.push(current as WorktreeInfo);
92
- }
93
-
94
- return worktrees;
95
- }
96
-
97
- /**
98
- * Create a worktree for a ticket
99
- */
100
- export async function createWorktree(
101
- ticket: TicketState,
102
- options: { baseBranch?: string } = {}
103
- ): Promise<string> {
104
- const repoRoot = getRepoRoot();
105
- const baseBranch = options.baseBranch || getDefaultBranch();
106
- const worktreePath = join(repoRoot, ticket.git.worktree);
107
- const branch = ticket.git.branch;
108
-
109
- // Ensure worktrees directory exists
110
- await mkdir(join(repoRoot, '.golem/worktrees'), { recursive: true });
111
-
112
- // Check if worktree already exists
113
- const worktrees = listWorktrees();
114
- const existing = worktrees.find(w => w.branch === branch);
115
- if (existing) {
116
- return existing.path;
117
- }
118
-
119
- // Determine the best base ref to use
120
- let baseRef = baseBranch;
121
-
122
- // Try to fetch and use remote if available
123
- try {
124
- git('fetch origin', repoRoot);
125
- // Verify origin/baseBranch exists
126
- git(`rev-parse --verify origin/${baseBranch}`, repoRoot);
127
- baseRef = `origin/${baseBranch}`;
128
- } catch {
129
- // No remote or remote branch doesn't exist, use local branch
130
- try {
131
- git(`rev-parse --verify ${baseBranch}`, repoRoot);
132
- baseRef = baseBranch;
133
- } catch {
134
- // Local branch doesn't exist either, use HEAD
135
- baseRef = 'HEAD';
136
- }
137
- }
138
-
139
- // Create the worktree with a new branch based on the base ref
140
- git(`worktree add -b ${branch} "${worktreePath}" ${baseRef}`, repoRoot);
141
-
142
- return worktreePath;
143
- }
144
-
145
- /**
146
- * Remove a worktree
147
- */
148
- export async function removeWorktree(worktreePath: string): Promise<void> {
149
- const repoRoot = getRepoRoot();
150
-
151
- // Check if it exists
152
- try {
153
- await access(worktreePath);
154
- } catch {
155
- return; // Already gone
156
- }
157
-
158
- // Remove the worktree
159
- git(`worktree remove "${worktreePath}" --force`, repoRoot);
160
- }
161
-
162
- /**
163
- * Get current branch in a worktree
164
- */
165
- export function getCurrentBranch(worktreePath: string): string {
166
- return git('rev-parse --abbrev-ref HEAD', worktreePath);
167
- }
168
-
169
- /**
170
- * Get list of commits on branch since it diverged from base
171
- */
172
- export async function getCommitsSinceBase(
173
- worktreePath: string,
174
- baseBranch?: string
175
- ): Promise<string[]> {
176
- const base = baseBranch || getDefaultBranch();
177
- const output = await gitAsync(
178
- `log origin/${base}..HEAD --format=%H`,
179
- worktreePath
180
- );
181
- return output ? output.split('\n') : [];
182
- }
183
-
184
- /**
185
- * Squash all commits since base into one
186
- */
187
- export async function squashCommits(
188
- worktreePath: string,
189
- message: string,
190
- baseBranch?: string
191
- ): Promise<string> {
192
- const base = baseBranch || getDefaultBranch();
193
-
194
- // Soft reset to merge base, keeping changes staged
195
- await gitAsync(`reset --soft origin/${base}`, worktreePath);
196
-
197
- // Create new commit with all changes
198
- await gitAsync(`commit -m "${message.replace(/"/g, '\\"')}"`, worktreePath);
199
-
200
- // Return new commit SHA
201
- return gitAsync('rev-parse HEAD', worktreePath);
202
- }
203
-
204
- /**
205
- * Create a commit in the worktree
206
- */
207
- export async function createCommit(
208
- worktreePath: string,
209
- message: string
210
- ): Promise<string> {
211
- // Stage all changes
212
- await gitAsync('add -A', worktreePath);
213
-
214
- // Check if there are changes to commit
215
- try {
216
- await gitAsync('diff --cached --quiet', worktreePath);
217
- // No changes
218
- return '';
219
- } catch {
220
- // There are changes, commit them
221
- }
222
-
223
- await gitAsync(`commit -m "${message.replace(/"/g, '\\"')}"`, worktreePath);
224
- return gitAsync('rev-parse HEAD', worktreePath);
225
- }
226
-
227
- /**
228
- * Push branch to remote
229
- */
230
- export async function pushBranch(
231
- worktreePath: string,
232
- force = false
233
- ): Promise<void> {
234
- const forceFlag = force ? '--force-with-lease' : '';
235
- await gitAsync(`push -u origin HEAD ${forceFlag}`, worktreePath);
236
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "declaration": true,
12
- "declarationMap": true,
13
- "sourceMap": true,
14
- "resolveJsonModule": true
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["node_modules", "dist"]
18
- }