brn-toolkit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/GEMINI.md +92 -0
  2. package/README.md +145 -0
  3. package/cli/brn.ts +301 -0
  4. package/dist/cli/brn.js +274 -0
  5. package/dist/lib/utils.js +167 -0
  6. package/dist/skills/github/scripts/create_pr.js +24 -0
  7. package/dist/skills/github/scripts/get_repo_info.js +23 -0
  8. package/dist/skills/github/scripts/list_prs.js +27 -0
  9. package/dist/skills/github/scripts/list_repos.js +39 -0
  10. package/dist/skills/jira/scripts/add_comment.js +36 -0
  11. package/dist/skills/jira/scripts/get_ticket.js +45 -0
  12. package/dist/skills/jira/scripts/list_tickets.js +42 -0
  13. package/dist/skills/jira/scripts/update_ticket.js +30 -0
  14. package/dist/skills/workflow/scripts/start.js +75 -0
  15. package/dist/skills/workspace-manager/scripts/configure_workspace.js +59 -0
  16. package/dist/skills/workspace-manager/scripts/create_workspace.js +60 -0
  17. package/lib/utils.ts +236 -0
  18. package/package.json +46 -0
  19. package/skills/git-worktree/SKILL.md +49 -0
  20. package/skills/git-worktree/scripts/clone_repo.sh +53 -0
  21. package/skills/git-worktree/scripts/create_worktree.sh +66 -0
  22. package/skills/git-worktree/scripts/list_worktrees.sh +39 -0
  23. package/skills/git-worktree/scripts/remove_worktree.sh +47 -0
  24. package/skills/github/SKILL.md +51 -0
  25. package/skills/github/references/api_patterns.md +36 -0
  26. package/skills/github/scripts/create_pr.ts +38 -0
  27. package/skills/github/scripts/get_repo_info.ts +39 -0
  28. package/skills/github/scripts/list_prs.ts +45 -0
  29. package/skills/github/scripts/list_repos.ts +54 -0
  30. package/skills/jira/SKILL.md +50 -0
  31. package/skills/jira/references/api_patterns.md +59 -0
  32. package/skills/jira/scripts/add_comment.ts +41 -0
  33. package/skills/jira/scripts/get_ticket.ts +71 -0
  34. package/skills/jira/scripts/list_tickets.ts +64 -0
  35. package/skills/jira/scripts/update_ticket.ts +50 -0
  36. package/skills/workflow/SKILL.md +94 -0
  37. package/skills/workflow/references/coding_workflow.md +88 -0
  38. package/skills/workflow/references/planning_workflow.md +96 -0
  39. package/skills/workflow/references/review_workflow.md +104 -0
  40. package/skills/workflow/scripts/start.ts +93 -0
  41. package/skills/workspace-manager/SKILL.md +49 -0
  42. package/skills/workspace-manager/scripts/configure_workspace.sh +87 -0
  43. package/skills/workspace-manager/scripts/configure_workspace.ts +70 -0
  44. package/skills/workspace-manager/scripts/create_workspace.sh +66 -0
  45. package/skills/workspace-manager/scripts/create_workspace.ts +74 -0
  46. package/skills/workspace-manager/scripts/get_active_workspace.sh +24 -0
  47. package/skills/workspace-manager/scripts/list_workspaces.sh +31 -0
  48. package/skills/workspace-manager/scripts/switch_workspace.sh +38 -0
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Update JIRA ticket status via transition
4
+ * Usage: npx tsx update_ticket.ts <ticket_key> <target_status>
5
+ */
6
+ import { jiraRequest } from "../../../lib/utils.js";
7
+ const [ticketKey, targetStatus] = process.argv.slice(2);
8
+ if (!ticketKey || !targetStatus) {
9
+ console.log("Usage: npx tsx update_ticket.ts <ticket_key> <target_status>");
10
+ console.log("Example: npx tsx update_ticket.ts PROJ-123 'In Progress'");
11
+ process.exit(1);
12
+ }
13
+ // Get available transitions
14
+ const { transitions } = await jiraRequest(`/rest/api/3/issue/${ticketKey}/transitions`);
15
+ // Find matching transition (case-insensitive)
16
+ const transition = transitions.find((t) => t.name.toLowerCase() === targetStatus.toLowerCase());
17
+ if (!transition) {
18
+ console.error(`Error: Transition '${targetStatus}' not available`);
19
+ console.error("Available transitions:");
20
+ for (const t of transitions) {
21
+ console.error(` - ${t.name}`);
22
+ }
23
+ process.exit(1);
24
+ }
25
+ // Perform transition
26
+ await jiraRequest(`/rest/api/3/issue/${ticketKey}/transitions`, {
27
+ method: "POST",
28
+ body: JSON.stringify({ transition: { id: transition.id } }),
29
+ });
30
+ console.log(`✓ Transitioned ${ticketKey} to '${targetStatus}'`);
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Initiate a new development workflow for a JIRA ticket
4
+ * Usage: npx tsx start.ts <ticket_id>
5
+ */
6
+ import { $ } from "zx";
7
+ import { getActiveWorkspace, jiraRequest } from "../../../lib/utils.js";
8
+ import { join } from "path";
9
+ import { homedir } from "os";
10
+ $.verbose = false;
11
+ const ticketId = process.argv[2];
12
+ if (!ticketId) {
13
+ console.log("Usage: npx tsx start.ts <ticket_id>");
14
+ console.log("Example: npx tsx start.ts PROJ-123");
15
+ process.exit(1);
16
+ }
17
+ async function run() {
18
+ console.log(`🚀 Initiating workflow for ${ticketId}...`);
19
+ // 1. Fetch ticket details
20
+ console.log(`📋 Fetching ticket ${ticketId}...`);
21
+ const ticket = await jiraRequest(`/rest/api/3/issue/${ticketId}`);
22
+ const summary = ticket.fields.summary;
23
+ const projectKey = ticket.fields.project.key;
24
+ console.log(`✅ Ticket found: ${summary}`);
25
+ // 2. Identify repo (simple heuristic or prompt)
26
+ // For now, let's look for repos in the workspace
27
+ const workspace = getActiveWorkspace();
28
+ let workDir = workspace.path;
29
+ if (workDir.startsWith("~")) {
30
+ workDir = join(homedir(), workDir.slice(1));
31
+ }
32
+ // In a real scenario, we might have a mapping or ask the user
33
+ // For this automation, we'll try to find a repo that matches the project key or ask
34
+ console.log(`📂 Searching for repositories in ${workDir}...`);
35
+ // List directories in workDir
36
+ const repos = (await $ `ls -d ${workDir}/*/ 2>/dev/null`.quiet()).stdout
37
+ .split("\n")
38
+ .map(p => p.trim())
39
+ .filter(p => p && !p.endsWith("-worktrees/"))
40
+ .map(p => p.split("/").filter(Boolean).pop());
41
+ if (repos.length === 0) {
42
+ console.error("❌ No repositories found in workspace. Please clone a repo first using git-worktree:clone.");
43
+ process.exit(1);
44
+ }
45
+ let selectedRepo = repos[0];
46
+ if (repos.length > 1) {
47
+ console.log("Multiple repositories found:");
48
+ repos.forEach((r, i) => console.log(` ${i + 1}. ${r}`));
49
+ console.log(`Using ${selectedRepo} (default). To use another, clone it or configure mapping.`);
50
+ }
51
+ // 3. Create branch name
52
+ const branchName = `${ticketId}-${summary.toLowerCase().replace(/[^a-z0-9]/g, "-").slice(0, 30)}`;
53
+ // 4. Create worktree
54
+ console.log(`🌿 Creating worktree for branch ${branchName}...`);
55
+ const createWorktreeScript = join(process.cwd(), "skills/git-worktree/scripts/create_worktree.sh");
56
+ await $ `${createWorktreeScript} ${selectedRepo} ${branchName}`;
57
+ // 5. Update ticket status
58
+ console.log(`🔄 Updating ticket status to 'In Progress'...`);
59
+ const updateTicketScript = join(process.cwd(), "skills/jira/scripts/update_ticket.ts");
60
+ await $ `npx tsx ${updateTicketScript} ${ticketId} "In Progress"`.quiet();
61
+ const worktreePath = join(workDir, `${selectedRepo}-worktrees`, branchName);
62
+ console.log("\n" + "=".repeat(50));
63
+ console.log(`✅ Workflow initiated successfully!`);
64
+ console.log(`📍 Worktree: ${worktreePath}`);
65
+ console.log(`🔗 Ticket: ${workspace.jira_url}/browse/${ticketId}`);
66
+ console.log("=".repeat(50));
67
+ console.log(`
68
+ Next steps:
69
+ cd ${worktreePath}
70
+ # Start planning and coding!`);
71
+ }
72
+ run().catch(err => {
73
+ console.error("❌ Error initiating workflow:", err.message);
74
+ process.exit(1);
75
+ });
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Configure a workspace setting
4
+ * Usage: npx tsx configure_workspace.ts <workspace_name> <key> <value>
5
+ */
6
+ import { getBrnConfig, saveBrnConfig } from "../../../lib/utils.js";
7
+ const workspaceName = process.argv[2];
8
+ const key = process.argv[3];
9
+ const value = process.argv[4];
10
+ if (!workspaceName || !key || !value) {
11
+ console.log("Usage: configure_workspace.ts <workspace_name> <key> <value>");
12
+ console.log("");
13
+ console.log("Standard keys: github_token, jira_token, jira_url, jira_email, path");
14
+ console.log("");
15
+ console.log("Automation keys (default: false):");
16
+ console.log(" automation.github_auto_push - Auto-push commits");
17
+ console.log(" automation.github_auto_pr - Auto-create PRs");
18
+ console.log(" automation.jira_auto_transition - Auto-update ticket status");
19
+ console.log(" automation.jira_auto_comment - Auto-add comments");
20
+ process.exit(1);
21
+ }
22
+ const config = getBrnConfig();
23
+ if (!config.workspaces[workspaceName]) {
24
+ console.error(`Error: Workspace '${workspaceName}' not found`);
25
+ process.exit(1);
26
+ }
27
+ // Validate key
28
+ const validKeys = ["github_token", "github_org", "jira_token", "jira_url", "jira_email", "path"];
29
+ const validAutomationKeys = [
30
+ "automation.github_auto_push",
31
+ "automation.github_auto_pr",
32
+ "automation.jira_auto_transition",
33
+ "automation.jira_auto_comment"
34
+ ];
35
+ let isValid = validKeys.includes(key) || validAutomationKeys.includes(key);
36
+ if (!isValid) {
37
+ console.error(`Error: Invalid key '${key}'`);
38
+ console.log(`Valid keys: ${validKeys.join(", ")}`);
39
+ console.log(`Automation keys: ${validAutomationKeys.join(", ")}`);
40
+ process.exit(1);
41
+ }
42
+ // Handle automation keys (nested)
43
+ if (key.startsWith("automation.")) {
44
+ const automationKey = key.replace("automation.", "");
45
+ const boolValue = value === "true";
46
+ if (value !== "true" && value !== "false") {
47
+ console.error("Error: Automation values must be 'true' or 'false'");
48
+ process.exit(1);
49
+ }
50
+ if (!config.workspaces[workspaceName].automation) {
51
+ config.workspaces[workspaceName].automation = {};
52
+ }
53
+ config.workspaces[workspaceName].automation[automationKey] = boolValue;
54
+ }
55
+ else {
56
+ config.workspaces[workspaceName][key] = value;
57
+ }
58
+ saveBrnConfig(config);
59
+ console.log(`✓ Set ${key} for workspace '${workspaceName}'`);
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Create a new workspace
4
+ * Usage: npx tsx create_workspace.ts <name> <work_dir>
5
+ */
6
+ import { existsSync, mkdirSync, readFileSync } from "fs";
7
+ import { join } from "path";
8
+ import { homedir } from "os";
9
+ import YAML from "yaml";
10
+ import { saveBrnConfig } from "../../../lib/utils.js";
11
+ const workspaceName = process.argv[2];
12
+ let workDir = process.argv[3];
13
+ if (!workspaceName || !workDir) {
14
+ console.log("Usage: create_workspace.ts <name> <work_dir>");
15
+ console.log("Example: create_workspace.ts personal ~/dev/personal/auto");
16
+ process.exit(1);
17
+ }
18
+ const configPath = join(homedir(), ".brn", "config.yaml");
19
+ const configDir = join(homedir(), ".brn");
20
+ // Create config directory if it doesn't exist
21
+ if (!existsSync(configDir)) {
22
+ mkdirSync(configDir, { recursive: true });
23
+ }
24
+ // Expand work_dir path
25
+ if (workDir.startsWith("~")) {
26
+ workDir = join(homedir(), workDir.slice(1));
27
+ }
28
+ let config = {
29
+ version: "1.0",
30
+ active_workspace: workspaceName,
31
+ workspaces: {}
32
+ };
33
+ if (existsSync(configPath)) {
34
+ const content = readFileSync(configPath, "utf-8");
35
+ config = YAML.parse(content);
36
+ }
37
+ // Check if workspace already exists
38
+ if (config.workspaces && config.workspaces[workspaceName]) {
39
+ console.error(`Error: Workspace '${workspaceName}' already exists`);
40
+ process.exit(1);
41
+ }
42
+ if (!config.workspaces) {
43
+ config.workspaces = {};
44
+ }
45
+ config.workspaces[workspaceName] = {
46
+ path: workDir,
47
+ github_token: null,
48
+ jira_token: null,
49
+ jira_url: null
50
+ };
51
+ // If no active workspace, set this one
52
+ if (!config.active_workspace) {
53
+ config.active_workspace = workspaceName;
54
+ }
55
+ saveBrnConfig(config);
56
+ // Create the work directory
57
+ if (!existsSync(workDir)) {
58
+ mkdirSync(workDir, { recursive: true });
59
+ }
60
+ console.log(`✓ Created workspace '${workspaceName}' at ${workDir}`);
package/lib/utils.ts ADDED
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Shared utilities for brn scripts
3
+ */
4
+ import { $ } from "zx";
5
+ import { readFileSync, existsSync, writeFileSync } from "fs";
6
+ import { homedir } from "os";
7
+ import { join } from "path";
8
+ import YAML from "yaml";
9
+
10
+ $.verbose = false;
11
+
12
+ export interface AutomationConfig {
13
+ github_auto_push?: boolean;
14
+ github_auto_pr?: boolean;
15
+ jira_auto_transition?: boolean;
16
+ jira_auto_comment?: boolean;
17
+ }
18
+
19
+ export interface WorkspaceConfig {
20
+ path: string;
21
+ github_token?: string | null;
22
+ github_org?: string | null;
23
+ jira_token?: string | null;
24
+ jira_url?: string | null;
25
+ jira_email?: string | null;
26
+ automation?: AutomationConfig;
27
+ }
28
+
29
+ export interface BrnConfig {
30
+ version: string;
31
+ active_workspace: string;
32
+ workspaces: Record<string, WorkspaceConfig>;
33
+ }
34
+
35
+ /**
36
+ * Get the brn config from ~/.brn/config.yaml
37
+ */
38
+ export function getBrnConfig(): BrnConfig {
39
+ const configPath = join(homedir(), ".brn", "config.yaml");
40
+
41
+ if (!existsSync(configPath)) {
42
+ console.error("Error: No config found at ~/.brn/config.yaml");
43
+ console.error("Run: create_workspace.sh <name> <path>");
44
+ process.exit(1);
45
+ }
46
+
47
+ const content = readFileSync(configPath, "utf-8");
48
+ return YAML.parse(content) as BrnConfig;
49
+ }
50
+
51
+ /**
52
+ * Save the brn config to ~/.brn/config.yaml
53
+ */
54
+ export function saveBrnConfig(config: BrnConfig): void {
55
+ const configPath = join(homedir(), ".brn", "config.yaml");
56
+ writeFileSync(configPath, YAML.stringify(config));
57
+ }
58
+
59
+ /**
60
+ * Update the active workspace configuration
61
+ */
62
+ export function updateWorkspace(updates: Partial<WorkspaceConfig>): void {
63
+ const config = getBrnConfig();
64
+ const activeWorkspaceName = config.active_workspace;
65
+
66
+ if (!config.workspaces[activeWorkspaceName]) {
67
+ console.error(`Error: Active workspace '${activeWorkspaceName}' not found`);
68
+ process.exit(1);
69
+ }
70
+
71
+ config.workspaces[activeWorkspaceName] = {
72
+ ...config.workspaces[activeWorkspaceName],
73
+ ...updates,
74
+ };
75
+
76
+ saveBrnConfig(config);
77
+ }
78
+
79
+ /**
80
+ * Get the active workspace config
81
+ */
82
+ export function getActiveWorkspace(): WorkspaceConfig & { name: string } {
83
+ const config = getBrnConfig();
84
+ const name = config.active_workspace;
85
+ const workspace = config.workspaces[name];
86
+
87
+ if (!workspace) {
88
+ console.error(`Error: Workspace '${name}' not found in config`);
89
+ process.exit(1);
90
+ }
91
+
92
+ return { name, ...workspace };
93
+ }
94
+
95
+ /**
96
+ * Check if an automation action is enabled for the active workspace.
97
+ * All automation is OFF by default for safety.
98
+ */
99
+ export function isAutomationEnabled(
100
+ action: keyof AutomationConfig
101
+ ): boolean {
102
+ const workspace = getActiveWorkspace();
103
+ return workspace.automation?.[action] ?? false;
104
+ }
105
+
106
+ /**
107
+ * Require automation to be enabled, or prompt user to confirm.
108
+ * Returns true if action should proceed, false if user declined.
109
+ */
110
+ export function checkAutomation(
111
+ action: keyof AutomationConfig,
112
+ description: string
113
+ ): boolean {
114
+ if (isAutomationEnabled(action)) {
115
+ return true;
116
+ }
117
+
118
+ console.log(`⚠️ Automation disabled: ${description}`);
119
+ console.log(` To enable, run: configure_workspace.sh <workspace> automation.${action} true`);
120
+ console.log(` Skipping automatic execution.`);
121
+ return false;
122
+ }
123
+
124
+ /**
125
+ * Get GitHub token from active workspace
126
+ */
127
+ export function getGitHubToken(): string {
128
+ const workspace = getActiveWorkspace();
129
+
130
+ if (!workspace.github_token) {
131
+ console.error("Error: github_token not configured for this workspace");
132
+ console.error(
133
+ `Run: configure_workspace.sh ${workspace.name} github_token <token>`
134
+ );
135
+ process.exit(1);
136
+ }
137
+
138
+ return workspace.github_token;
139
+ }
140
+
141
+ /**
142
+ * Get JIRA config from active workspace
143
+ */
144
+ export function getJiraConfig(): {
145
+ url: string;
146
+ email: string;
147
+ token: string;
148
+ } {
149
+ const workspace = getActiveWorkspace();
150
+
151
+ if (!workspace.jira_url) {
152
+ console.error("Error: jira_url not configured");
153
+ process.exit(1);
154
+ }
155
+ if (!workspace.jira_email) {
156
+ console.error("Error: jira_email not configured");
157
+ process.exit(1);
158
+ }
159
+ if (!workspace.jira_token) {
160
+ console.error("Error: jira_token not configured");
161
+ process.exit(1);
162
+ }
163
+
164
+ return {
165
+ url: workspace.jira_url.replace(/\/$/, ""),
166
+ email: workspace.jira_email,
167
+ token: workspace.jira_token,
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Make a GitHub API request
173
+ */
174
+ export async function githubRequest<T>(
175
+ endpoint: string,
176
+ options: RequestInit = {}
177
+ ): Promise<T> {
178
+ const token = getGitHubToken();
179
+
180
+ console.log(`[GitHub API] Requesting: ${endpoint}`);
181
+
182
+ const response = await fetch(`https://api.github.com${endpoint}`, {
183
+ ...options,
184
+ headers: {
185
+ Authorization: `Bearer ${token}`,
186
+ Accept: "application/vnd.github+json",
187
+ "X-GitHub-Api-Version": "2022-11-28",
188
+ "Content-Type": "application/json",
189
+ ...options.headers,
190
+ },
191
+ });
192
+
193
+ if (!response.ok) {
194
+ const error = await response.json().catch(() => ({}));
195
+ console.error(`Error: ${response.status} - ${response.statusText}`);
196
+ if (error.message) console.error(` ${error.message}`);
197
+ process.exit(1);
198
+ }
199
+
200
+ return response.json() as Promise<T>;
201
+ }
202
+
203
+ /**
204
+ * Make a JIRA API request
205
+ */
206
+ export async function jiraRequest<T>(
207
+ endpoint: string,
208
+ options: RequestInit = {}
209
+ ): Promise<T> {
210
+ const { url, email, token } = getJiraConfig();
211
+ const auth = Buffer.from(`${email}:${token}`).toString("base64");
212
+
213
+ const response = await fetch(`${url}${endpoint}`, {
214
+ ...options,
215
+ headers: {
216
+ Authorization: `Basic ${auth}`,
217
+ Accept: "application/json",
218
+ "Content-Type": "application/json",
219
+ ...options.headers,
220
+ },
221
+ });
222
+
223
+ if (!response.ok) {
224
+ const error = await response.json().catch(() => ({}));
225
+ console.error(`Error: ${response.status} - ${response.statusText}`);
226
+ if (error.errorMessages) console.error(` ${error.errorMessages}`);
227
+ process.exit(1);
228
+ }
229
+
230
+ // Handle 204 No Content
231
+ if (response.status === 204) {
232
+ return {} as T;
233
+ }
234
+
235
+ return response.json() as Promise<T>;
236
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "brn-toolkit",
3
+ "version": "1.0.0",
4
+ "description": "AI Developer Workflow Toolkit - Skills for Claude Code and Copilot CLI",
5
+ "type": "module",
6
+ "author": "zfael",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/zfael/brn.git"
11
+ },
12
+ "keywords": [],
13
+ "bin": {
14
+ "brn": "./dist/cli/brn.js"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "skills",
19
+ "lib",
20
+ "cli",
21
+ "README.md",
22
+ "GEMINI.md"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "prepublishOnly": "npm run build",
27
+ "setup": "npx tsx cli/brn.ts setup",
28
+ "github:repos": "npx tsx skills/github/scripts/list_repos.ts",
29
+ "github:repo": "npx tsx skills/github/scripts/get_repo_info.ts",
30
+ "github:pr:create": "npx tsx skills/github/scripts/create_pr.ts",
31
+ "github:pr:list": "npx tsx skills/github/scripts/list_prs.ts",
32
+ "jira:tickets": "npx tsx skills/jira/scripts/list_tickets.ts",
33
+ "jira:ticket": "npx tsx skills/jira/scripts/get_ticket.ts",
34
+ "jira:update": "npx tsx skills/jira/scripts/update_ticket.ts",
35
+ "jira:comment": "npx tsx skills/jira/scripts/add_comment.ts"
36
+ },
37
+ "dependencies": {
38
+ "tsx": "^4.7.0",
39
+ "yaml": "^2.3.4",
40
+ "zx": "^8.1.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.10.0",
44
+ "typescript": "^5.3.0"
45
+ }
46
+ }
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: brn:git-worktree
3
+ description: |
4
+ Manage git repositories using the worktree pattern.
5
+ This allows multiple branches to be checked out simultaneously in sibling directories.
6
+ ---
7
+
8
+ # Git Worktree Manager
9
+
10
+ ## Description
11
+ The git-worktree skill manages repositories using a specific directory structure optimized for multi-tasking. Instead of a single `.git` folder with one working directory, it uses a "bare" clone (or similar) and creates a separate directory for each branch/feature.
12
+
13
+ ## Available Scripts
14
+
15
+ ### `clone_repo`
16
+ Clones a repository into the current workspace, setting it up for worktree usage. This creates a directory structure where the main repo is stored, and worktrees can be added alongside it.
17
+
18
+ * **Usage**: `brn git-worktree clone_repo <repo_url> [name]`
19
+ * **Arguments**:
20
+ * `repo_url`: The URL of the git repository to clone.
21
+ * `name` (optional): The name of the directory to create. Defaults to the repo name from the URL.
22
+ * **Example**: `brn git-worktree clone_repo https://github.com/myorg/api.git`
23
+
24
+ ### `create_worktree`
25
+ Creates a new worktree for a specific branch. If the branch doesn't exist, it can be created from a base branch.
26
+
27
+ * **Usage**: `brn git-worktree create_worktree <repo_name> <branch_name> [base_branch]`
28
+ * **Arguments**:
29
+ * `repo_name`: The name of the repository (must be already cloned via `clone_repo`).
30
+ * `branch_name`: The name of the new branch and worktree directory.
31
+ * `base_branch` (optional): The branch to split from. Defaults to `main` or `master`.
32
+ * **Example**: `brn git-worktree create_worktree api feature/login`
33
+
34
+ ### `list_worktrees`
35
+ Lists all active worktrees for a repository.
36
+
37
+ * **Usage**: `brn git-worktree list_worktrees <repo_name>`
38
+ * **Arguments**:
39
+ * `repo_name`: The name of the repository to list worktrees for.
40
+ * **Example**: `brn git-worktree list_worktrees api`
41
+
42
+ ### `remove_worktree`
43
+ Removes a worktree and its directory. This cleans up the working directory and the git worktree entry.
44
+
45
+ * **Usage**: `brn git-worktree remove_worktree <repo_name> <branch_name>`
46
+ * **Arguments**:
47
+ * `repo_name`: The name of the repository.
48
+ * `branch_name`: The name of the worktree/branch to remove.
49
+ * **Example**: `brn git-worktree remove_worktree api feature/login`
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+ # Clone a repository to the active workspace
3
+ # Usage: clone_repo.sh <repo_url> [name]
4
+
5
+ set -e
6
+
7
+ REPO_URL="$1"
8
+ REPO_NAME="$2"
9
+
10
+ if [ -z "$REPO_URL" ]; then
11
+ echo "Usage: clone_repo.sh <repo_url> [name]"
12
+ echo "Example: clone_repo.sh https://github.com/org/my-app.git"
13
+ exit 1
14
+ fi
15
+
16
+ # Get the script directory to find workspace-manager
17
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
18
+ WORKSPACE_SCRIPT="$SCRIPT_DIR/../../workspace-manager/scripts/get_active_workspace.sh"
19
+
20
+ if [ ! -f "$WORKSPACE_SCRIPT" ]; then
21
+ echo "Error: workspace-manager skill not found"
22
+ exit 1
23
+ fi
24
+
25
+ # Get workspace config
26
+ WORKSPACE_JSON=$("$WORKSPACE_SCRIPT" --json)
27
+ WORK_DIR=$(echo "$WORKSPACE_JSON" | yq -r '.path')
28
+ WORK_DIR="${WORK_DIR/#\~/$HOME}"
29
+
30
+ # Derive repo name from URL if not provided
31
+ if [ -z "$REPO_NAME" ]; then
32
+ REPO_NAME=$(basename "$REPO_URL" .git)
33
+ fi
34
+
35
+ REPO_PATH="$WORK_DIR/$REPO_NAME"
36
+
37
+ if [ -d "$REPO_PATH" ]; then
38
+ echo "Repository already exists at $REPO_PATH"
39
+ exit 0
40
+ fi
41
+
42
+ # Create work directory if needed
43
+ mkdir -p "$WORK_DIR"
44
+
45
+ # Clone the repository
46
+ echo "Cloning $REPO_URL to $REPO_PATH..."
47
+ git clone "$REPO_URL" "$REPO_PATH"
48
+
49
+ # Create worktrees directory
50
+ mkdir -p "$WORK_DIR/${REPO_NAME}-worktrees"
51
+
52
+ echo "✓ Cloned '$REPO_NAME' to $REPO_PATH"
53
+ echo " Worktrees will be created in: $WORK_DIR/${REPO_NAME}-worktrees/"
@@ -0,0 +1,66 @@
1
+ #!/bin/bash
2
+ # Create a new worktree for a branch
3
+ # Usage: create_worktree.sh <repo_name> <branch_name> [base_branch]
4
+
5
+ set -e
6
+
7
+ REPO_NAME="$1"
8
+ BRANCH_NAME="$2"
9
+ BASE_BRANCH="${3:-main}"
10
+
11
+ if [ -z "$REPO_NAME" ] || [ -z "$BRANCH_NAME" ]; then
12
+ echo "Usage: create_worktree.sh <repo_name> <branch_name> [base_branch]"
13
+ echo "Example: create_worktree.sh my-app PROJ-123-feature main"
14
+ exit 1
15
+ fi
16
+
17
+ # Get the script directory to find workspace-manager
18
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
19
+ WORKSPACE_SCRIPT="$SCRIPT_DIR/../../workspace-manager/scripts/get_active_workspace.sh"
20
+
21
+ if [ ! -f "$WORKSPACE_SCRIPT" ]; then
22
+ echo "Error: workspace-manager skill not found"
23
+ exit 1
24
+ fi
25
+
26
+ # Get workspace config
27
+ WORKSPACE_JSON=$("$WORKSPACE_SCRIPT" --json)
28
+ WORK_DIR=$(echo "$WORKSPACE_JSON" | yq -r '.path')
29
+ WORK_DIR="${WORK_DIR/#\~/$HOME}"
30
+
31
+ REPO_PATH="$WORK_DIR/$REPO_NAME"
32
+ WORKTREES_DIR="$WORK_DIR/${REPO_NAME}-worktrees"
33
+ WORKTREE_PATH="$WORKTREES_DIR/$BRANCH_NAME"
34
+
35
+ if [ ! -d "$REPO_PATH" ]; then
36
+ echo "Error: Repository not found at $REPO_PATH"
37
+ echo "Run clone_repo.sh first"
38
+ exit 1
39
+ fi
40
+
41
+ if [ -d "$WORKTREE_PATH" ]; then
42
+ echo "Worktree already exists at $WORKTREE_PATH"
43
+ exit 0
44
+ fi
45
+
46
+ # Ensure worktrees directory exists
47
+ mkdir -p "$WORKTREES_DIR"
48
+
49
+ # Go to the main repo
50
+ cd "$REPO_PATH"
51
+
52
+ # Fetch latest
53
+ echo "Fetching latest from origin..."
54
+ git fetch origin
55
+
56
+ # Try to checkout existing remote branch, or create new one from base
57
+ if git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME"; then
58
+ echo "Creating worktree from existing remote branch..."
59
+ git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"
60
+ else
61
+ echo "Creating new branch '$BRANCH_NAME' from '$BASE_BRANCH'..."
62
+ git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" "origin/$BASE_BRANCH"
63
+ fi
64
+
65
+ echo "✓ Created worktree at $WORKTREE_PATH"
66
+ echo " cd $WORKTREE_PATH"