@wrongstack/plugins 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/dist/auto-doc.d.ts +13 -0
- package/dist/auto-doc.js +239 -0
- package/dist/cost-tracker.d.ts +14 -0
- package/dist/cost-tracker.js +209 -0
- package/dist/cron.d.ts +14 -0
- package/dist/cron.js +223 -0
- package/dist/file-watcher.d.ts +14 -0
- package/dist/file-watcher.js +192 -0
- package/dist/git-autocommit.d.ts +14 -0
- package/dist/git-autocommit.js +305 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +2503 -0
- package/dist/json-path.d.ts +15 -0
- package/dist/json-path.js +311 -0
- package/dist/semver-bump.d.ts +14 -0
- package/dist/semver-bump.js +347 -0
- package/dist/shell-check.d.ts +13 -0
- package/dist/shell-check.js +225 -0
- package/dist/template-engine.d.ts +15 -0
- package/dist/template-engine.js +267 -0
- package/dist/web-search.d.ts +13 -0
- package/dist/web-search.js +210 -0
- package/package.json +75 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
|
|
4
|
+
// src/git-autocommit/index.ts
|
|
5
|
+
var API_VERSION = "^0.1.10";
|
|
6
|
+
function runGit(args, cwd) {
|
|
7
|
+
try {
|
|
8
|
+
return execSync(`git ${args.join(" ")}`, {
|
|
9
|
+
encoding: "utf-8",
|
|
10
|
+
cwd,
|
|
11
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
12
|
+
timeout: 3e4,
|
|
13
|
+
maxBuffer: 10 * 1024 * 1024
|
|
14
|
+
}).trim();
|
|
15
|
+
} catch (err) {
|
|
16
|
+
const e = err;
|
|
17
|
+
throw new Error(`git command failed: ${e.message ?? e.stderr ?? String(err)}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function getChangedFiles(cwd) {
|
|
21
|
+
const output = runGit(["status", "--porcelain"], cwd);
|
|
22
|
+
if (!output) return [];
|
|
23
|
+
return output.split("\n").filter((l) => l.trim()).map((l) => l.slice(3).trim());
|
|
24
|
+
}
|
|
25
|
+
function getStagedFiles(cwd) {
|
|
26
|
+
const output = runGit(["diff", "--cached", "--name-only"], cwd);
|
|
27
|
+
return output ? output.split("\n").filter(Boolean) : [];
|
|
28
|
+
}
|
|
29
|
+
function stageFiles(files, cwd) {
|
|
30
|
+
if (!files || !Array.isArray(files)) return;
|
|
31
|
+
const existing = files.filter((f) => {
|
|
32
|
+
try {
|
|
33
|
+
return existsSync(f);
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (existing.length === 0) throw new Error("No files exist to stage");
|
|
39
|
+
runGit(["add", ...existing], cwd);
|
|
40
|
+
}
|
|
41
|
+
function commitWithMessage(message, cwd) {
|
|
42
|
+
return runGit(["commit", "-m", message], cwd);
|
|
43
|
+
}
|
|
44
|
+
function getCommitHistory(since, cwd) {
|
|
45
|
+
const range = `${since}..HEAD` ;
|
|
46
|
+
const output = runGit(["log", range, "--format=%H %s"], cwd);
|
|
47
|
+
if (!output) return [];
|
|
48
|
+
return output.split("\n").filter(Boolean).map((line) => {
|
|
49
|
+
const spaceIdx = line.indexOf(" ");
|
|
50
|
+
const hash = line.slice(0, spaceIdx);
|
|
51
|
+
const message = line.slice(spaceIdx + 1);
|
|
52
|
+
const typeMatch = message.match(/^(\w+)(!)?:\s/);
|
|
53
|
+
const type = typeMatch?.[1] ?? "chore";
|
|
54
|
+
return { hash, message, type };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function generateCommitMessage(type, scope, summary, body) {
|
|
58
|
+
const scopePart = scope ? `(${scope})` : "";
|
|
59
|
+
const footer = body ? `
|
|
60
|
+
|
|
61
|
+
${body}` : "";
|
|
62
|
+
return `${type}${scopePart}: ${summary}${footer}`;
|
|
63
|
+
}
|
|
64
|
+
var plugin = {
|
|
65
|
+
name: "git-autocommit",
|
|
66
|
+
version: "0.1.0",
|
|
67
|
+
description: "AI-powered git staging and conventional commit message generation",
|
|
68
|
+
apiVersion: API_VERSION,
|
|
69
|
+
capabilities: { tools: true },
|
|
70
|
+
defaultConfig: {
|
|
71
|
+
conventionalCommits: true,
|
|
72
|
+
autoStage: false,
|
|
73
|
+
defaultType: "feat"
|
|
74
|
+
},
|
|
75
|
+
configSchema: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
conventionalCommits: { type: "boolean", default: true },
|
|
79
|
+
autoStage: { type: "boolean", default: false },
|
|
80
|
+
defaultType: { type: "string", default: "feat" }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
setup(api) {
|
|
84
|
+
const cwd = api.config.extensions?.["git-autocommit"];
|
|
85
|
+
const opts = {
|
|
86
|
+
conventionalCommits: cwd?.["conventionalCommits"] ?? true,
|
|
87
|
+
autoStage: cwd?.["autoStage"] ?? false,
|
|
88
|
+
defaultType: cwd?.["defaultType"] ?? "feat"
|
|
89
|
+
};
|
|
90
|
+
api.tools.register({
|
|
91
|
+
name: "git_autocommit",
|
|
92
|
+
description: "Stage files and create a git commit with an AI-generated conventional commit message. Pass files to stage specific ones, or leave empty to auto-detect all changed files.",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
files: {
|
|
97
|
+
type: "array",
|
|
98
|
+
items: { type: "string" },
|
|
99
|
+
description: "Specific files to stage. If empty, auto-detects all changed files."
|
|
100
|
+
},
|
|
101
|
+
type: {
|
|
102
|
+
type: "string",
|
|
103
|
+
enum: ["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "ci", "build", "revert"],
|
|
104
|
+
description: "Conventional commit type"
|
|
105
|
+
},
|
|
106
|
+
scope: { type: "string", description: "Commit scope (e.g. auth, api, ui)" },
|
|
107
|
+
message: { type: "string", description: "Commit summary message" },
|
|
108
|
+
body: { type: "string", description: "Optional commit body/description" },
|
|
109
|
+
dryRun: { type: "boolean", default: false, description: "Show what would be committed without committing" }
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
permission: "confirm",
|
|
113
|
+
mutating: true,
|
|
114
|
+
async execute(input, _ctx) {
|
|
115
|
+
try {
|
|
116
|
+
const type = input["type"] ?? opts.defaultType;
|
|
117
|
+
const scope = input["scope"];
|
|
118
|
+
const summary = input["message"] ?? "";
|
|
119
|
+
const body = input["body"];
|
|
120
|
+
const dryRun = input["dryRun"] ?? false;
|
|
121
|
+
const validTypes = ["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "ci", "build", "revert"];
|
|
122
|
+
if (!type || !validTypes.includes(type)) {
|
|
123
|
+
if (dryRun) {
|
|
124
|
+
return {
|
|
125
|
+
ok: true,
|
|
126
|
+
dryRun: true,
|
|
127
|
+
message: `Would create: ${summary || "update code"}`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return { ok: false, error: "type is required and must be a valid conventional commit type" };
|
|
131
|
+
}
|
|
132
|
+
const msg = generateCommitMessage(type, scope, summary || "update code", body);
|
|
133
|
+
let files;
|
|
134
|
+
const rawFiles = input["files"];
|
|
135
|
+
if (rawFiles !== void 0) {
|
|
136
|
+
if (!Array.isArray(rawFiles)) {
|
|
137
|
+
return { ok: false, error: "files must be an array of file paths" };
|
|
138
|
+
}
|
|
139
|
+
files = rawFiles;
|
|
140
|
+
}
|
|
141
|
+
if (files && files.length > 0) {
|
|
142
|
+
try {
|
|
143
|
+
stageFiles(files);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return { ok: false, error: `Failed to stage files: ${err instanceof Error ? err.message : String(err)}` };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
let staged = [];
|
|
149
|
+
try {
|
|
150
|
+
staged = getStagedFiles();
|
|
151
|
+
} catch {
|
|
152
|
+
staged = [];
|
|
153
|
+
}
|
|
154
|
+
if (staged.length === 0) {
|
|
155
|
+
try {
|
|
156
|
+
const changed = getChangedFiles();
|
|
157
|
+
if (changed.length > 0) {
|
|
158
|
+
try {
|
|
159
|
+
stageFiles(changed);
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
staged = getStagedFiles();
|
|
164
|
+
} catch {
|
|
165
|
+
staged = [];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (staged.length === 0) {
|
|
172
|
+
return { ok: false, error: "Nothing staged. Add files with git add or provide files input." };
|
|
173
|
+
}
|
|
174
|
+
let hash = "";
|
|
175
|
+
try {
|
|
176
|
+
hash = commitWithMessage(msg);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
return { ok: false, error: `Failed to commit: ${err instanceof Error ? err.message : String(err)}` };
|
|
179
|
+
}
|
|
180
|
+
api.log.info("git-autocommit: created commit", { hash, type, scope });
|
|
181
|
+
try {
|
|
182
|
+
await api.session.append({
|
|
183
|
+
type: "git-autocommit:commit",
|
|
184
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
185
|
+
hash: String(hash),
|
|
186
|
+
commitType: type,
|
|
187
|
+
scope: String(scope ?? ""),
|
|
188
|
+
files: Array.isArray(staged) ? staged : []
|
|
189
|
+
});
|
|
190
|
+
} catch (_err) {
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
ok: true,
|
|
194
|
+
hash,
|
|
195
|
+
message: msg,
|
|
196
|
+
stagedFiles: staged,
|
|
197
|
+
type,
|
|
198
|
+
scope: scope ?? null
|
|
199
|
+
};
|
|
200
|
+
} catch (err) {
|
|
201
|
+
return { ok: false, error: `Uncaught error in git_autocommit: ${err instanceof Error ? err.message : String(err)}` };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
api.tools.register({
|
|
206
|
+
name: "git_stage",
|
|
207
|
+
description: "Stage specific files for commit. Shows what would be staged without staging if dryRun is true.",
|
|
208
|
+
inputSchema: {
|
|
209
|
+
type: "object",
|
|
210
|
+
properties: {
|
|
211
|
+
files: { type: "array", items: { type: "string" }, description: "Files to stage" },
|
|
212
|
+
dryRun: { type: "boolean", default: false }
|
|
213
|
+
},
|
|
214
|
+
required: ["files"]
|
|
215
|
+
},
|
|
216
|
+
permission: "confirm",
|
|
217
|
+
mutating: true,
|
|
218
|
+
async execute(input) {
|
|
219
|
+
try {
|
|
220
|
+
let files;
|
|
221
|
+
try {
|
|
222
|
+
files = input["files"] ?? [];
|
|
223
|
+
} catch {
|
|
224
|
+
files = [];
|
|
225
|
+
}
|
|
226
|
+
const dryRun = input["dryRun"] ?? false;
|
|
227
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
228
|
+
return { ok: false, error: "files must be a non-empty array of file paths" };
|
|
229
|
+
}
|
|
230
|
+
if (dryRun) {
|
|
231
|
+
return { ok: true, dryRun: true, files, message: `Would stage: ${files.join(", ")}` };
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
stageFiles(files);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
return { ok: false, error: `Failed to stage files: ${err instanceof Error ? err.message : String(err)}` };
|
|
237
|
+
}
|
|
238
|
+
let stillChanged = [];
|
|
239
|
+
try {
|
|
240
|
+
stillChanged = getChangedFiles();
|
|
241
|
+
} catch {
|
|
242
|
+
stillChanged = [];
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
ok: true,
|
|
246
|
+
staged: files,
|
|
247
|
+
stillChanged,
|
|
248
|
+
message: `Staged ${files.length} file(s). ${stillChanged.length} file(s) still changed.`
|
|
249
|
+
};
|
|
250
|
+
} catch (err) {
|
|
251
|
+
return { ok: false, error: `git_stage error: ${err instanceof Error ? err.message : String(err)}` };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
api.tools.register({
|
|
256
|
+
name: "git_status_summary",
|
|
257
|
+
description: "Returns a summary of the current git repository status: changed files, staged files, current branch, and recent commits.",
|
|
258
|
+
inputSchema: { type: "object", properties: {} },
|
|
259
|
+
permission: "auto",
|
|
260
|
+
mutating: false,
|
|
261
|
+
async execute() {
|
|
262
|
+
let branch = "";
|
|
263
|
+
let changed = [];
|
|
264
|
+
let staged = [];
|
|
265
|
+
let aheadBehind = "";
|
|
266
|
+
const recentCommits = [];
|
|
267
|
+
try {
|
|
268
|
+
branch = runGit(["branch", "--show-current"]);
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
changed = getChangedFiles();
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
staged = getStagedFiles();
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
aheadBehind = runGit(["status", "-sb"]).split("\n")[0] ?? "";
|
|
281
|
+
} catch {
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
recentCommits.push(...getCommitHistory("-3", void 0).map((c) => ({ hash: c.hash.slice(0, 7), message: c.message })));
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
ok: true,
|
|
289
|
+
branch,
|
|
290
|
+
changedFiles: changed,
|
|
291
|
+
stagedFiles: staged,
|
|
292
|
+
aheadBehind,
|
|
293
|
+
recentCommits
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
api.log.info("git-autocommit plugin loaded", {
|
|
298
|
+
version: "0.1.0",
|
|
299
|
+
conventionalCommits: opts.conventionalCommits
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
var git_autocommit_default = plugin;
|
|
304
|
+
|
|
305
|
+
export { git_autocommit_default as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as autoDocPlugin } from './auto-doc.js';
|
|
2
|
+
export { default as gitAutocommitPlugin } from './git-autocommit.js';
|
|
3
|
+
export { default as shellCheckPlugin } from './shell-check.js';
|
|
4
|
+
export { default as costTrackerPlugin } from './cost-tracker.js';
|
|
5
|
+
export { default as fileWatcherPlugin } from './file-watcher.js';
|
|
6
|
+
export { default as webSearchPlugin } from './web-search.js';
|
|
7
|
+
export { default as jsonPathPlugin } from './json-path.js';
|
|
8
|
+
export { default as cronPlugin } from './cron.js';
|
|
9
|
+
export { default as templateEnginePlugin } from './template-engine.js';
|
|
10
|
+
export { default as semverBumpPlugin } from './semver-bump.js';
|
|
11
|
+
import '@wrongstack/core';
|