opencode-gitloops 0.1.0 → 0.2.1

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/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # Gitloops
2
+
3
+ A plugin for [OpenCode](https://opencode.ai) that lets you clone and explore any public GitHub repository locally. Ask questions about a repo's code, structure, and patterns — all without leaving your terminal.
4
+
5
+ Gitloops registers a read-only agent with three custom tools (`gitloops_clone`, `gitloops_refresh`, `gitloops_list`) and restricts itself to `read`, `grep`, `glob`, and `list` — no file modifications, no shell access.
6
+
7
+ ## Configuration
8
+
9
+ Gitloops auto-creates a config file on first load at:
10
+
11
+ ```
12
+ ~/.config/opencode/plugin/gitloops.json
13
+ ```
14
+
15
+ ```json
16
+ {
17
+ "$schema": "https://raw.githubusercontent.com/maharshi-me/gitloops/main/schema/config.schema.json",
18
+ "max_repos": 10,
19
+ "cache_loc": "~/.cache/gitloops/repos",
20
+ "eviction_strategy": "lru"
21
+ }
22
+ ```
23
+
24
+ | Option | Type | Default | Description |
25
+ |---|---|---|---|
26
+ | `max_repos` | `integer` | `10` | Maximum number of cached repos. When exceeded, repos are evicted automatically. |
27
+ | `cache_loc` | `string` | `~/.cache/gitloops/repos` | Directory where cloned repos are stored (`<cache_loc>/<owner>/<repo>/`). Supports `~`. |
28
+ | `eviction_strategy` | `string` | `"lru"` | Strategy for removing repos when `max_repos` is exceeded. |
29
+
30
+ ### Eviction strategies
31
+
32
+ | Strategy | Behavior |
33
+ |---|---|
34
+ | `lru` | Remove the least recently used repo (oldest modification time) |
35
+ | `fifo` | Remove the oldest cloned repo (oldest creation time) |
36
+ | `largest` | Remove the largest repo by disk size |
37
+
38
+ The `$schema` field enables autocompletion and validation in editors that support JSON Schema.
@@ -0,0 +1,25 @@
1
+ export type EvictionStrategy = "lru" | "fifo" | "largest";
2
+ export interface GitloopsConfig {
3
+ max_repos: number;
4
+ cache_loc: string;
5
+ eviction_strategy: EvictionStrategy;
6
+ }
7
+ /**
8
+ * Load the config from disk and merge with defaults.
9
+ * Result is cached after the first call.
10
+ */
11
+ export declare function getConfig(): Promise<GitloopsConfig>;
12
+ /**
13
+ * Reset the cached config so the next getConfig() call re-reads from disk.
14
+ */
15
+ export declare function resetConfigCache(): void;
16
+ /**
17
+ * Create the config file with defaults if it doesn't already exist.
18
+ * Returns true if a new file was created, false if it already existed.
19
+ */
20
+ export declare function ensureConfigFile(): Promise<boolean>;
21
+ /**
22
+ * Returns the config file path (for logging).
23
+ */
24
+ export declare function getConfigPath(): string;
25
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;AAEzD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,gBAAgB,CAAA;CACpC;AAiCD;;;GAGG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC,CA8CzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAyBzD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
package/dist/config.js ADDED
@@ -0,0 +1,104 @@
1
+ import * as fs from "fs/promises";
2
+ import * as path from "path";
3
+ import * as os from "os";
4
+ import { logger } from "./logger";
5
+ const SCHEMA_URL = "https://raw.githubusercontent.com/maharshi-me/gitloops/main/schema/config.schema.json";
6
+ const CONFIG_PATH = path.join(os.homedir(), ".config", "opencode", "plugin", "gitloops.json");
7
+ const DEFAULT_CACHE_LOC = path.join(os.homedir(), ".cache", "gitloops", "repos");
8
+ const DEFAULTS = {
9
+ max_repos: 10,
10
+ cache_loc: DEFAULT_CACHE_LOC,
11
+ eviction_strategy: "lru",
12
+ };
13
+ /**
14
+ * Resolve ~ to the user's home directory.
15
+ */
16
+ function resolvePath(p) {
17
+ if (p.startsWith("~")) {
18
+ return path.join(os.homedir(), p.slice(1));
19
+ }
20
+ return path.resolve(p);
21
+ }
22
+ let _cached = null;
23
+ /**
24
+ * Load the config from disk and merge with defaults.
25
+ * Result is cached after the first call.
26
+ */
27
+ export async function getConfig() {
28
+ if (_cached)
29
+ return _cached;
30
+ let raw = {};
31
+ try {
32
+ const contents = await fs.readFile(CONFIG_PATH, "utf8");
33
+ raw = JSON.parse(contents);
34
+ }
35
+ catch (err) {
36
+ // Distinguish between missing file and invalid JSON
37
+ if (err?.code === "ENOENT") {
38
+ await logger.debug("Config file not found, using defaults", {
39
+ path: CONFIG_PATH,
40
+ });
41
+ }
42
+ else {
43
+ await logger.warn("Config file has invalid JSON, falling back to defaults", {
44
+ path: CONFIG_PATH,
45
+ error: err?.message || String(err),
46
+ });
47
+ }
48
+ }
49
+ const merged = {
50
+ max_repos: typeof raw.max_repos === "number" && raw.max_repos >= 1
51
+ ? raw.max_repos
52
+ : DEFAULTS.max_repos,
53
+ cache_loc: raw.cache_loc
54
+ ? resolvePath(raw.cache_loc)
55
+ : DEFAULTS.cache_loc,
56
+ eviction_strategy: raw.eviction_strategy &&
57
+ ["lru", "fifo", "largest"].includes(raw.eviction_strategy)
58
+ ? raw.eviction_strategy
59
+ : DEFAULTS.eviction_strategy,
60
+ };
61
+ _cached = merged;
62
+ await logger.info("Config loaded", {
63
+ max_repos: merged.max_repos,
64
+ cache_loc: merged.cache_loc,
65
+ eviction_strategy: merged.eviction_strategy,
66
+ });
67
+ return merged;
68
+ }
69
+ /**
70
+ * Reset the cached config so the next getConfig() call re-reads from disk.
71
+ */
72
+ export function resetConfigCache() {
73
+ _cached = null;
74
+ }
75
+ /**
76
+ * Create the config file with defaults if it doesn't already exist.
77
+ * Returns true if a new file was created, false if it already existed.
78
+ */
79
+ export async function ensureConfigFile() {
80
+ try {
81
+ await fs.access(CONFIG_PATH);
82
+ return false; // already exists
83
+ }
84
+ catch {
85
+ // File doesn't exist — create it
86
+ }
87
+ const configDir = path.dirname(CONFIG_PATH);
88
+ await fs.mkdir(configDir, { recursive: true });
89
+ const defaultContent = {
90
+ $schema: SCHEMA_URL,
91
+ max_repos: DEFAULTS.max_repos,
92
+ cache_loc: "~/.cache/gitloops/repos",
93
+ eviction_strategy: DEFAULTS.eviction_strategy,
94
+ };
95
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(defaultContent, null, 2) + "\n", "utf8");
96
+ return true;
97
+ }
98
+ /**
99
+ * Returns the config file path (for logging).
100
+ */
101
+ export function getConfigPath() {
102
+ return CONFIG_PATH;
103
+ }
104
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { GitloopsConfig } from "./config";
2
+ /**
3
+ * Evict repos if the total count exceeds max_repos.
4
+ * Removes repos according to the configured eviction strategy until
5
+ * the count is within the limit.
6
+ *
7
+ * @param config - The current gitloops config
8
+ * @param currentSlug - Optional slug to protect from eviction (e.g. the repo just cloned)
9
+ * @returns Array of evicted repo slugs
10
+ */
11
+ export declare function evictIfNeeded(config: GitloopsConfig, currentSlug?: string): Promise<string[]>;
12
+ //# sourceMappingURL=eviction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eviction.d.ts","sourceRoot":"","sources":["../src/eviction.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,UAAU,CAAA;AAyIhE;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiDnB"}
@@ -0,0 +1,170 @@
1
+ import * as fs from "fs/promises";
2
+ import * as path from "path";
3
+ import { logger } from "./logger";
4
+ /**
5
+ * Recursively compute the total size (in bytes) of a directory.
6
+ */
7
+ async function getDirSize(dirPath) {
8
+ let total = 0;
9
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
10
+ for (const entry of entries) {
11
+ const fullPath = path.join(dirPath, entry.name);
12
+ if (entry.isDirectory()) {
13
+ total += await getDirSize(fullPath);
14
+ }
15
+ else if (entry.isFile()) {
16
+ const stat = await fs.stat(fullPath);
17
+ total += stat.size;
18
+ }
19
+ }
20
+ return total;
21
+ }
22
+ /**
23
+ * Gather stats for all cached repos under the cache location.
24
+ */
25
+ async function gatherRepoStats(cacheLoc) {
26
+ const stats = [];
27
+ let owners;
28
+ try {
29
+ owners = await fs.readdir(cacheLoc);
30
+ }
31
+ catch {
32
+ return stats;
33
+ }
34
+ for (const owner of owners) {
35
+ const ownerPath = path.join(cacheLoc, owner);
36
+ let ownerStat;
37
+ try {
38
+ ownerStat = await fs.stat(ownerPath);
39
+ }
40
+ catch {
41
+ continue;
42
+ }
43
+ if (!ownerStat.isDirectory())
44
+ continue;
45
+ let repoNames;
46
+ try {
47
+ repoNames = await fs.readdir(ownerPath);
48
+ }
49
+ catch {
50
+ continue;
51
+ }
52
+ for (const repo of repoNames) {
53
+ const repoPath = path.join(ownerPath, repo);
54
+ try {
55
+ const gitDir = path.join(repoPath, ".git");
56
+ const gitStat = await fs.stat(gitDir);
57
+ if (!gitStat.isDirectory())
58
+ continue;
59
+ }
60
+ catch {
61
+ continue; // not a git repo
62
+ }
63
+ try {
64
+ const stat = await fs.stat(repoPath);
65
+ stats.push({
66
+ slug: `${owner}/${repo}`,
67
+ repoPath,
68
+ mtime: stat.mtime,
69
+ birthtime: stat.birthtime,
70
+ size: 0, // computed lazily for "largest" strategy
71
+ });
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ }
77
+ }
78
+ return stats;
79
+ }
80
+ /**
81
+ * Remove a cached repo directory and clean up empty parent owner directories.
82
+ */
83
+ async function removeRepo(repoPath, cacheLoc) {
84
+ await fs.rm(repoPath, { recursive: true, force: true });
85
+ // Clean up empty owner directory
86
+ const ownerDir = path.dirname(repoPath);
87
+ try {
88
+ const remaining = await fs.readdir(ownerDir);
89
+ if (remaining.length === 0) {
90
+ await fs.rmdir(ownerDir);
91
+ }
92
+ }
93
+ catch {
94
+ // Ignore — owner dir may already be gone
95
+ }
96
+ }
97
+ /**
98
+ * Sort repos by eviction priority (first element = first to evict).
99
+ */
100
+ async function sortByStrategy(repos, strategy) {
101
+ const sorted = [...repos];
102
+ switch (strategy) {
103
+ case "lru":
104
+ // Least recently modified first
105
+ sorted.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
106
+ break;
107
+ case "fifo":
108
+ // Oldest creation time first
109
+ sorted.sort((a, b) => a.birthtime.getTime() - b.birthtime.getTime());
110
+ break;
111
+ case "largest":
112
+ // Compute sizes then sort largest first
113
+ for (const repo of sorted) {
114
+ repo.size = await getDirSize(repo.repoPath);
115
+ }
116
+ sorted.sort((a, b) => b.size - a.size);
117
+ break;
118
+ }
119
+ return sorted;
120
+ }
121
+ /**
122
+ * Evict repos if the total count exceeds max_repos.
123
+ * Removes repos according to the configured eviction strategy until
124
+ * the count is within the limit.
125
+ *
126
+ * @param config - The current gitloops config
127
+ * @param currentSlug - Optional slug to protect from eviction (e.g. the repo just cloned)
128
+ * @returns Array of evicted repo slugs
129
+ */
130
+ export async function evictIfNeeded(config, currentSlug) {
131
+ const allRepos = await gatherRepoStats(config.cache_loc);
132
+ if (allRepos.length <= config.max_repos) {
133
+ await logger.debug("Eviction check passed", {
134
+ cached: allRepos.length,
135
+ max: config.max_repos,
136
+ });
137
+ return [];
138
+ }
139
+ const evictCount = allRepos.length - config.max_repos;
140
+ await logger.info(`Eviction triggered: ${allRepos.length} cached repos exceeds limit of ${config.max_repos}`, { strategy: config.eviction_strategy, evictCount });
141
+ const sorted = await sortByStrategy(allRepos, config.eviction_strategy);
142
+ // Filter out the current repo from eviction candidates
143
+ const candidates = currentSlug
144
+ ? sorted.filter((r) => r.slug !== currentSlug)
145
+ : sorted;
146
+ const evicted = [];
147
+ for (let i = 0; i < evictCount && i < candidates.length; i++) {
148
+ const repo = candidates[i];
149
+ try {
150
+ await removeRepo(repo.repoPath, config.cache_loc);
151
+ evicted.push(repo.slug);
152
+ await logger.info(`Evicted repo: ${repo.slug}`, {
153
+ strategy: config.eviction_strategy,
154
+ path: repo.repoPath,
155
+ });
156
+ }
157
+ catch (err) {
158
+ await logger.warn(`Failed to evict repo: ${repo.slug}`, {
159
+ path: repo.repoPath,
160
+ error: err?.message || String(err),
161
+ });
162
+ }
163
+ }
164
+ await logger.info(`Eviction complete: removed ${evicted.length} repo(s)`, {
165
+ evicted,
166
+ remaining: allRepos.length - evicted.length,
167
+ });
168
+ return evicted;
169
+ }
170
+ //# sourceMappingURL=eviction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eviction.js","sourceRoot":"","sources":["../src/eviction.ts"],"names":[],"mappings":"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"}
@@ -1 +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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAmDjD,eAAO,MAAM,cAAc,EAAE,MA4E5B,CAAA"}
package/dist/index.js CHANGED
@@ -1,12 +1,15 @@
1
1
  import { gitloops_clone, gitloops_refresh, gitloops_list } from "./tools";
2
2
  import { ensureRepo } from "./repo-manager";
3
+ import { ensureConfigFile, getConfig, getConfigPath } from "./config";
4
+ import { initLogger, logger } from "./logger";
3
5
  import * as fs from "fs/promises";
4
6
  import * as path from "path";
5
7
  import * as os from "os";
6
- const AGENT_MD = `---
8
+ function buildAgentMD(cacheLoc) {
9
+ return `---
7
10
  description: Explore GitHub repositories locally. Clone any public repo and answer questions about its code, structure, and patterns.
8
11
  mode: all
9
- color: "#4078c0"
12
+ color: "#ed5f00"
10
13
  temperature: 0.1
11
14
  tools:
12
15
  read: true
@@ -25,7 +28,7 @@ permission:
25
28
  bash: deny
26
29
  ---
27
30
 
28
- You are GitLoops, a read-only agent for exploring public GitHub repositories locally.
31
+ You are Gitloops, a read-only agent for exploring public GitHub repositories locally.
29
32
 
30
33
  When a user asks about a repository:
31
34
  1. Parse the repo slug from their message (e.g. "facebook/react" or a full GitHub URL)
@@ -40,38 +43,43 @@ Important rules:
40
43
  - All file paths passed to read/grep/glob/list must be absolute paths under the repo's localPath.
41
44
  - When switching between repos in the same session, always call \`gitloops_clone\` again for the new repo.
42
45
 
43
- Repos are cached at: ~/.cache/gitloops/repos/<owner>/<repo>/
46
+ Repos are cached at: ${cacheLoc}/<owner>/<repo>/
44
47
  `;
48
+ }
45
49
  export const GitLoopsPlugin = async ({ client }) => {
50
+ // Initialize the logger so all modules can use it
51
+ initLogger(client);
52
+ await logger.info("Gitloops plugin initialized");
46
53
  return {
47
- // Write agent definition to global config on server connect (idempotent)
54
+ // Write agent definition and ensure config on server connect (idempotent)
48
55
  "server.connected": async () => {
56
+ // Ensure the plugin config file exists (auto-create with defaults)
49
57
  try {
58
+ const created = await ensureConfigFile();
59
+ if (created) {
60
+ await logger.info(`Config created with defaults at ${getConfigPath()}`);
61
+ }
62
+ }
63
+ catch (err) {
64
+ await logger.warn(`Failed to create config: ${err.message || err}`);
65
+ }
66
+ // Write agent definition to global config
67
+ try {
68
+ const config = await getConfig();
69
+ const agentMD = buildAgentMD(config.cache_loc);
50
70
  const agentsDir = path.join(os.homedir(), ".config", "opencode", "agents");
51
71
  const agentPath = path.join(agentsDir, "gitloops.md");
52
72
  await fs.mkdir(agentsDir, { recursive: true });
53
73
  const existing = await fs
54
74
  .readFile(agentPath, "utf8")
55
75
  .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
- });
76
+ if (existing !== agentMD) {
77
+ await fs.writeFile(agentPath, agentMD, "utf8");
78
+ await logger.info(`Agent definition written to ${agentPath}`);
65
79
  }
66
80
  }
67
81
  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
- });
82
+ await logger.warn(`Failed to write agent definition: ${err.message || err}`);
75
83
  }
