@andre-barbosa/opencode-commit 0.1.1 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts DELETED
@@ -1,136 +0,0 @@
1
- import type { Plugin } from "@opencode-ai/plugin";
2
- import type { Part } from "@opencode-ai/sdk";
3
- import { gatherGitContext, isGitRepo } from "./git.js";
4
- import { fetchAgent, generateCommitMessage } from "./generate.js";
5
- import { resolveConfig, type PluginOptions } from "./types.js";
6
-
7
- const COMMAND_NAME = "commit";
8
- const SKIP_ERROR = "skip";
9
-
10
- function textPart(text: string): Part {
11
- return { type: "text", text } as Part;
12
- }
13
-
14
- function formatOutput(input: {
15
- message: string;
16
- modelLabel: string;
17
- childSessionID: string;
18
- }): string {
19
- return [
20
- "## Suggested commit message",
21
- "",
22
- "```",
23
- input.message,
24
- "```",
25
- "",
26
- `Model: \`${input.modelLabel}\` · Child session: \`${input.childSessionID}\``,
27
- "",
28
- "Copy the message above and run `git commit` when ready.",
29
- ].join("\n");
30
- }
31
-
32
- const skipCommand = (): never => {
33
- throw new Error(SKIP_ERROR);
34
- };
35
-
36
- export const OpenCodeCommitPlugin: Plugin = async (
37
- { client, $, worktree },
38
- options?: PluginOptions,
39
- ) => {
40
- const config = resolveConfig(options);
41
-
42
- const log = async (
43
- level: "info" | "warn" | "error",
44
- message: string,
45
- extra?: Record<string, unknown>,
46
- ) => {
47
- await client.app
48
- .log({
49
- body: {
50
- service: "opencode-commit",
51
- level,
52
- message,
53
- extra,
54
- },
55
- })
56
- .catch(() => {});
57
- };
58
-
59
- return {
60
- "command.execute.before": async (input, output) => {
61
- if (input.command !== COMMAND_NAME) return;
62
-
63
- try {
64
- if (!(await isGitRepo($, worktree))) {
65
- output.parts = [
66
- textPart(
67
- "Not a git repository. Run `/commit` from inside a git worktree.",
68
- ),
69
- ];
70
- return skipCommand();
71
- }
72
-
73
- const context = await gatherGitContext($, worktree, config.maxDiffChars);
74
-
75
- if (!context.hasStagedChanges) {
76
- output.parts = [
77
- textPart(
78
- "No staged changes found. Stage files with `git add` and run `/commit` again.",
79
- ),
80
- ];
81
- return skipCommand();
82
- }
83
-
84
- const resolvedAgent = await fetchAgent(client, config.agent);
85
- if (!resolvedAgent) {
86
- output.parts = [
87
- textPart(
88
- [
89
- `Sub-agent \`${config.agent}\` is not configured.`,
90
- "",
91
- "Copy `agents/commit-writer.md` from this plugin into `.opencode/agents/commit-writer.md`,",
92
- "or add the agent to your `opencode.json`.",
93
- ].join("\n"),
94
- ),
95
- ];
96
- return skipCommand();
97
- }
98
-
99
- await log("info", "Generating commit message", {
100
- agent: config.agent,
101
- branch: context.branch,
102
- sessionID: input.sessionID,
103
- });
104
-
105
- const userHint = input.arguments?.trim() || undefined;
106
- const result = await generateCommitMessage({
107
- client,
108
- parentSessionID: input.sessionID,
109
- agentName: config.agent,
110
- agent: resolvedAgent,
111
- context,
112
- userHint,
113
- });
114
-
115
- output.parts = [textPart(formatOutput(result))];
116
- return skipCommand();
117
- } catch (error) {
118
- if (error instanceof Error && error.message === SKIP_ERROR) {
119
- throw error;
120
- }
121
-
122
- await log("error", "Commit message generation failed", {
123
- error: String(error),
124
- sessionID: input.sessionID,
125
- });
126
-
127
- output.parts = [
128
- textPart(`Failed to generate commit message: ${String(error)}`),
129
- ];
130
- return skipCommand();
131
- }
132
- },
133
- };
134
- };
135
-
136
- export default OpenCodeCommitPlugin;
package/src/prompt.ts DELETED
@@ -1,51 +0,0 @@
1
- import type { GitContext } from "./types.js";
2
-
3
- export function buildCommitPrompt(context: GitContext, userHint?: string): string {
4
- const recentCommitsBlock =
5
- context.recentCommits.length > 0
6
- ? context.recentCommits.map((subject) => `- ${subject}`).join("\n")
7
- : "(none)";
8
-
9
- const hintBlock =
10
- userHint && userHint.trim().length > 0
11
- ? `\nAdditional instruction from the user:\n${userHint.trim()}\n`
12
- : "";
13
-
14
- return `Write a git commit message for the staged changes below.
15
-
16
- Format rules (Conventional Commits — required):
17
- - Subject line MUST use type(scope): description or type: description
18
- - Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
19
- - Scope is optional but preferred when changes are localized (e.g. feat(auth):, fix(api):)
20
- - Description: imperative mood, lowercase, no trailing period, max 72 chars for the full subject
21
- - Optional body: blank line after subject, then bullet points; keep lines <= 72 chars
22
- - Breaking changes: use feat!: or fix!: prefix, or add a BREAKING CHANGE: footer
23
- - Do NOT use free-form subjects like "Update files", "WIP", or "Misc changes"
24
- - Output ONLY the commit message text — no code fences, no commentary, no tool calls
25
-
26
- Examples:
27
- feat(auth): add oauth login flow
28
-
29
- - add google and github providers
30
- - store refresh tokens in secure storage
31
-
32
- fix(api): handle null response from user endpoint
33
- ${hintBlock}
34
- Branch: ${context.branch}
35
-
36
- Recent commit subjects (align scope naming when sensible):
37
- ${recentCommitsBlock}
38
-
39
- Staged changes (stat):
40
- ${context.stat || "(empty)"}
41
-
42
- Staged diff:
43
- ${context.diff || "(empty)"}
44
- `;
45
- }
46
-
47
- export function stripCodeFences(text: string): string {
48
- const trimmed = text.trim();
49
- const fenced = trimmed.match(/^```(?:\w*\n)?([\s\S]*?)```$/);
50
- return (fenced?.[1] ?? trimmed).trim();
51
- }
package/src/types.ts DELETED
@@ -1,40 +0,0 @@
1
- export type ModelRef = {
2
- providerID: string;
3
- modelID: string;
4
- };
5
-
6
- export type PluginOptions = {
7
- agent?: string;
8
- maxDiffChars?: number;
9
- };
10
-
11
- export type ResolvedPluginConfig = {
12
- agent: string;
13
- maxDiffChars: number;
14
- };
15
-
16
- export const DEFAULT_AGENT = "commit-writer";
17
- export const DEFAULT_MAX_DIFF_CHARS = 12_000;
18
-
19
- export function resolveConfig(options?: PluginOptions): ResolvedPluginConfig {
20
- return {
21
- agent: options?.agent?.trim() || DEFAULT_AGENT,
22
- maxDiffChars:
23
- typeof options?.maxDiffChars === "number" && options.maxDiffChars > 0
24
- ? Math.floor(options.maxDiffChars)
25
- : DEFAULT_MAX_DIFF_CHARS,
26
- };
27
- }
28
-
29
- export type GitContext = {
30
- branch: string;
31
- stat: string;
32
- diff: string;
33
- recentCommits: string[];
34
- hasStagedChanges: boolean;
35
- };
36
-
37
- export type AgentInfo = {
38
- name: string;
39
- model?: ModelRef;
40
- };