opencode-gitloops 0.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/repo-manager.d.ts +44 -0
- package/dist/repo-manager.d.ts.map +1 -0
- package/dist/repo-manager.js +181 -0
- package/dist/repo-manager.js.map +1 -0
- package/dist/tools.d.ts +26 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +71 -0
- package/dist/tools.js.map +1 -0
- package/package.json +45 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AA+CjD,eAAO,MAAM,cAAc,EAAE,MA0E5B,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { gitloops_clone, gitloops_refresh, gitloops_list } from "./tools";
|
|
2
|
+
import { ensureRepo } from "./repo-manager";
|
|
3
|
+
import * as fs from "fs/promises";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import * as os from "os";
|
|
6
|
+
const AGENT_MD = `---
|
|
7
|
+
description: Explore GitHub repositories locally. Clone any public repo and answer questions about its code, structure, and patterns.
|
|
8
|
+
mode: all
|
|
9
|
+
color: "#4078c0"
|
|
10
|
+
temperature: 0.1
|
|
11
|
+
tools:
|
|
12
|
+
read: true
|
|
13
|
+
grep: true
|
|
14
|
+
glob: true
|
|
15
|
+
list: true
|
|
16
|
+
webfetch: false
|
|
17
|
+
bash: false
|
|
18
|
+
edit: false
|
|
19
|
+
write: false
|
|
20
|
+
gitloops_clone: true
|
|
21
|
+
gitloops_refresh: true
|
|
22
|
+
gitloops_list: true
|
|
23
|
+
permission:
|
|
24
|
+
edit: deny
|
|
25
|
+
bash: deny
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
You are GitLoops, a read-only agent for exploring public GitHub repositories locally.
|
|
29
|
+
|
|
30
|
+
When a user asks about a repository:
|
|
31
|
+
1. Parse the repo slug from their message (e.g. "facebook/react" or a full GitHub URL)
|
|
32
|
+
2. Call \`gitloops_clone\` with the slug to clone or refresh the repo and get its local path
|
|
33
|
+
3. Use \`read\`, \`grep\`, \`glob\`, and \`list\` with absolute paths under the returned \`localPath\` to explore the code
|
|
34
|
+
4. Call \`gitloops_refresh\` if the user explicitly wants the latest changes
|
|
35
|
+
5. Call \`gitloops_list\` to show which repos are already cached locally
|
|
36
|
+
|
|
37
|
+
Important rules:
|
|
38
|
+
- You CANNOT modify any files. You are strictly read-only.
|
|
39
|
+
- Always call \`gitloops_clone\` first before trying to read any files from a repo.
|
|
40
|
+
- All file paths passed to read/grep/glob/list must be absolute paths under the repo's localPath.
|
|
41
|
+
- When switching between repos in the same session, always call \`gitloops_clone\` again for the new repo.
|
|
42
|
+
|
|
43
|
+
Repos are cached at: ~/.cache/gitloops/repos/<owner>/<repo>/
|
|
44
|
+
`;
|
|
45
|
+
export const GitLoopsPlugin = async ({ client }) => {
|
|
46
|
+
return {
|
|
47
|
+
// Write agent definition to global config on server connect (idempotent)
|
|
48
|
+
"server.connected": async () => {
|
|
49
|
+
try {
|
|
50
|
+
const agentsDir = path.join(os.homedir(), ".config", "opencode", "agents");
|
|
51
|
+
const agentPath = path.join(agentsDir, "gitloops.md");
|
|
52
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
53
|
+
const existing = await fs
|
|
54
|
+
.readFile(agentPath, "utf8")
|
|
55
|
+
.catch(() => null);
|
|
56
|
+
if (existing !== AGENT_MD) {
|
|
57
|
+
await fs.writeFile(agentPath, AGENT_MD, "utf8");
|
|
58
|
+
await client.app.log({
|
|
59
|
+
body: {
|
|
60
|
+
service: "opencode-gitloops",
|
|
61
|
+
level: "info",
|
|
62
|
+
message: `Agent definition written to ${agentPath}`,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
await client.app.log({
|
|
69
|
+
body: {
|
|
70
|
+
service: "opencode-gitloops",
|
|
71
|
+
level: "warn",
|
|
72
|
+
message: `Failed to write agent definition: ${err.message || err}`,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
// Auto-fetch: when a gitloops session is created, pre-warm clone if a slug
|
|
78
|
+
// is detectable from the session title
|
|
79
|
+
event: async ({ event }) => {
|
|
80
|
+
if (event.type !== "session.created")
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
const session = event.properties;
|
|
84
|
+
if (!session || session.agentID !== "gitloops")
|
|
85
|
+
return;
|
|
86
|
+
// Try to find a slug in the session title
|
|
87
|
+
const slugMatch = session.title?.match(/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/);
|
|
88
|
+
if (slugMatch) {
|
|
89
|
+
await ensureRepo(slugMatch[1]);
|
|
90
|
+
await client.app.log({
|
|
91
|
+
body: {
|
|
92
|
+
service: "opencode-gitloops",
|
|
93
|
+
level: "info",
|
|
94
|
+
message: `Pre-warmed repo: ${slugMatch[1]}`,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Silent — agent will call gitloops_clone explicitly
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
tool: {
|
|
104
|
+
gitloops_clone,
|
|
105
|
+
gitloops_refresh,
|
|
106
|
+
gitloops_list,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface RepoInfo {
|
|
2
|
+
localPath: string;
|
|
3
|
+
slug: string;
|
|
4
|
+
owner: string;
|
|
5
|
+
repo: string;
|
|
6
|
+
lastCommit: string;
|
|
7
|
+
lastFetched: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CachedRepo {
|
|
10
|
+
slug: string;
|
|
11
|
+
localPath: string;
|
|
12
|
+
lastModified: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ParsedRepo {
|
|
15
|
+
owner: string;
|
|
16
|
+
repo: string;
|
|
17
|
+
cloneUrl: string;
|
|
18
|
+
slug: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse a repo slug or URL into its components.
|
|
22
|
+
*
|
|
23
|
+
* Accepts:
|
|
24
|
+
* - "facebook/react"
|
|
25
|
+
* - "https://github.com/facebook/react"
|
|
26
|
+
* - "https://github.com/facebook/react.git"
|
|
27
|
+
* - "git@github.com:facebook/react.git"
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseRepoSlug(input: string): ParsedRepo;
|
|
30
|
+
/**
|
|
31
|
+
* Get the local filesystem path where a repo is (or would be) cached.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getLocalPath(slug: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Clone a repo (or fetch latest if already cloned). Returns metadata about the repo.
|
|
36
|
+
*
|
|
37
|
+
* Respects GITLOOPS_FULL_CLONE env var — if truthy, clones without --depth=1.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ensureRepo(input: string): Promise<RepoInfo>;
|
|
40
|
+
/**
|
|
41
|
+
* List all repos currently cached under ~/.cache/gitloops/repos/.
|
|
42
|
+
*/
|
|
43
|
+
export declare function listCachedRepos(): Promise<CachedRepo[]>;
|
|
44
|
+
//# sourceMappingURL=repo-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-manager.d.ts","sourceRoot":"","sources":["../src/repo-manager.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA8CvD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD;AA0BD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoDjE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CA8C7D"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { $ } from "bun";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
const CACHE_ROOT = path.join(os.homedir(), ".cache", "gitloops", "repos");
|
|
6
|
+
/**
|
|
7
|
+
* Parse a repo slug or URL into its components.
|
|
8
|
+
*
|
|
9
|
+
* Accepts:
|
|
10
|
+
* - "facebook/react"
|
|
11
|
+
* - "https://github.com/facebook/react"
|
|
12
|
+
* - "https://github.com/facebook/react.git"
|
|
13
|
+
* - "git@github.com:facebook/react.git"
|
|
14
|
+
*/
|
|
15
|
+
export function parseRepoSlug(input) {
|
|
16
|
+
const trimmed = input.trim();
|
|
17
|
+
// Handle full HTTPS URLs
|
|
18
|
+
const httpsMatch = trimmed.match(/^https?:\/\/github\.com\/([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/);
|
|
19
|
+
if (httpsMatch) {
|
|
20
|
+
const [, owner, repo] = httpsMatch;
|
|
21
|
+
return {
|
|
22
|
+
owner,
|
|
23
|
+
repo,
|
|
24
|
+
cloneUrl: `https://github.com/${owner}/${repo}.git`,
|
|
25
|
+
slug: `${owner}/${repo}`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Handle SSH URLs
|
|
29
|
+
const sshMatch = trimmed.match(/^git@github\.com:([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+?)(?:\.git)?$/);
|
|
30
|
+
if (sshMatch) {
|
|
31
|
+
const [, owner, repo] = sshMatch;
|
|
32
|
+
return {
|
|
33
|
+
owner,
|
|
34
|
+
repo,
|
|
35
|
+
cloneUrl: `https://github.com/${owner}/${repo}.git`,
|
|
36
|
+
slug: `${owner}/${repo}`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Handle owner/repo shorthand
|
|
40
|
+
const slugMatch = trimmed.match(/^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/);
|
|
41
|
+
if (slugMatch) {
|
|
42
|
+
const [, owner, repo] = slugMatch;
|
|
43
|
+
return {
|
|
44
|
+
owner,
|
|
45
|
+
repo,
|
|
46
|
+
cloneUrl: `https://github.com/${owner}/${repo}.git`,
|
|
47
|
+
slug: `${owner}/${repo}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Invalid repo identifier: "${input}". Expected "owner/repo", a GitHub HTTPS URL, or an SSH URL.`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the local filesystem path where a repo is (or would be) cached.
|
|
54
|
+
*/
|
|
55
|
+
export function getLocalPath(slug) {
|
|
56
|
+
const { owner, repo } = parseRepoSlug(slug);
|
|
57
|
+
return path.join(CACHE_ROOT, owner, repo);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check if a directory exists.
|
|
61
|
+
*/
|
|
62
|
+
async function dirExists(dirPath) {
|
|
63
|
+
try {
|
|
64
|
+
const stat = await fs.stat(dirPath);
|
|
65
|
+
return stat.isDirectory();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the latest commit hash from a local repo.
|
|
73
|
+
*/
|
|
74
|
+
async function getLastCommit(repoPath) {
|
|
75
|
+
try {
|
|
76
|
+
const result = await $ `git -C ${repoPath} rev-parse --short HEAD`.text();
|
|
77
|
+
return result.trim();
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return "unknown";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Clone a repo (or fetch latest if already cloned). Returns metadata about the repo.
|
|
85
|
+
*
|
|
86
|
+
* Respects GITLOOPS_FULL_CLONE env var — if truthy, clones without --depth=1.
|
|
87
|
+
*/
|
|
88
|
+
export async function ensureRepo(input) {
|
|
89
|
+
const parsed = parseRepoSlug(input);
|
|
90
|
+
const localPath = path.join(CACHE_ROOT, parsed.owner, parsed.repo);
|
|
91
|
+
const fullClone = process.env.GITLOOPS_FULL_CLONE === "true";
|
|
92
|
+
const depthArgs = fullClone ? [] : ["--depth=1"];
|
|
93
|
+
if (await dirExists(path.join(localPath, ".git"))) {
|
|
94
|
+
// Repo already cloned — fetch latest
|
|
95
|
+
try {
|
|
96
|
+
if (fullClone) {
|
|
97
|
+
await $ `git -C ${localPath} fetch origin`.quiet();
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
await $ `git -C ${localPath} fetch --depth=1 origin`.quiet();
|
|
101
|
+
}
|
|
102
|
+
await $ `git -C ${localPath} reset --hard origin/HEAD`.quiet();
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
throw new Error(`Failed to fetch updates for ${parsed.slug}: ${err.message || err}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Fresh clone
|
|
110
|
+
await fs.mkdir(path.dirname(localPath), { recursive: true });
|
|
111
|
+
try {
|
|
112
|
+
await $ `git clone ${depthArgs} ${parsed.cloneUrl} ${localPath}`.quiet();
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
// Clean up partial clone on failure
|
|
116
|
+
await fs.rm(localPath, { recursive: true, force: true }).catch(() => { });
|
|
117
|
+
if (String(err).includes("not found") ||
|
|
118
|
+
String(err).includes("Repository not found")) {
|
|
119
|
+
throw new Error(`Repository "${parsed.slug}" not found on GitHub. Only public repos are supported in v1.`);
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`Failed to clone ${parsed.slug}: ${err.message || err}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const lastCommit = await getLastCommit(localPath);
|
|
125
|
+
return {
|
|
126
|
+
localPath,
|
|
127
|
+
slug: parsed.slug,
|
|
128
|
+
owner: parsed.owner,
|
|
129
|
+
repo: parsed.repo,
|
|
130
|
+
lastCommit,
|
|
131
|
+
lastFetched: new Date().toISOString(),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* List all repos currently cached under ~/.cache/gitloops/repos/.
|
|
136
|
+
*/
|
|
137
|
+
export async function listCachedRepos() {
|
|
138
|
+
const repos = [];
|
|
139
|
+
if (!(await dirExists(CACHE_ROOT))) {
|
|
140
|
+
return repos;
|
|
141
|
+
}
|
|
142
|
+
let owners;
|
|
143
|
+
try {
|
|
144
|
+
owners = await fs.readdir(CACHE_ROOT);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return repos;
|
|
148
|
+
}
|
|
149
|
+
for (const owner of owners) {
|
|
150
|
+
const ownerPath = path.join(CACHE_ROOT, owner);
|
|
151
|
+
if (!(await dirExists(ownerPath)))
|
|
152
|
+
continue;
|
|
153
|
+
let repoNames;
|
|
154
|
+
try {
|
|
155
|
+
repoNames = await fs.readdir(ownerPath);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
for (const repo of repoNames) {
|
|
161
|
+
const repoPath = path.join(ownerPath, repo);
|
|
162
|
+
if (!(await dirExists(path.join(repoPath, ".git"))))
|
|
163
|
+
continue;
|
|
164
|
+
let lastModified;
|
|
165
|
+
try {
|
|
166
|
+
const stat = await fs.stat(repoPath);
|
|
167
|
+
lastModified = stat.mtime.toISOString();
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
lastModified = "unknown";
|
|
171
|
+
}
|
|
172
|
+
repos.push({
|
|
173
|
+
slug: `${owner}/${repo}`,
|
|
174
|
+
localPath: repoPath,
|
|
175
|
+
lastModified,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return repos;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=repo-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-manager.js","sourceRoot":"","sources":["../src/repo-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const gitloops_clone: {
|
|
2
|
+
description: string;
|
|
3
|
+
args: {
|
|
4
|
+
repo: import("zod").ZodString;
|
|
5
|
+
branch: import("zod").ZodOptional<import("zod").ZodString>;
|
|
6
|
+
};
|
|
7
|
+
execute(args: {
|
|
8
|
+
repo: string;
|
|
9
|
+
branch?: string | undefined;
|
|
10
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
11
|
+
};
|
|
12
|
+
export declare const gitloops_refresh: {
|
|
13
|
+
description: string;
|
|
14
|
+
args: {
|
|
15
|
+
repo: import("zod").ZodString;
|
|
16
|
+
};
|
|
17
|
+
execute(args: {
|
|
18
|
+
repo: string;
|
|
19
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
20
|
+
};
|
|
21
|
+
export declare const gitloops_list: {
|
|
22
|
+
description: string;
|
|
23
|
+
args: {};
|
|
24
|
+
execute(args: Record<string, never>, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc;;;;;;;;;;CA0CzB,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;CAmB3B,CAAA;AAEF,eAAO,MAAM,aAAa;;;;CAiBxB,CAAA"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { ensureRepo, listCachedRepos } from "./repo-manager";
|
|
3
|
+
export const gitloops_clone = tool({
|
|
4
|
+
description: "Clone or ensure a GitHub repo is available locally for exploration. " +
|
|
5
|
+
"Returns the local filesystem path to use with read/grep/glob/list tools. " +
|
|
6
|
+
"Accepts 'owner/repo' shorthand or a full GitHub URL.",
|
|
7
|
+
args: {
|
|
8
|
+
repo: tool.schema
|
|
9
|
+
.string()
|
|
10
|
+
.describe("Repo identifier — e.g. 'facebook/react' or 'https://github.com/facebook/react'"),
|
|
11
|
+
branch: tool.schema
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Branch to checkout after cloning (default: repo default branch)"),
|
|
15
|
+
},
|
|
16
|
+
async execute(args) {
|
|
17
|
+
const info = await ensureRepo(args.repo);
|
|
18
|
+
// If a specific branch was requested, check it out
|
|
19
|
+
if (args.branch) {
|
|
20
|
+
const { $ } = await import("bun");
|
|
21
|
+
try {
|
|
22
|
+
await $ `git -C ${info.localPath} fetch origin ${args.branch}`.quiet();
|
|
23
|
+
await $ `git -C ${info.localPath} checkout ${args.branch}`.quiet();
|
|
24
|
+
await $ `git -C ${info.localPath} reset --hard origin/${args.branch}`.quiet();
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
throw new Error(`Failed to checkout branch "${args.branch}" for ${info.slug}: ${err.message || err}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return [
|
|
31
|
+
`Repository: ${info.slug}`,
|
|
32
|
+
`Local path: ${info.localPath}`,
|
|
33
|
+
`Last commit: ${info.lastCommit}`,
|
|
34
|
+
`Last fetched: ${info.lastFetched}`,
|
|
35
|
+
``,
|
|
36
|
+
`Use this path with read, grep, glob, and list tools to explore the repo.`,
|
|
37
|
+
].join("\n");
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
export const gitloops_refresh = tool({
|
|
41
|
+
description: "Force-fetch the latest changes for a previously cloned GitHub repo. " +
|
|
42
|
+
"Use this when the user wants the most up-to-date code.",
|
|
43
|
+
args: {
|
|
44
|
+
repo: tool.schema
|
|
45
|
+
.string()
|
|
46
|
+
.describe("Repo identifier — e.g. 'facebook/react'"),
|
|
47
|
+
},
|
|
48
|
+
async execute(args) {
|
|
49
|
+
const info = await ensureRepo(args.repo);
|
|
50
|
+
return [
|
|
51
|
+
`Refreshed: ${info.slug}`,
|
|
52
|
+
`Local path: ${info.localPath}`,
|
|
53
|
+
`Last commit: ${info.lastCommit}`,
|
|
54
|
+
`Fetched at: ${info.lastFetched}`,
|
|
55
|
+
].join("\n");
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
export const gitloops_list = tool({
|
|
59
|
+
description: "List all GitHub repos currently cached locally by gitloops. " +
|
|
60
|
+
"Shows slug, local path, and last modified time for each cached repo.",
|
|
61
|
+
args: {},
|
|
62
|
+
async execute() {
|
|
63
|
+
const repos = await listCachedRepos();
|
|
64
|
+
if (repos.length === 0) {
|
|
65
|
+
return "No repos cached. Use gitloops_clone to clone a repo first.";
|
|
66
|
+
}
|
|
67
|
+
const lines = repos.map((r) => `${r.slug} ${r.localPath} (modified: ${r.lastModified})`);
|
|
68
|
+
return [`Cached repos (${repos.length}):`, "", ...lines].join("\n");
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-gitloops",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "GitHub repo explorer agent and plugin for OpenCode. Clone and explore any public GitHub repo locally.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"opencode",
|
|
19
|
+
"opencode-plugin",
|
|
20
|
+
"github",
|
|
21
|
+
"git",
|
|
22
|
+
"explorer",
|
|
23
|
+
"gitloops"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "bun run tsc",
|
|
27
|
+
"prepublishOnly": "bun run build"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@opencode-ai/plugin": "latest"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/bun": "latest",
|
|
34
|
+
"typescript": "^5.5.0"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/maharshi-me/gitloops"
|
|
40
|
+
},
|
|
41
|
+
"author": "",
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
}
|
|
45
|
+
}
|