opencode-ask-github 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/LICENSE +21 -0
- package/README.md +84 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/src/commands/ask.d.ts +7 -0
- package/dist/src/commands/ask.d.ts.map +1 -0
- package/dist/src/commands/ask.js +139 -0
- package/dist/src/commands/ask.js.map +1 -0
- package/dist/src/commands/list.d.ts +7 -0
- package/dist/src/commands/list.d.ts.map +1 -0
- package/dist/src/commands/list.js +48 -0
- package/dist/src/commands/list.js.map +1 -0
- package/dist/src/commands/remove.d.ts +7 -0
- package/dist/src/commands/remove.d.ts.map +1 -0
- package/dist/src/commands/remove.js +34 -0
- package/dist/src/commands/remove.js.map +1 -0
- package/dist/src/config.d.ts +10 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +56 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/notification.d.ts +19 -0
- package/dist/src/notification.d.ts.map +1 -0
- package/dist/src/notification.js +28 -0
- package/dist/src/notification.js.map +1 -0
- package/dist/src/repo-manager.d.ts +32 -0
- package/dist/src/repo-manager.d.ts.map +1 -0
- package/dist/src/repo-manager.js +147 -0
- package/dist/src/repo-manager.js.map +1 -0
- package/dist/src/repo-parser.d.ts +19 -0
- package/dist/src/repo-parser.d.ts.map +1 -0
- package/dist/src/repo-parser.js +116 -0
- package/dist/src/repo-parser.js.map +1 -0
- package/dist/src/types.d.ts +36 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 JosXa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# opencode-ask-github
|
|
2
|
+
|
|
3
|
+
GitHub repository management plugin for OpenCode. Automatically clones repositories and delegates analysis to AI subagents.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Auto-clone**: Repositories are cloned on-demand with shallow clone for speed
|
|
8
|
+
- **AI Analysis**: Delegates to the `explore` subagent for codebase analysis
|
|
9
|
+
- **Aliases**: Configure shortcuts for frequently used repositories
|
|
10
|
+
- **Cache Management**: List and remove cloned repositories
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Add to your OpenCode configuration (`~/.config/opencode/config.json`):
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"plugins": ["D:/projects/opencode-ask-github"]
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
|
|
24
|
+
### `/github-ask <repo> [question]`
|
|
25
|
+
|
|
26
|
+
Clone/locate a repository and analyze it with AI.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
/github-ask sveltejs/svelte how is the component compiler structured?
|
|
30
|
+
/github-ask https://github.com/tailwindlabs/tailwindcss what's the CLI architecture?
|
|
31
|
+
/github-ask sv explain the reactivity system
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Supported input formats:**
|
|
35
|
+
|
|
36
|
+
- GitHub URLs: `https://github.com/owner/repo`
|
|
37
|
+
- owner/repo pairs: `sveltejs/svelte`
|
|
38
|
+
- Aliases: `sv` (if configured)
|
|
39
|
+
|
|
40
|
+
### `/github-list`
|
|
41
|
+
|
|
42
|
+
List all cloned repositories and configured aliases.
|
|
43
|
+
|
|
44
|
+
### `/github-remove <repo>`
|
|
45
|
+
|
|
46
|
+
Remove a cloned repository from the cache.
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
Aliases are stored in `~/.config/opencode/ask-github.json`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"aliases": {
|
|
55
|
+
"sv": "sveltejs/svelte",
|
|
56
|
+
"tw": "tailwindlabs/tailwindcss"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
You can also manage aliases by asking the AI:
|
|
62
|
+
|
|
63
|
+
- "Add a GitHub alias 'react' for facebook/react"
|
|
64
|
+
- "List my GitHub aliases"
|
|
65
|
+
- "Remove the 'sv' alias"
|
|
66
|
+
|
|
67
|
+
## Storage
|
|
68
|
+
|
|
69
|
+
Repositories are cloned to `~/.cache/opencode-github/{owner}/{repo}/`.
|
|
70
|
+
|
|
71
|
+
## AI Tools
|
|
72
|
+
|
|
73
|
+
The plugin provides these tools for the AI to use:
|
|
74
|
+
|
|
75
|
+
| Tool | Description |
|
|
76
|
+
| --------------------- | --------------------------- |
|
|
77
|
+
| `github-alias-add` | Add a repository alias |
|
|
78
|
+
| `github-alias-remove` | Remove an alias |
|
|
79
|
+
| `github-alias-list` | List all aliases |
|
|
80
|
+
| `github-repo-info` | Get info about a repository |
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuBlD,eAAO,MAAM,eAAe,EAAE,MAmI7B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { handleAsk } from "./src/commands/ask";
|
|
3
|
+
import { handleList } from "./src/commands/list";
|
|
4
|
+
import { handleRemove } from "./src/commands/remove";
|
|
5
|
+
import { loadConfig, saveConfig } from "./src/config";
|
|
6
|
+
import { getRepoPath, isCloned, listClonedRepos } from "./src/repo-manager";
|
|
7
|
+
import { parseRepoInput } from "./src/repo-parser";
|
|
8
|
+
/** Marker error to indicate command was handled */
|
|
9
|
+
const COMMAND_HANDLED_MARKER = "__GH_COMMAND_HANDLED__";
|
|
10
|
+
export const AskGithubPlugin = async ({ client, $, directory }) => {
|
|
11
|
+
return {
|
|
12
|
+
config: async (cfg) => {
|
|
13
|
+
cfg.command ??= {};
|
|
14
|
+
cfg.command["gh-ask"] = {
|
|
15
|
+
template: "",
|
|
16
|
+
description: "Clone/locate a GitHub repo and analyze with AI",
|
|
17
|
+
};
|
|
18
|
+
cfg.command["gh-list"] = {
|
|
19
|
+
template: "",
|
|
20
|
+
description: "List cloned GitHub repositories and aliases",
|
|
21
|
+
};
|
|
22
|
+
cfg.command["gh-remove"] = {
|
|
23
|
+
template: "",
|
|
24
|
+
description: "Remove a cloned GitHub repository from cache",
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
"command.execute.before": async (input, output) => {
|
|
28
|
+
const ctx = {
|
|
29
|
+
client,
|
|
30
|
+
$,
|
|
31
|
+
directory,
|
|
32
|
+
sessionId: input.sessionID,
|
|
33
|
+
};
|
|
34
|
+
try {
|
|
35
|
+
if (input.command === "gh-ask") {
|
|
36
|
+
await handleAsk(input.arguments, ctx);
|
|
37
|
+
output.parts.length = 0;
|
|
38
|
+
throw new Error(COMMAND_HANDLED_MARKER);
|
|
39
|
+
}
|
|
40
|
+
if (input.command === "gh-list") {
|
|
41
|
+
await handleList(ctx);
|
|
42
|
+
output.parts.length = 0;
|
|
43
|
+
throw new Error(COMMAND_HANDLED_MARKER);
|
|
44
|
+
}
|
|
45
|
+
if (input.command === "gh-remove") {
|
|
46
|
+
await handleRemove(input.arguments, ctx);
|
|
47
|
+
output.parts.length = 0;
|
|
48
|
+
throw new Error(COMMAND_HANDLED_MARKER);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// Re-throw marker error to signal command was handled
|
|
53
|
+
if (error instanceof Error && error.message === COMMAND_HANDLED_MARKER) {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
// Re-throw other errors
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
tool: {
|
|
61
|
+
"gh-alias-add": tool({
|
|
62
|
+
description: "Add a GitHub repository alias for quick access with /gh-ask",
|
|
63
|
+
args: {
|
|
64
|
+
alias: tool.schema.string("Short name for the alias (e.g., 'sv')"),
|
|
65
|
+
target: tool.schema.string("Repository in owner/repo format (e.g., 'sveltejs/svelte')"),
|
|
66
|
+
},
|
|
67
|
+
async execute(args) {
|
|
68
|
+
const config = loadConfig();
|
|
69
|
+
config.aliases[args.alias] = args.target;
|
|
70
|
+
saveConfig(config);
|
|
71
|
+
return `Added alias: ${args.alias} → ${args.target}`;
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
"gh-alias-remove": tool({
|
|
75
|
+
description: "Remove a GitHub repository alias",
|
|
76
|
+
args: {
|
|
77
|
+
alias: tool.schema.string("The alias to remove"),
|
|
78
|
+
},
|
|
79
|
+
async execute(args) {
|
|
80
|
+
const config = loadConfig();
|
|
81
|
+
if (!config.aliases[args.alias]) {
|
|
82
|
+
return `Alias '${args.alias}' not found`;
|
|
83
|
+
}
|
|
84
|
+
delete config.aliases[args.alias];
|
|
85
|
+
saveConfig(config);
|
|
86
|
+
return `Removed alias: ${args.alias}`;
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
"gh-alias-list": tool({
|
|
90
|
+
description: "List all configured GitHub repository aliases",
|
|
91
|
+
args: {},
|
|
92
|
+
async execute() {
|
|
93
|
+
const config = loadConfig();
|
|
94
|
+
const entries = Object.entries(config.aliases);
|
|
95
|
+
if (entries.length === 0) {
|
|
96
|
+
return "No aliases configured";
|
|
97
|
+
}
|
|
98
|
+
return entries.map(([alias, repo]) => `${alias} → ${repo}`).join("\n");
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
"gh-repo-info": tool({
|
|
102
|
+
description: "Get information about a GitHub repository (whether cloned, local path)",
|
|
103
|
+
args: {
|
|
104
|
+
repo: tool.schema.string("Repository URL, owner/repo, or alias"),
|
|
105
|
+
},
|
|
106
|
+
async execute(args) {
|
|
107
|
+
const config = loadConfig();
|
|
108
|
+
const clonedRepos = listClonedRepos();
|
|
109
|
+
const result = parseRepoInput(args.repo, config.aliases, clonedRepos);
|
|
110
|
+
if (!result.repoInfo) {
|
|
111
|
+
return `Could not parse: ${args.repo}`;
|
|
112
|
+
}
|
|
113
|
+
const info = result.repoInfo;
|
|
114
|
+
const cloned = isCloned(info);
|
|
115
|
+
const path = getRepoPath(info);
|
|
116
|
+
return JSON.stringify({
|
|
117
|
+
owner: info.owner,
|
|
118
|
+
repo: info.repo,
|
|
119
|
+
url: info.url,
|
|
120
|
+
cloned,
|
|
121
|
+
localPath: cloned ? path : null,
|
|
122
|
+
}, null, 2);
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CommandContext } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Handle /github-ask command.
|
|
4
|
+
* Clone repo if needed, then delegate to explore subagent.
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleAsk(args: string, ctx: CommandContext): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=ask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../../src/commands/ask.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CA8Jf"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { getPromptConfig, loadConfig } from "../config";
|
|
2
|
+
import { showToast } from "../notification";
|
|
3
|
+
import { cloneRepo, getRepoPath, isCloned, listClonedRepos, updateRepo, } from "../repo-manager";
|
|
4
|
+
import { formatRepo, parseRepoInput } from "../repo-parser";
|
|
5
|
+
/**
|
|
6
|
+
* Handle /github-ask command.
|
|
7
|
+
* Clone repo if needed, then delegate to explore subagent.
|
|
8
|
+
*/
|
|
9
|
+
export async function handleAsk(args, ctx) {
|
|
10
|
+
const { client, directory, sessionId } = ctx;
|
|
11
|
+
const config = loadConfig();
|
|
12
|
+
// Parse args: first token is repo, rest is question
|
|
13
|
+
const tokens = args.trim().split(/\s+/);
|
|
14
|
+
const repoInput = tokens[0];
|
|
15
|
+
const question = tokens.slice(1).join(" ") || "provide an overview of this repository";
|
|
16
|
+
if (!repoInput) {
|
|
17
|
+
await injectMessage(client, sessionId, directory, `Usage: /github-ask <repo> [question]
|
|
18
|
+
|
|
19
|
+
**repo** can be:
|
|
20
|
+
- GitHub URL: \`https://github.com/owner/repo\`
|
|
21
|
+
- owner/repo: \`sveltejs/svelte\`
|
|
22
|
+
- alias: \`sv\` (if configured)
|
|
23
|
+
|
|
24
|
+
**Configured aliases:**
|
|
25
|
+
${formatAliases(config.aliases)}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Parse the repo input (with cloned repos for substring matching)
|
|
29
|
+
const clonedRepos = listClonedRepos();
|
|
30
|
+
const parseResult = parseRepoInput(repoInput, config.aliases, clonedRepos);
|
|
31
|
+
if (!parseResult.repoInfo) {
|
|
32
|
+
const errorMsg = `Could not parse repository: \`${repoInput}\`
|
|
33
|
+
|
|
34
|
+
Expected formats:
|
|
35
|
+
- \`https://github.com/owner/repo\`
|
|
36
|
+
- \`owner/repo\`
|
|
37
|
+
- Configured alias
|
|
38
|
+
- Substring of a cloned repo name
|
|
39
|
+
|
|
40
|
+
**Configured aliases:**
|
|
41
|
+
${formatAliases(config.aliases)}
|
|
42
|
+
|
|
43
|
+
_Tip: You can add an alias for quick access using the github-alias-add tool._`;
|
|
44
|
+
await injectMessage(client, sessionId, directory, errorMsg);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const repoInfo = parseResult.repoInfo;
|
|
48
|
+
const repoDisplay = formatRepo(repoInfo);
|
|
49
|
+
const localPath = getRepoPath(repoInfo);
|
|
50
|
+
const alreadyCloned = isCloned(repoInfo);
|
|
51
|
+
// Prepare the repo (clone or update)
|
|
52
|
+
if (!alreadyCloned) {
|
|
53
|
+
// Clone flow
|
|
54
|
+
await injectMessage(client, sessionId, directory, `Preparing ${repoDisplay}...`, true);
|
|
55
|
+
await showToast(ctx, {
|
|
56
|
+
message: `Cloning ${repoDisplay}...`,
|
|
57
|
+
variant: "info",
|
|
58
|
+
});
|
|
59
|
+
const result = await cloneRepo(repoInfo);
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
await showToast(ctx, {
|
|
62
|
+
title: "Clone failed",
|
|
63
|
+
message: result.error ?? "Unknown error",
|
|
64
|
+
variant: "error",
|
|
65
|
+
});
|
|
66
|
+
await injectMessage(client, sessionId, directory, `Failed to clone ${repoDisplay}:\n\`\`\`\n${result.error}\n\`\`\``);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await showToast(ctx, {
|
|
70
|
+
title: "Cloned",
|
|
71
|
+
message: repoDisplay,
|
|
72
|
+
variant: "success",
|
|
73
|
+
duration: 3000,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Update flow
|
|
78
|
+
await injectMessage(client, sessionId, directory, `Preparing ${repoDisplay}...`, true);
|
|
79
|
+
await showToast(ctx, {
|
|
80
|
+
message: `Updating ${repoDisplay}...`,
|
|
81
|
+
variant: "info",
|
|
82
|
+
});
|
|
83
|
+
const result = await updateRepo(repoInfo);
|
|
84
|
+
if (!result.success) {
|
|
85
|
+
await showToast(ctx, {
|
|
86
|
+
title: "Update failed",
|
|
87
|
+
message: result.error ?? "Unknown error",
|
|
88
|
+
variant: "error",
|
|
89
|
+
});
|
|
90
|
+
await injectMessage(client, sessionId, directory, `Failed to update ${repoDisplay}:\n\`\`\`\n${result.error}\n\`\`\``);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await showToast(ctx, {
|
|
94
|
+
title: "Updated",
|
|
95
|
+
message: repoDisplay,
|
|
96
|
+
variant: "success",
|
|
97
|
+
duration: 3000,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// Delegate to explore subagent
|
|
101
|
+
const promptConfig = getPromptConfig(config);
|
|
102
|
+
const instruction = promptConfig.delegateInstruction.replace("{path}", localPath);
|
|
103
|
+
const explorePrompt = promptConfig.exploreTemplate.replace("{question}", question);
|
|
104
|
+
await client.session.prompt({
|
|
105
|
+
path: { id: sessionId },
|
|
106
|
+
body: {
|
|
107
|
+
parts: [
|
|
108
|
+
{
|
|
109
|
+
type: "text",
|
|
110
|
+
text: `${instruction}
|
|
111
|
+
---
|
|
112
|
+
${explorePrompt}
|
|
113
|
+
---`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
query: { directory },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function formatAliases(aliases) {
|
|
121
|
+
const entries = Object.entries(aliases);
|
|
122
|
+
if (entries.length === 0) {
|
|
123
|
+
return "_No aliases configured_";
|
|
124
|
+
}
|
|
125
|
+
return entries
|
|
126
|
+
.map(([alias, repo]) => `- \`${alias}\` → \`${repo}\``)
|
|
127
|
+
.join("\n");
|
|
128
|
+
}
|
|
129
|
+
async function injectMessage(client, sessionId, directory, text, noReply = false) {
|
|
130
|
+
await client.session.prompt({
|
|
131
|
+
path: { id: sessionId },
|
|
132
|
+
body: {
|
|
133
|
+
noReply,
|
|
134
|
+
parts: [{ type: "text", text }],
|
|
135
|
+
},
|
|
136
|
+
query: { directory },
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.js","sourceRoot":"","sources":["../../../src/commands/ask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/list.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C;;;GAGG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BnE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { loadConfig } from "../config";
|
|
2
|
+
import { sendIgnoredMessage } from "../notification";
|
|
3
|
+
import { listClonedRepos } from "../repo-manager";
|
|
4
|
+
/**
|
|
5
|
+
* Handle /github-list command.
|
|
6
|
+
* List all cloned repos and configured aliases.
|
|
7
|
+
*/
|
|
8
|
+
export async function handleList(ctx) {
|
|
9
|
+
const config = loadConfig();
|
|
10
|
+
const repos = listClonedRepos();
|
|
11
|
+
const lines = ["**Cloned repositories:**"];
|
|
12
|
+
if (repos.length === 0) {
|
|
13
|
+
lines.push("_No repositories cloned yet_");
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
for (const repo of repos) {
|
|
17
|
+
const age = formatAge(repo.lastModified);
|
|
18
|
+
lines.push(`- \`${repo.owner}/${repo.repo}\` (${age})`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push("**Configured aliases:**");
|
|
23
|
+
const aliases = Object.entries(config.aliases);
|
|
24
|
+
if (aliases.length === 0) {
|
|
25
|
+
lines.push("_No aliases configured_");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
for (const [alias, target] of aliases) {
|
|
29
|
+
lines.push(`- \`${alias}\` → \`${target}\``);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
await sendIgnoredMessage(ctx, lines.join("\n"));
|
|
33
|
+
}
|
|
34
|
+
function formatAge(date) {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const diff = now - date.getTime();
|
|
37
|
+
const minutes = Math.floor(diff / 60000);
|
|
38
|
+
const hours = Math.floor(minutes / 60);
|
|
39
|
+
const days = Math.floor(hours / 24);
|
|
40
|
+
if (days > 0)
|
|
41
|
+
return `${days} day${days > 1 ? "s" : ""} ago`;
|
|
42
|
+
if (hours > 0)
|
|
43
|
+
return `${hours} hour${hours > 1 ? "s" : ""} ago`;
|
|
44
|
+
if (minutes > 0)
|
|
45
|
+
return `${minutes} min${minutes > 1 ? "s" : ""} ago`;
|
|
46
|
+
return "just now";
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAmB;IAClD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAEhC,MAAM,KAAK,GAAa,CAAC,0BAA0B,CAAC,CAAC;IAErD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC7C,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,OAAO,GAAG,GAAG,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAEtC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,kBAAkB,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,SAAS,CAAC,IAAU;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAClC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAEpC,IAAI,IAAI,GAAG,CAAC;QAAE,OAAO,GAAG,IAAI,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IAC7D,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,GAAG,KAAK,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACjE,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,GAAG,OAAO,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACtE,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CommandContext } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Handle /github-remove command.
|
|
4
|
+
* Remove a cloned repository from the cache.
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleRemove(args: string, ctx: CommandContext): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=remove.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/commands/remove.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCnF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { loadConfig } from "../config";
|
|
2
|
+
import { sendIgnoredMessage } from "../notification";
|
|
3
|
+
import { isCloned, listClonedRepos, removeRepo } from "../repo-manager";
|
|
4
|
+
import { formatRepo, parseRepoInput } from "../repo-parser";
|
|
5
|
+
/**
|
|
6
|
+
* Handle /github-remove command.
|
|
7
|
+
* Remove a cloned repository from the cache.
|
|
8
|
+
*/
|
|
9
|
+
export async function handleRemove(args, ctx) {
|
|
10
|
+
const config = loadConfig();
|
|
11
|
+
const repoInput = args.trim();
|
|
12
|
+
if (!repoInput) {
|
|
13
|
+
await sendIgnoredMessage(ctx, "Usage: `/gh-remove <repo>`");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const clonedRepos = listClonedRepos();
|
|
17
|
+
const result = parseRepoInput(repoInput, config.aliases, clonedRepos);
|
|
18
|
+
if (!result.repoInfo) {
|
|
19
|
+
await sendIgnoredMessage(ctx, `Could not parse repository: \`${repoInput}\``);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const repoInfo = result.repoInfo;
|
|
23
|
+
const repoDisplay = formatRepo(repoInfo);
|
|
24
|
+
if (!isCloned(repoInfo)) {
|
|
25
|
+
await sendIgnoredMessage(ctx, `Repository \`${repoDisplay}\` is not cloned.`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const removed = removeRepo(repoInfo);
|
|
29
|
+
const message = removed
|
|
30
|
+
? `Removed \`${repoDisplay}\` from cache.`
|
|
31
|
+
: `Failed to remove \`${repoDisplay}\`.`;
|
|
32
|
+
await sendIgnoredMessage(ctx, message);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=remove.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.js","sourceRoot":"","sources":["../../../src/commands/remove.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG5D;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,GAAmB;IAClE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAE9B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,kBAAkB,CAAC,GAAG,EAAE,4BAA4B,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IAED,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAEtE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,kBAAkB,CAAC,GAAG,EAAE,iCAAiC,SAAS,IAAI,CAAC,CAAC;QAC9E,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,MAAM,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,WAAW,mBAAmB,CAAC,CAAC;QAC9E,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAErC,MAAM,OAAO,GAAG,OAAO;QACrB,CAAC,CAAC,aAAa,WAAW,gBAAgB;QAC1C,CAAC,CAAC,sBAAsB,WAAW,KAAK,CAAC;IAE3C,MAAM,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Config, PromptConfig } from "./types";
|
|
2
|
+
export declare function getConfigPath(): string;
|
|
3
|
+
export declare function getCacheDir(): string;
|
|
4
|
+
export declare function getConfigDir(): string;
|
|
5
|
+
export declare function getDefaultPrompt(): PromptConfig;
|
|
6
|
+
export declare function loadConfig(): Config;
|
|
7
|
+
export declare function getPromptConfig(config: Config): PromptConfig;
|
|
8
|
+
export declare function saveConfig(config: Config): void;
|
|
9
|
+
export declare function ensureCacheDir(): void;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAWpD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,gBAAgB,IAAI,YAAY,CAE/C;AAQD,wBAAgB,UAAU,IAAI,MAAM,CAWnC;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAO5D;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAK/C;AAED,wBAAgB,cAAc,IAAI,IAAI,CAIrC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const CONFIG_DIR = join(homedir(), ".config", "opencode");
|
|
5
|
+
const CONFIG_FILE = join(CONFIG_DIR, "ask-github.json");
|
|
6
|
+
const CACHE_DIR = join(homedir(), ".cache", "opencode-github");
|
|
7
|
+
const DEFAULT_PROMPT = {
|
|
8
|
+
delegateInstruction: "Use a subagent to explore `{path}` and answer:",
|
|
9
|
+
exploreTemplate: "{question}",
|
|
10
|
+
};
|
|
11
|
+
export function getConfigPath() {
|
|
12
|
+
return CONFIG_FILE;
|
|
13
|
+
}
|
|
14
|
+
export function getCacheDir() {
|
|
15
|
+
return CACHE_DIR;
|
|
16
|
+
}
|
|
17
|
+
export function getConfigDir() {
|
|
18
|
+
return CONFIG_DIR;
|
|
19
|
+
}
|
|
20
|
+
export function getDefaultPrompt() {
|
|
21
|
+
return { ...DEFAULT_PROMPT };
|
|
22
|
+
}
|
|
23
|
+
function defaultConfig() {
|
|
24
|
+
return {
|
|
25
|
+
aliases: {},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function loadConfig() {
|
|
29
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
30
|
+
return defaultConfig();
|
|
31
|
+
}
|
|
32
|
+
const content = readFileSync(CONFIG_FILE, "utf-8");
|
|
33
|
+
const parsed = JSON.parse(content);
|
|
34
|
+
return {
|
|
35
|
+
aliases: parsed.aliases ?? {},
|
|
36
|
+
prompt: parsed.prompt,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function getPromptConfig(config) {
|
|
40
|
+
return {
|
|
41
|
+
delegateInstruction: config.prompt?.delegateInstruction ?? DEFAULT_PROMPT.delegateInstruction,
|
|
42
|
+
exploreTemplate: config.prompt?.exploreTemplate ?? DEFAULT_PROMPT.exploreTemplate,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function saveConfig(config) {
|
|
46
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
47
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
50
|
+
}
|
|
51
|
+
export function ensureCacheDir() {
|
|
52
|
+
if (!existsSync(CACHE_DIR)) {
|
|
53
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;AACxD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAE/D,MAAM,cAAc,GAAiB;IACnC,mBAAmB,EAAE,gDAAgD;IACrE,eAAe,EAAE,YAAY;CAC9B,CAAC;AAEF,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,aAAa;IACpB,OAAO;QACL,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,aAAa,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE;QAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO;QACL,mBAAmB,EACjB,MAAM,CAAC,MAAM,EAAE,mBAAmB,IAAI,cAAc,CAAC,mBAAmB;QAC1E,eAAe,EACb,MAAM,CAAC,MAAM,EAAE,eAAe,IAAI,cAAc,CAAC,eAAe;KACnE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CommandContext } from "./types";
|
|
2
|
+
type ToastVariant = "info" | "success" | "warning" | "error";
|
|
3
|
+
interface ToastOptions {
|
|
4
|
+
title?: string;
|
|
5
|
+
message: string;
|
|
6
|
+
variant?: ToastVariant;
|
|
7
|
+
duration?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Show a toast notification in the TUI.
|
|
11
|
+
*/
|
|
12
|
+
export declare function showToast(ctx: CommandContext, options: ToastOptions): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Sends a message that will be displayed but ignored by the AI.
|
|
15
|
+
* Used for command output that shouldn't trigger AI responses.
|
|
16
|
+
*/
|
|
17
|
+
export declare function sendIgnoredMessage(ctx: CommandContext, text: string): Promise<void>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=notification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../../src/notification.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,KAAK,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE7D,UAAU,YAAY;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CASzF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASzF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Show a toast notification in the TUI.
|
|
3
|
+
*/
|
|
4
|
+
export async function showToast(ctx, options) {
|
|
5
|
+
await ctx.client.tui.showToast({
|
|
6
|
+
body: {
|
|
7
|
+
title: options.title,
|
|
8
|
+
message: options.message,
|
|
9
|
+
variant: options.variant ?? "info",
|
|
10
|
+
duration: options.duration,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Sends a message that will be displayed but ignored by the AI.
|
|
16
|
+
* Used for command output that shouldn't trigger AI responses.
|
|
17
|
+
*/
|
|
18
|
+
export async function sendIgnoredMessage(ctx, text) {
|
|
19
|
+
await ctx.client.session.prompt({
|
|
20
|
+
path: { id: ctx.sessionId },
|
|
21
|
+
body: {
|
|
22
|
+
noReply: true,
|
|
23
|
+
parts: [{ type: "text", text, ignored: true }],
|
|
24
|
+
},
|
|
25
|
+
query: { directory: ctx.directory },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=notification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.js","sourceRoot":"","sources":["../../src/notification.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAmB,EAAE,OAAqB;IACxE,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QAC7B,IAAI,EAAE;YACJ,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,MAAM;YAClC,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAmB,EAAE,IAAY;IACxE,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE;QAC3B,IAAI,EAAE;YACJ,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;SAC/C;QACD,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE;KACpC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ClonedRepo, RepoInfo } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Get the local path for a repository.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getRepoPath(info: RepoInfo): string;
|
|
6
|
+
/**
|
|
7
|
+
* Check if a repository is already cloned.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isCloned(info: RepoInfo): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Clone a repository using shallow clone for speed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function cloneRepo(info: RepoInfo): Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Update an existing repository by fetching and resetting to the default branch.
|
|
19
|
+
*/
|
|
20
|
+
export declare function updateRepo(info: RepoInfo): Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Remove a cloned repository.
|
|
26
|
+
*/
|
|
27
|
+
export declare function removeRepo(info: RepoInfo): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* List all cloned repositories.
|
|
30
|
+
*/
|
|
31
|
+
export declare function listClonedRepos(): ClonedRepo[];
|
|
32
|
+
//# 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":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEpD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAElD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAGhD;AA4BD;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB7F;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoD9F;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CASlD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,UAAU,EAAE,CAmC9C"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { ensureCacheDir, getCacheDir } from "./config";
|
|
4
|
+
/**
|
|
5
|
+
* Get the local path for a repository.
|
|
6
|
+
*/
|
|
7
|
+
export function getRepoPath(info) {
|
|
8
|
+
return join(getCacheDir(), info.owner, info.repo);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check if a repository is already cloned.
|
|
12
|
+
*/
|
|
13
|
+
export function isCloned(info) {
|
|
14
|
+
const path = getRepoPath(info);
|
|
15
|
+
return existsSync(join(path, ".git"));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Run a git command with stdout/stderr suppressed.
|
|
19
|
+
* Returns { success, error } result object.
|
|
20
|
+
*/
|
|
21
|
+
async function runGitCommand(args) {
|
|
22
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
23
|
+
stdout: "pipe",
|
|
24
|
+
stderr: "pipe",
|
|
25
|
+
});
|
|
26
|
+
const [stdout, stderr] = await Promise.all([
|
|
27
|
+
new Response(proc.stdout).text(),
|
|
28
|
+
new Response(proc.stderr).text(),
|
|
29
|
+
]);
|
|
30
|
+
const exitCode = await proc.exited;
|
|
31
|
+
return {
|
|
32
|
+
success: exitCode === 0,
|
|
33
|
+
stdout,
|
|
34
|
+
stderr,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Clone a repository using shallow clone for speed.
|
|
39
|
+
*/
|
|
40
|
+
export async function cloneRepo(info) {
|
|
41
|
+
ensureCacheDir();
|
|
42
|
+
const path = getRepoPath(info);
|
|
43
|
+
const ownerDir = join(getCacheDir(), info.owner);
|
|
44
|
+
// Ensure owner directory exists
|
|
45
|
+
if (!existsSync(ownerDir)) {
|
|
46
|
+
mkdirSync(ownerDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
const result = await runGitCommand(["clone", "--depth", "1", `${info.url}.git`, path]);
|
|
49
|
+
if (!result.success) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: result.stderr,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { success: true };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Update an existing repository by fetching and resetting to the default branch.
|
|
59
|
+
*/
|
|
60
|
+
export async function updateRepo(info) {
|
|
61
|
+
const path = getRepoPath(info);
|
|
62
|
+
// Fetch latest from origin
|
|
63
|
+
const fetchResult = await runGitCommand(["-C", path, "fetch", "--depth", "1", "origin"]);
|
|
64
|
+
if (!fetchResult.success) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: fetchResult.stderr,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Get the default branch name from remote HEAD
|
|
71
|
+
const defaultBranchResult = await runGitCommand([
|
|
72
|
+
"-C",
|
|
73
|
+
path,
|
|
74
|
+
"symbolic-ref",
|
|
75
|
+
"refs/remotes/origin/HEAD",
|
|
76
|
+
"--short",
|
|
77
|
+
]);
|
|
78
|
+
if (!defaultBranchResult.success) {
|
|
79
|
+
// Fallback: try to reset to origin/main or origin/master
|
|
80
|
+
const resetMain = await runGitCommand(["-C", path, "reset", "--hard", "origin/main"]);
|
|
81
|
+
if (resetMain.success) {
|
|
82
|
+
return { success: true };
|
|
83
|
+
}
|
|
84
|
+
const resetMaster = await runGitCommand(["-C", path, "reset", "--hard", "origin/master"]);
|
|
85
|
+
if (resetMaster.success) {
|
|
86
|
+
return { success: true };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: "Could not determine default branch",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Reset to the default branch (e.g., "origin/main" -> reset to that)
|
|
94
|
+
const defaultBranch = defaultBranchResult.stdout.trim();
|
|
95
|
+
const resetResult = await runGitCommand(["-C", path, "reset", "--hard", defaultBranch]);
|
|
96
|
+
if (!resetResult.success) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: resetResult.stderr,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return { success: true };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Remove a cloned repository.
|
|
106
|
+
*/
|
|
107
|
+
export function removeRepo(info) {
|
|
108
|
+
const path = getRepoPath(info);
|
|
109
|
+
if (!existsSync(path)) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
rmSync(path, { recursive: true, force: true });
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* List all cloned repositories.
|
|
117
|
+
*/
|
|
118
|
+
export function listClonedRepos() {
|
|
119
|
+
const cacheDir = getCacheDir();
|
|
120
|
+
const repos = [];
|
|
121
|
+
if (!existsSync(cacheDir)) {
|
|
122
|
+
return repos;
|
|
123
|
+
}
|
|
124
|
+
const owners = readdirSync(cacheDir);
|
|
125
|
+
for (const owner of owners) {
|
|
126
|
+
const ownerPath = join(cacheDir, owner);
|
|
127
|
+
const stat = statSync(ownerPath);
|
|
128
|
+
if (!stat.isDirectory())
|
|
129
|
+
continue;
|
|
130
|
+
const repoNames = readdirSync(ownerPath);
|
|
131
|
+
for (const repo of repoNames) {
|
|
132
|
+
const repoPath = join(ownerPath, repo);
|
|
133
|
+
const gitPath = join(repoPath, ".git");
|
|
134
|
+
if (existsSync(gitPath)) {
|
|
135
|
+
const repoStat = statSync(repoPath);
|
|
136
|
+
repos.push({
|
|
137
|
+
owner,
|
|
138
|
+
repo,
|
|
139
|
+
path: repoPath,
|
|
140
|
+
lastModified: repoStat.mtime,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return repos.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
146
|
+
}
|
|
147
|
+
//# 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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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AliasMap, ClonedRepo, RepoInfo } from "./types.ts";
|
|
2
|
+
export interface ParseResult {
|
|
3
|
+
repoInfo: RepoInfo | null;
|
|
4
|
+
matchedAlias?: {
|
|
5
|
+
alias: string;
|
|
6
|
+
target: string;
|
|
7
|
+
};
|
|
8
|
+
matchedClonedRepo?: ClonedRepo;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse various input formats into RepoInfo.
|
|
12
|
+
* Supports: GitHub URLs, owner/repo pairs, aliases, and substring matches against cloned repos.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseRepoInput(input: string, aliases: AliasMap, clonedRepos?: ClonedRepo[]): ParseResult;
|
|
15
|
+
/**
|
|
16
|
+
* Format a RepoInfo as owner/repo string.
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatRepo(info: RepoInfo): string;
|
|
19
|
+
//# sourceMappingURL=repo-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-parser.d.ts","sourceRoot":"","sources":["../../src/repo-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAiBjE,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,iBAAiB,CAAC,EAAE,UAAU,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,EACjB,WAAW,GAAE,UAAU,EAAO,GAC7B,WAAW,CAmFb;AAyBD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEjD"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const GITHUB_URL_REGEX = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)/;
|
|
2
|
+
const OWNER_REPO_REGEX = /^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/;
|
|
3
|
+
/**
|
|
4
|
+
* Normalize a GitHub URL by stripping trailing .git, /tree/..., /blob/..., etc.
|
|
5
|
+
*/
|
|
6
|
+
function _normalizeUrl(url) {
|
|
7
|
+
return url
|
|
8
|
+
.replace(/\.git$/, "")
|
|
9
|
+
.replace(/\/(tree|blob)\/[^/]+.*$/, "")
|
|
10
|
+
.replace(/\/$/, "");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse various input formats into RepoInfo.
|
|
14
|
+
* Supports: GitHub URLs, owner/repo pairs, aliases, and substring matches against cloned repos.
|
|
15
|
+
*/
|
|
16
|
+
export function parseRepoInput(input, aliases, clonedRepos = []) {
|
|
17
|
+
const trimmed = input.trim().toLowerCase();
|
|
18
|
+
// Check if it's an exact alias match
|
|
19
|
+
const exactAlias = Object.keys(aliases).find((a) => a.toLowerCase() === trimmed);
|
|
20
|
+
if (exactAlias) {
|
|
21
|
+
const target = aliases[exactAlias];
|
|
22
|
+
const result = parseRepoInputSimple(target);
|
|
23
|
+
return {
|
|
24
|
+
repoInfo: result,
|
|
25
|
+
matchedAlias: { alias: exactAlias, target },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Try GitHub URL format
|
|
29
|
+
const urlMatch = input.trim().match(GITHUB_URL_REGEX);
|
|
30
|
+
if (urlMatch) {
|
|
31
|
+
const owner = urlMatch[1];
|
|
32
|
+
const repo = urlMatch[2].replace(/\.git$/, "");
|
|
33
|
+
return {
|
|
34
|
+
repoInfo: {
|
|
35
|
+
owner,
|
|
36
|
+
repo,
|
|
37
|
+
url: `https://github.com/${owner}/${repo}`,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Try owner/repo format
|
|
42
|
+
const ownerRepoMatch = input.trim().match(OWNER_REPO_REGEX);
|
|
43
|
+
if (ownerRepoMatch) {
|
|
44
|
+
const owner = ownerRepoMatch[1];
|
|
45
|
+
const repo = ownerRepoMatch[2];
|
|
46
|
+
return {
|
|
47
|
+
repoInfo: {
|
|
48
|
+
owner,
|
|
49
|
+
repo,
|
|
50
|
+
url: `https://github.com/${owner}/${repo}`,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Only do substring matching if there's no slash in the input
|
|
55
|
+
// (slash indicates explicit owner/repo format that didn't match above)
|
|
56
|
+
const hasSlash = input.includes("/");
|
|
57
|
+
if (!hasSlash) {
|
|
58
|
+
// Try substring match against cloned repos
|
|
59
|
+
const matchingRepos = clonedRepos.filter((r) => {
|
|
60
|
+
const repoLower = r.repo.toLowerCase();
|
|
61
|
+
return repoLower.includes(trimmed);
|
|
62
|
+
});
|
|
63
|
+
if (matchingRepos.length === 1) {
|
|
64
|
+
const match = matchingRepos[0];
|
|
65
|
+
return {
|
|
66
|
+
repoInfo: {
|
|
67
|
+
owner: match.owner,
|
|
68
|
+
repo: match.repo,
|
|
69
|
+
url: `https://github.com/${match.owner}/${match.repo}`,
|
|
70
|
+
},
|
|
71
|
+
matchedClonedRepo: match,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Try substring match against aliases
|
|
75
|
+
const matchingAliases = Object.entries(aliases).filter(([alias, target]) => {
|
|
76
|
+
const aliasLower = alias.toLowerCase();
|
|
77
|
+
const repoName = target.split("/")[1]?.toLowerCase() || "";
|
|
78
|
+
return aliasLower.includes(trimmed) || repoName.includes(trimmed);
|
|
79
|
+
});
|
|
80
|
+
if (matchingAliases.length === 1) {
|
|
81
|
+
const [alias, target] = matchingAliases[0];
|
|
82
|
+
const result = parseRepoInputSimple(target);
|
|
83
|
+
return {
|
|
84
|
+
repoInfo: result,
|
|
85
|
+
matchedAlias: { alias, target },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { repoInfo: null };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Simple parse without suggestions (used internally for alias targets).
|
|
93
|
+
*/
|
|
94
|
+
function parseRepoInputSimple(input) {
|
|
95
|
+
const trimmed = input.trim();
|
|
96
|
+
const urlMatch = trimmed.match(GITHUB_URL_REGEX);
|
|
97
|
+
if (urlMatch) {
|
|
98
|
+
const owner = urlMatch[1];
|
|
99
|
+
const repo = urlMatch[2].replace(/\.git$/, "");
|
|
100
|
+
return { owner, repo, url: `https://github.com/${owner}/${repo}` };
|
|
101
|
+
}
|
|
102
|
+
const ownerRepoMatch = trimmed.match(OWNER_REPO_REGEX);
|
|
103
|
+
if (ownerRepoMatch) {
|
|
104
|
+
const owner = ownerRepoMatch[1];
|
|
105
|
+
const repo = ownerRepoMatch[2];
|
|
106
|
+
return { owner, repo, url: `https://github.com/${owner}/${repo}` };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Format a RepoInfo as owner/repo string.
|
|
112
|
+
*/
|
|
113
|
+
export function formatRepo(info) {
|
|
114
|
+
return `${info.owner}/${info.repo}`;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=repo-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo-parser.js","sourceRoot":"","sources":["../../src/repo-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
export type AliasMap = Record<string, string>;
|
|
3
|
+
export interface PromptConfig {
|
|
4
|
+
/**
|
|
5
|
+
* The instruction that tells the AI how to delegate. Use {path} placeholder.
|
|
6
|
+
* Default: "Delegate to an @explore subagent to answer the following question about the repository at `{path}`:"
|
|
7
|
+
*/
|
|
8
|
+
delegateInstruction: string;
|
|
9
|
+
/**
|
|
10
|
+
* Template for the explore prompt. Use {question} placeholder.
|
|
11
|
+
* Default: "{question}"
|
|
12
|
+
*/
|
|
13
|
+
exploreTemplate: string;
|
|
14
|
+
}
|
|
15
|
+
export interface Config {
|
|
16
|
+
aliases: AliasMap;
|
|
17
|
+
prompt?: Partial<PromptConfig>;
|
|
18
|
+
}
|
|
19
|
+
export interface RepoInfo {
|
|
20
|
+
owner: string;
|
|
21
|
+
repo: string;
|
|
22
|
+
url: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ClonedRepo {
|
|
25
|
+
owner: string;
|
|
26
|
+
repo: string;
|
|
27
|
+
path: string;
|
|
28
|
+
lastModified: Date;
|
|
29
|
+
}
|
|
30
|
+
export interface CommandContext {
|
|
31
|
+
client: PluginInput["client"];
|
|
32
|
+
$: PluginInput["$"];
|
|
33
|
+
directory: string;
|
|
34
|
+
sessionId: string;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,MAAM;IACrB,OAAO,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-ask-github",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "GitHub repository management plugin for OpenCode - auto-clone repos and analyze with AI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/JosXa/opencode-ask-github.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/JosXa/opencode-ask-github/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/JosXa/opencode-ask-github#readme",
|
|
16
|
+
"author": "JosXa",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"keywords": [
|
|
19
|
+
"opencode",
|
|
20
|
+
"opencode-plugin",
|
|
21
|
+
"github",
|
|
22
|
+
"ai",
|
|
23
|
+
"code-analysis",
|
|
24
|
+
"repository"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"bun": ">=1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.build.json",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"format:check": "biome check .",
|
|
36
|
+
"format:fix": "biome check --write ."
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@biomejs/biome": "^2.0.0",
|
|
43
|
+
"@types/bun": "^1.2.0",
|
|
44
|
+
"typescript": "^5.8.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@opencode-ai/plugin": ">=1.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|