76
84
  },
77
85
  // Auto-fetch: when a gitloops session is created, pre-warm clone if a slug
@@ -87,13 +95,7 @@ export const GitLoopsPlugin = async ({ client }) => {
87
95
  const slugMatch = session.title?.match(/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/);
88
96
  if (slugMatch) {
89
97
  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
- });
98
+ await logger.info(`Pre-warmed repo: ${slugMatch[1]}`);
97
99
  }
98
100
  }
99
101
  catch {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ type LogLevel = "debug" | "info" | "warn" | "error";
2
+ interface LogClient {
3
+ app: {
4
+ log(opts: {
5
+ body: {
6
+ service: string;
7
+ level: LogLevel;
8
+ message: string;
9
+ extra?: Record<string, unknown>;
10
+ };
11
+ }): Promise<unknown>;
12
+ };
13
+ }
14
+ /**
15
+ * Initialize the logger with the OpenCode plugin client.
16
+ * Call this once during plugin setup.
17
+ */
18
+ export declare function initLogger(client: LogClient): void;
19
+ export declare const logger: {
20
+ debug: (message: string, extra?: Record<string, unknown>) => Promise<void>;
21
+ info: (message: string, extra?: Record<string, unknown>) => Promise<void>;
22
+ warn: (message: string, extra?: Record<string, unknown>) => Promise<void>;
23
+ error: (message: string, extra?: Record<string, unknown>) => Promise<void>;
24
+ };
25
+ export {};
26
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAEA,KAAK,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,UAAU,SAAS;IACjB,GAAG,EAAE;QACH,GAAG,CAAC,IAAI,EAAE;YACR,IAAI,EAAE;gBACJ,OAAO,EAAE,MAAM,CAAA;gBACf,KAAK,EAAE,QAAQ,CAAA;gBACf,OAAO,EAAE,MAAM,CAAA;gBACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAChC,CAAA;SACF,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KACrB,CAAA;CACF;AAID;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAElD;AAqBD,eAAO,MAAM,MAAM;qBACA,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBAExC,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBAEvC,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;qBAEtC,MAAM,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAEzD,CAAA"}
package/dist/logger.js ADDED
@@ -0,0 +1,32 @@
1
+ const SERVICE = "opencode-gitloops";
2
+ let _client = null;
3
+ /**
4
+ * Initialize the logger with the OpenCode plugin client.
5
+ * Call this once during plugin setup.
6
+ */
7
+ export function initLogger(client) {
8
+ _client = client;
9
+ }
10
+ /**
11
+ * Log a structured message via the OpenCode SDK.
12
+ * Silently no-ops if the logger hasn't been initialized yet.
13
+ */
14
+ async function log(level, message, extra) {
15
+ if (!_client)
16
+ return;
17
+ try {
18
+ await _client.app.log({
19
+ body: { service: SERVICE, level, message, ...(extra ? { extra } : {}) },
20
+ });
21
+ }
22
+ catch {
23
+ // Never let logging failures propagate
24
+ }
25
+ }
26
+ export const logger = {
27
+ debug: (message, extra) => log("debug", message, extra),
28
+ info: (message, extra) => log("info", message, extra),
29
+ warn: (message, extra) => log("warn", message, extra),
30
+ error: (message, extra) => log("error", message, extra),
31
+ };
32
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,mBAAmB,CAAA;AAiBnC,IAAI,OAAO,GAAqB,IAAI,CAAA;AAEpC;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,OAAO,GAAG,MAAM,CAAA;AAClB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,GAAG,CAChB,KAAe,EACf,OAAe,EACf,KAA+B;IAE/B,IAAI,CAAC,OAAO;QAAE,OAAM;IACpB,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YACpB,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;SACxE,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,uCAAuC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,OAAe,EAAE,KAA+B,EAAE,EAAE,CAC1D,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC;IAC9B,IAAI,EAAE,CAAC,OAAe,EAAE,KAA+B,EAAE,EAAE,CACzD,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;IAC7B,IAAI,EAAE,CAAC,OAAe,EAAE,KAA+B,EAAE,EAAE,CACzD,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;IAC7B,KAAK,EAAE,CAAC,OAAe,EAAE,KAA+B,EAAE,EAAE,CAC1D,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC;CAC/B,CAAA"}
@@ -30,15 +30,16 @@ export declare function parseRepoSlug(input: string): ParsedRepo;
30
30
  /**
31
31
  * Get the local filesystem path where a repo is (or would be) cached.
32
32
  */
33
- export declare function getLocalPath(slug: string): string;
33
+ export declare function getLocalPath(slug: string): Promise<string>;
34
34
  /**
35
35
  * Clone a repo (or fetch latest if already cloned). Returns metadata about the repo.
36
36
  *
37
37
  * Respects GITLOOPS_FULL_CLONE env var — if truthy, clones without --depth=1.
38
+ * Enforces max_repos limit via the configured eviction strategy after cloning.
38
39
  */
39
40
  export declare function ensureRepo(input: string): Promise<RepoInfo>;
40
41
  /**
41
- * List all repos currently cached under ~/.cache/gitloops/repos/.
42
+ * List all repos currently cached under the configured cache location.
42
43
  */
43
44
  export declare function listCachedRepos(): Promise<CachedRepo[]>;
44
45
  //# sourceMappingURL=repo-manager.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"repo-manager.d.ts","sourceRoot":"","sources":["../src/repo-manager.ts"],"names":[],"mappings":"AAOA,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;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA8CvD;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIhE;AA0BD;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwFjE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAgD7D"}
@@ -1,8 +1,9 @@
1
1
  import { $ } from "bun";
2
2
  import * as fs from "fs/promises";
3
3
  import * as path from "path";
4
- import * as os from "os";
5
- const CACHE_ROOT = path.join(os.homedir(), ".cache", "gitloops", "repos");
4
+ import { getConfig } from "./config";
5
+ import { evictIfNeeded } from "./eviction";
6
+ import { logger } from "./logger";
6
7
  /**
7
8
  * Parse a repo slug or URL into its components.
8
9
  *
@@ -52,9 +53,10 @@ export function parseRepoSlug(input) {
52
53
  /**
53
54
  * Get the local filesystem path where a repo is (or would be) cached.
54
55
  */
55
- export function getLocalPath(slug) {
56
+ export async function getLocalPath(slug) {
56
57
  const { owner, repo } = parseRepoSlug(slug);
57
- return path.join(CACHE_ROOT, owner, repo);
58
+ const config = await getConfig();
59
+ return path.join(config.cache_loc, owner, repo);
58
60
  }
59
61
  /**
60
62
  * Check if a directory exists.
@@ -84,14 +86,25 @@ async function getLastCommit(repoPath) {
84
86
  * Clone a repo (or fetch latest if already cloned). Returns metadata about the repo.
85
87
  *
86
88
  * Respects GITLOOPS_FULL_CLONE env var — if truthy, clones without --depth=1.
89
+ * Enforces max_repos limit via the configured eviction strategy after cloning.
87
90
  */
88
91
  export async function ensureRepo(input) {
89
92
  const parsed = parseRepoSlug(input);
90
- const localPath = path.join(CACHE_ROOT, parsed.owner, parsed.repo);
93
+ await logger.debug(`Parsed repo identifier: ${parsed.slug}`, {
94
+ input,
95
+ owner: parsed.owner,
96
+ repo: parsed.repo,
97
+ });
98
+ const config = await getConfig();
99
+ const localPath = path.join(config.cache_loc, parsed.owner, parsed.repo);
91
100
  const fullClone = process.env.GITLOOPS_FULL_CLONE === "true";
92
101
  const depthArgs = fullClone ? [] : ["--depth=1"];
93
102
  if (await dirExists(path.join(localPath, ".git"))) {
94
103
  // Repo already cloned — fetch latest
104
+ await logger.info(`Fetching updates for ${parsed.slug}`, {
105
+ path: localPath,
106
+ fullClone,
107
+ });
95
108
  try {
96
109
  if (fullClone) {
97
110
  await $ `git -C ${localPath} fetch origin`.quiet();
@@ -100,26 +113,49 @@ export async function ensureRepo(input) {
100
113
  await $ `git -C ${localPath} fetch --depth=1 origin`.quiet();
101
114
  }
102
115
  await $ `git -C ${localPath} reset --hard origin/HEAD`.quiet();
116
+ await logger.info(`Updated repo: ${parsed.slug}`);
103
117
  }
104
118
  catch (err) {
119
+ await logger.error(`Failed to fetch updates for ${parsed.slug}`, {
120
+ error: err?.message || String(err),
121
+ });
105
122
  throw new Error(`Failed to fetch updates for ${parsed.slug}: ${err.message || err}`);
106
123
  }
107
124
  }
108
125
  else {
109
126
  // Fresh clone
127
+ await logger.info(`Cloning ${parsed.slug}`, {
128
+ url: parsed.cloneUrl,
129
+ path: localPath,
130
+ fullClone,
131
+ });
132
+ const startTime = Date.now();
110
133
  await fs.mkdir(path.dirname(localPath), { recursive: true });
111
134
  try {
112
135
  await $ `git clone ${depthArgs} ${parsed.cloneUrl} ${localPath}`.quiet();
136
+ const duration = Date.now() - startTime;
137
+ await logger.info(`Cloned repo: ${parsed.slug} (${duration}ms)`, {
138
+ path: localPath,
139
+ durationMs: duration,
140
+ });
113
141
  }
114
142
  catch (err) {
115
143
  // Clean up partial clone on failure
116
144
  await fs.rm(localPath, { recursive: true, force: true }).catch(() => { });
117
145
  if (String(err).includes("not found") ||
118
146
  String(err).includes("Repository not found")) {
147
+ await logger.error(`Repository not found: ${parsed.slug}`, {
148
+ url: parsed.cloneUrl,
149
+ });
119
150
  throw new Error(`Repository "${parsed.slug}" not found on GitHub. Only public repos are supported in v1.`);
120
151
  }
152
+ await logger.error(`Failed to clone ${parsed.slug}`, {
153
+ error: err?.message || String(err),
154
+ });
121
155
  throw new Error(`Failed to clone ${parsed.slug}: ${err.message || err}`);
122
156
  }
157
+ // Evict old repos if we've exceeded the max
158
+ await evictIfNeeded(config, parsed.slug);
123
159
  }
124
160
  const lastCommit = await getLastCommit(localPath);
125
161
  return {
@@ -132,22 +168,24 @@ export async function ensureRepo(input) {
132
168
  };
133
169
  }
134
170
  /**
135
- * List all repos currently cached under ~/.cache/gitloops/repos/.
171
+ * List all repos currently cached under the configured cache location.
136
172
  */
137
173
  export async function listCachedRepos() {
174
+ const config = await getConfig();
175
+ const cacheLoc = config.cache_loc;
138
176
  const repos = [];
139
- if (!(await dirExists(CACHE_ROOT))) {
177
+ if (!(await dirExists(cacheLoc))) {
140
178
  return repos;
141
179
  }
142
180
  let owners;
143
181
  try {
144
- owners = await fs.readdir(CACHE_ROOT);
182
+ owners = await fs.readdir(cacheLoc);
145
183
  }
146
184
  catch {
147
185
  return repos;
148
186
  }
149
187
  for (const owner of owners) {
150
- const ownerPath = path.join(CACHE_ROOT, owner);
188
+ const ownerPath = path.join(cacheLoc, owner);
151
189
  if (!(await dirExists(ownerPath)))
152
190
  continue;
153
191
  let repoNames;
@@ -1 +1 @@
1
- {"version":3,"file":"repo-manager.js","sourceRoot":"","sources":["../src/repo-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AAwBxB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;AAEzE;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAE5B,yBAAyB;IACzB,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAC9B,2EAA2E,CAC5E,CAAA;IACD,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,UAAU,CAAA;QAClC,OAAO;YACL,KAAK;YACL,IAAI;YACJ,QAAQ,EAAE,sBAAsB,KAAK,IAAI,IAAI,MAAM;YACnD,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE;SACzB,CAAA;IACH,CAAC;IAED,kBAAkB;IAClB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAC5B,mEAAmE,CACpE,CAAA;IACD,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAA;QAChC,OAAO;YACL,KAAK;YACL,IAAI;YACJ,QAAQ,EAAE,sBAAsB,KAAK,IAAI,IAAI,MAAM;YACnD,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE;SACzB,CAAA;IACH,CAAC;IAED,8BAA8B;IAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IACzE,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,SAAS,CAAA;QACjC,OAAO;YACL,KAAK;YACL,IAAI;YACJ,QAAQ,EAAE,sBAAsB,KAAK,IAAI,IAAI,MAAM;YACnD,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE;SACzB,CAAA;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,8DAA8D,CACjG,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;IAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;AAC3C,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACnC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,aAAa,CAAC,QAAgB;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,CAAA,UAAU,QAAQ,yBAAyB,CAAC,IAAI,EAAE,CAAA;QACxE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAa;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAA;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;IAClE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,MAAM,CAAA;IAC5D,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IAEhD,IAAI,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QAClD,qCAAqC;QACrC,IAAI,CAAC;YACH,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,CAAC,CAAA,UAAU,SAAS,eAAe,CAAC,KAAK,EAAE,CAAA;YACnD,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,CAAA,UAAU,SAAS,yBAAyB,CAAC,KAAK,EAAE,CAAA;YAC7D,CAAC;YACD,MAAM,CAAC,CAAA,UAAU,SAAS,2BAA2B,CAAC,KAAK,EAAE,CAAA;QAC/D,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,+BAA+B,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,IAAI,GAAG,EAAE,CACpE,CAAA;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,cAAc;QACd,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5D,IAAI,CAAC;YACH,MAAM,CAAC,CAAA,aAAa,SAAS,IAAI,MAAM,CAAC,QAAQ,IAAI,SAAS,EAAE,CAAC,KAAK,EAAE,CAAA;QACzE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,oCAAoC;YACpC,MAAM,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACxE,IACE,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;gBACjC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAC5C,CAAC;gBACD,MAAM,IAAI,KAAK,CACb,eAAe,MAAM,CAAC,IAAI,+DAA+D,CAC1F,CAAA;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CACb,mBAAmB,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,IAAI,GAAG,EAAE,CACxD,CAAA;QACH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAA;IAEjD,OAAO;QACL,SAAS;QACT,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,UAAU;QACV,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACtC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,KAAK,GAAiB,EAAE,CAAA;IAE9B,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QACnC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,MAAgB,CAAA;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAA;QAC9C,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;YAAE,SAAQ;QAE3C,IAAI,SAAmB,CAAA;QACvB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;YAC3C,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;gBAAE,SAAQ;YAE7D,IAAI,YAAoB,CAAA;YACxB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBACpC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAA;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY,GAAG,SAAS,CAAA;YAC1B,CAAC;YAED,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE;gBACxB,SAAS,EAAE,QAAQ;gBACnB,YAAY;aACb,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC"}
1
+ {"version":3,"file":"repo-manager.js","sourceRoot":"","sources":["../src/repo-manager.ts"],"names":[],"mappings":"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"}
@@ -1 +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"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc;;;;;;;;;;CA0DzB,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;CA0B3B,CAAA;AAEF,eAAO,MAAM,aAAa;;;;CAqBxB,CAAA"}
package/dist/tools.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { tool } from "@opencode-ai/plugin";
2
2
  import { ensureRepo, listCachedRepos } from "./repo-manager";
3
+ import { logger } from "./logger";
3
4
  export const gitloops_clone = tool({
4
5
  description: "Clone or ensure a GitHub repo is available locally for exploration. " +
5
6
  "Returns the local filesystem path to use with read/grep/glob/list tools. " +
@@ -14,19 +15,30 @@ export const gitloops_clone = tool({
14
15
  .describe("Branch to checkout after cloning (default: repo default branch)"),
15
16
  },
16
17
  async execute(args) {
18
+ await logger.info("Tool invoked: gitloops_clone", {
19
+ repo: args.repo,
20
+ branch: args.branch ?? null,
21
+ });
17
22
  const info = await ensureRepo(args.repo);
18
23
  // If a specific branch was requested, check it out
19
24
  if (args.branch) {
25
+ await logger.info(`Checking out branch "${args.branch}" for ${info.slug}`);
20
26
  const { $ } = await import("bun");
21
27
  try {
22
28
  await $ `git -C ${info.localPath} fetch origin ${args.branch}`.quiet();
23
29
  await $ `git -C ${info.localPath} checkout ${args.branch}`.quiet();
24
30
  await $ `git -C ${info.localPath} reset --hard origin/${args.branch}`.quiet();
31
+ await logger.info(`Checked out branch "${args.branch}" for ${info.slug}`);
25
32
  }
26
33
  catch (err) {
34
+ await logger.error(`Failed to checkout branch "${args.branch}" for ${info.slug}`, { error: err?.message || String(err) });
27
35
  throw new Error(`Failed to checkout branch "${args.branch}" for ${info.slug}: ${err.message || err}`);
28
36
  }
29
37
  }
38
+ await logger.debug("gitloops_clone complete", {
39
+ slug: info.slug,
40
+ path: info.localPath,
41
+ });
30
42
  return [
31
43
  `Repository: ${info.slug}`,
32
44
  `Local path: ${info.localPath}`,
@@ -46,7 +58,12 @@ export const gitloops_refresh = tool({
46
58
  .describe("Repo identifier — e.g. 'facebook/react'"),
47
59
  },
48
60
  async execute(args) {
61
+ await logger.info("Tool invoked: gitloops_refresh", { repo: args.repo });
49
62
  const info = await ensureRepo(args.repo);
63
+ await logger.debug("gitloops_refresh complete", {
64
+ slug: info.slug,
65
+ commit: info.lastCommit,
66
+ });
50
67
  return [
51
68
  `Refreshed: ${info.slug}`,
52
69
  `Local path: ${info.localPath}`,
@@ -60,7 +77,9 @@ export const gitloops_list = tool({
60
77
  "Shows slug, local path, and last modified time for each cached repo.",
61
78
  args: {},
62
79
  async execute() {
80
+ await logger.info("Tool invoked: gitloops_list");
63
81
  const repos = await listCachedRepos();
82
+ await logger.debug("gitloops_list complete", { count: repos.length });
64
83
  if (repos.length === 0) {
65
84
  return "No repos cached. Use gitloops_clone to clone a repo first.";
66
85
  }
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gitloops",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "GitHub repo explorer agent and plugin for OpenCode. Clone and explore any public GitHub repo locally.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,8 @@
12
12
  }
13
13
  },
14
14
  "files": [
15
- "dist/"
15
+ "dist/",
16
+ "schema/"
16
17
  ],
17
18
  "keywords": [
18
19
  "opencode",
@@ -24,6 +25,7 @@
24
25
  ],
25
26
  "scripts": {
26
27
  "build": "bun run tsc",
28
+ "typecheck": "tsc --noEmit",
27
29
  "prepublishOnly": "bun run build"
28
30
  },
29
31
  "dependencies": {
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://raw.githubusercontent.com/maharshi-me/gitloops/main/schema/config.schema.json",
4
+ "title": "Gitloops Configuration",
5
+ "description": "Configuration for the opencode-gitloops plugin.",
6
+ "type": "object",
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string",
10
+ "description": "Path or URL to the JSON schema for this config file."
11
+ },
12
+ "max_repos": {
13
+ "type": "integer",
14
+ "minimum": 1,
15
+ "default": 10,
16
+ "description": "Maximum number of cached repositories. When exceeded, repos are evicted based on the eviction_strategy."
17
+ },
18
+ "cache_loc": {
19
+ "type": "string",
20
+ "default": "~/.cache/gitloops/repos",
21
+ "description": "Directory where cloned repositories are stored. Repos are placed directly at <cache_loc>/<owner>/<repo>/. Supports ~ for the home directory."
22
+ },
23
+ "eviction_strategy": {
24
+ "type": "string",
25
+ "enum": ["lru", "fifo", "largest"],
26
+ "default": "lru",
27
+ "description": "Strategy used to evict repos when max_repos is exceeded. 'lru' removes least recently used, 'fifo' removes oldest cloned, 'largest' removes the largest repo by disk size."
28
+ }
29
+ },
30
+ "additionalProperties": false
31
+ }