@electric-agent/agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/clarifier.d.ts +16 -0
- package/dist/agents/clarifier.d.ts.map +1 -0
- package/dist/agents/clarifier.js +158 -0
- package/dist/agents/clarifier.js.map +1 -0
- package/dist/agents/coder.d.ts +14 -0
- package/dist/agents/coder.d.ts.map +1 -0
- package/dist/agents/coder.js +126 -0
- package/dist/agents/coder.js.map +1 -0
- package/dist/agents/planner.d.ts +6 -0
- package/dist/agents/planner.d.ts.map +1 -0
- package/dist/agents/planner.js +69 -0
- package/dist/agents/planner.js.map +1 -0
- package/dist/agents/prompts.d.ts +9 -0
- package/dist/agents/prompts.d.ts.map +1 -0
- package/dist/agents/prompts.js +231 -0
- package/dist/agents/prompts.js.map +1 -0
- package/dist/cli/headless.d.ts +9 -0
- package/dist/cli/headless.d.ts.map +1 -0
- package/dist/cli/headless.js +506 -0
- package/dist/cli/headless.js.map +1 -0
- package/dist/cli/serve.d.ts +6 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +113 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/engine/message-parser.d.ts +8 -0
- package/dist/engine/message-parser.d.ts.map +1 -0
- package/dist/engine/message-parser.js +106 -0
- package/dist/engine/message-parser.js.map +1 -0
- package/dist/engine/orchestrator.d.ts +50 -0
- package/dist/engine/orchestrator.d.ts.map +1 -0
- package/dist/engine/orchestrator.js +492 -0
- package/dist/engine/orchestrator.js.map +1 -0
- package/dist/engine/stdio-adapter.d.ts +24 -0
- package/dist/engine/stdio-adapter.d.ts.map +1 -0
- package/dist/engine/stdio-adapter.js +139 -0
- package/dist/engine/stdio-adapter.js.map +1 -0
- package/dist/engine/stream-adapter.d.ts +45 -0
- package/dist/engine/stream-adapter.d.ts.map +1 -0
- package/dist/engine/stream-adapter.js +154 -0
- package/dist/engine/stream-adapter.js.map +1 -0
- package/dist/find-env.d.ts +3 -0
- package/dist/find-env.d.ts.map +1 -0
- package/dist/find-env.js +16 -0
- package/dist/find-env.js.map +1 -0
- package/dist/git/index.d.ts +114 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +434 -0
- package/dist/git/index.js.map +1 -0
- package/dist/hooks/block-bash.d.ts +7 -0
- package/dist/hooks/block-bash.d.ts.map +1 -0
- package/dist/hooks/block-bash.js +15 -0
- package/dist/hooks/block-bash.js.map +1 -0
- package/dist/hooks/dependency-guard.d.ts +7 -0
- package/dist/hooks/dependency-guard.d.ts.map +1 -0
- package/dist/hooks/dependency-guard.js +43 -0
- package/dist/hooks/dependency-guard.js.map +1 -0
- package/dist/hooks/guardrail-inject.d.ts +17 -0
- package/dist/hooks/guardrail-inject.d.ts.map +1 -0
- package/dist/hooks/guardrail-inject.js +69 -0
- package/dist/hooks/guardrail-inject.js.map +1 -0
- package/dist/hooks/import-validation.d.ts +7 -0
- package/dist/hooks/import-validation.d.ts.map +1 -0
- package/dist/hooks/import-validation.js +192 -0
- package/dist/hooks/import-validation.js.map +1 -0
- package/dist/hooks/index.d.ts +15 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +42 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/migration-validation.d.ts +9 -0
- package/dist/hooks/migration-validation.d.ts.map +1 -0
- package/dist/hooks/migration-validation.js +62 -0
- package/dist/hooks/migration-validation.js.map +1 -0
- package/dist/hooks/schema-consistency.d.ts +12 -0
- package/dist/hooks/schema-consistency.d.ts.map +1 -0
- package/dist/hooks/schema-consistency.js +72 -0
- package/dist/hooks/schema-consistency.js.map +1 -0
- package/dist/hooks/write-protection.d.ts +7 -0
- package/dist/hooks/write-protection.d.ts.map +1 -0
- package/dist/hooks/write-protection.js +33 -0
- package/dist/hooks/write-protection.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/progress/reporter.d.ts +15 -0
- package/dist/progress/reporter.d.ts.map +1 -0
- package/dist/progress/reporter.js +133 -0
- package/dist/progress/reporter.js.map +1 -0
- package/dist/scaffold/index.d.ts +23 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +315 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/tools/build.d.ts +3 -0
- package/dist/tools/build.d.ts.map +1 -0
- package/dist/tools/build.js +84 -0
- package/dist/tools/build.js.map +1 -0
- package/dist/tools/playbook.d.ts +14 -0
- package/dist/tools/playbook.d.ts.map +1 -0
- package/dist/tools/playbook.js +239 -0
- package/dist/tools/playbook.js.map +1 -0
- package/dist/tools/server.d.ts +3 -0
- package/dist/tools/server.d.ts.map +1 -0
- package/dist/tools/server.js +13 -0
- package/dist/tools/server.js.map +1 -0
- package/dist/working-memory/errors.d.ts +14 -0
- package/dist/working-memory/errors.d.ts.map +1 -0
- package/dist/working-memory/errors.js +89 -0
- package/dist/working-memory/errors.js.map +1 -0
- package/dist/working-memory/session.d.ts +12 -0
- package/dist/working-memory/session.d.ts.map +1 -0
- package/dist/working-memory/session.js +71 -0
- package/dist/working-memory/session.js.map +1 -0
- package/package.json +50 -0
- package/playbooks/electric-app-guardrails/SKILL.md +255 -0
- package/template/.env.example +2 -0
- package/template/Caddyfile +11 -0
- package/template/docker-compose.yml +47 -0
- package/template/drizzle.config.ts +12 -0
- package/template/postgres.conf +4 -0
- package/template/src/components/ClientOnly.tsx +27 -0
- package/template/src/db/index.ts +7 -0
- package/template/src/db/schema.ts +14 -0
- package/template/src/db/utils.ts +31 -0
- package/template/src/db/zod-schemas.ts +14 -0
- package/template/src/lib/electric-proxy.ts +59 -0
- package/template/tests/helpers/schema-test-utils.ts +106 -0
- package/template/vitest.config.ts +7 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Internal helpers
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
function execGit(cwd, args, env) {
|
|
8
|
+
try {
|
|
9
|
+
return execFileSync("git", args, {
|
|
10
|
+
cwd,
|
|
11
|
+
encoding: "utf-8",
|
|
12
|
+
timeout: 30_000,
|
|
13
|
+
env: { ...process.env, ...env },
|
|
14
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
15
|
+
}).trim();
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
const stderr = e?.stderr || "";
|
|
19
|
+
const stdout = e?.stdout || "";
|
|
20
|
+
const detail = stderr || stdout || (e instanceof Error ? e.message : "git command failed");
|
|
21
|
+
throw new Error(`git ${args.join(" ")}: ${detail}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function execGh(args, opts) {
|
|
25
|
+
try {
|
|
26
|
+
return execFileSync("gh", args, {
|
|
27
|
+
cwd: opts?.cwd,
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
timeout: 30_000,
|
|
30
|
+
env: { ...process.env, ...opts?.env },
|
|
31
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
32
|
+
}).trim();
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
const stderr = e?.stderr || "";
|
|
36
|
+
const stdout = e?.stdout || "";
|
|
37
|
+
const detail = stderr || stdout || (e instanceof Error ? e.message : "gh command failed");
|
|
38
|
+
throw new Error(`gh ${args.join(" ")}: ${detail}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function parseRepoName(remoteUrl) {
|
|
42
|
+
if (!remoteUrl)
|
|
43
|
+
return null;
|
|
44
|
+
// Handle SSH: git@github.com:owner/repo.git
|
|
45
|
+
const ssh = remoteUrl.match(/github\.com[:/](.+?)(?:\.git)?$/);
|
|
46
|
+
if (ssh)
|
|
47
|
+
return ssh[1];
|
|
48
|
+
// Handle HTTPS: https://github.com/owner/repo.git
|
|
49
|
+
const https = remoteUrl.match(/github\.com\/(.+?)(?:\.git)?$/);
|
|
50
|
+
if (https)
|
|
51
|
+
return https[1];
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function generateCommitMessage(cwd) {
|
|
55
|
+
const status = execGit(cwd, ["status", "--porcelain"]);
|
|
56
|
+
const lines = status.split("\n").filter(Boolean);
|
|
57
|
+
const added = lines.filter((l) => l.startsWith("A") || l.startsWith("?")).length;
|
|
58
|
+
const modified = lines.filter((l) => l.startsWith(" M") || l.startsWith("M")).length;
|
|
59
|
+
const deleted = lines.filter((l) => l.startsWith(" D") || l.startsWith("D")).length;
|
|
60
|
+
const parts = [];
|
|
61
|
+
if (added)
|
|
62
|
+
parts.push(`+${added}`);
|
|
63
|
+
if (modified)
|
|
64
|
+
parts.push(`~${modified}`);
|
|
65
|
+
if (deleted)
|
|
66
|
+
parts.push(`-${deleted}`);
|
|
67
|
+
const summary = parts.length > 0 ? `${parts.join(" ")} files` : "no changes";
|
|
68
|
+
return `checkpoint: ${summary} [${new Date().toISOString().slice(0, 10)}]`;
|
|
69
|
+
}
|
|
70
|
+
function ensureGitIdentity(cwd) {
|
|
71
|
+
try {
|
|
72
|
+
execGit(cwd, ["config", "user.email"]);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
execGit(cwd, ["config", "user.email", "electric-agent@local"]);
|
|
76
|
+
execGit(cwd, ["config", "user.name", "Electric Agent"]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Public API — Local Git
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
/**
|
|
83
|
+
* Initialize a git repo with an initial commit.
|
|
84
|
+
* Called by scaffold after project setup.
|
|
85
|
+
*/
|
|
86
|
+
export function gitInit(projectDir, projectName) {
|
|
87
|
+
execGit(projectDir, ["init", "-b", "main"]);
|
|
88
|
+
ensureGitIdentity(projectDir);
|
|
89
|
+
execGit(projectDir, ["add", "-A"]);
|
|
90
|
+
return execGit(projectDir, ["commit", "-m", `chore: scaffold ${projectName ?? "project"}`]);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Checkout an existing branch or create a new one.
|
|
94
|
+
*/
|
|
95
|
+
export function gitCheckoutBranch(projectDir, branch) {
|
|
96
|
+
try {
|
|
97
|
+
// Try checking out existing branch
|
|
98
|
+
execGit(projectDir, ["checkout", branch]);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Branch doesn't exist locally — create it
|
|
102
|
+
execGit(projectDir, ["checkout", "-b", branch]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the current git status of a project directory.
|
|
107
|
+
*/
|
|
108
|
+
export function gitStatus(projectDir) {
|
|
109
|
+
const gitDir = path.join(projectDir, ".git");
|
|
110
|
+
if (!fs.existsSync(gitDir)) {
|
|
111
|
+
return {
|
|
112
|
+
initialized: false,
|
|
113
|
+
branch: null,
|
|
114
|
+
remoteUrl: null,
|
|
115
|
+
hasUncommitted: false,
|
|
116
|
+
lastCommit: null,
|
|
117
|
+
repoName: null,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
let branch = null;
|
|
121
|
+
try {
|
|
122
|
+
branch = execGit(projectDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// detached HEAD or empty repo
|
|
126
|
+
}
|
|
127
|
+
let remoteUrl = null;
|
|
128
|
+
try {
|
|
129
|
+
remoteUrl = execGit(projectDir, ["remote", "get-url", "origin"]);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// no remote configured
|
|
133
|
+
}
|
|
134
|
+
let hasUncommitted = false;
|
|
135
|
+
try {
|
|
136
|
+
const status = execGit(projectDir, ["status", "--porcelain"]);
|
|
137
|
+
hasUncommitted = status.length > 0;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// ignore
|
|
141
|
+
}
|
|
142
|
+
let lastCommit = null;
|
|
143
|
+
try {
|
|
144
|
+
const log = execGit(projectDir, ["log", "-1", "--format=%H%n%s%n%aI"]);
|
|
145
|
+
const [hash, message, ts] = log.split("\n");
|
|
146
|
+
if (hash && message && ts) {
|
|
147
|
+
lastCommit = { hash, message, ts };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// empty repo
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
initialized: true,
|
|
155
|
+
branch,
|
|
156
|
+
remoteUrl,
|
|
157
|
+
hasUncommitted,
|
|
158
|
+
lastCommit,
|
|
159
|
+
repoName: parseRepoName(remoteUrl),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Create a checkpoint commit with all current changes.
|
|
164
|
+
*/
|
|
165
|
+
export function gitCheckpoint(projectDir, message) {
|
|
166
|
+
const status = gitStatus(projectDir);
|
|
167
|
+
if (!status.initialized) {
|
|
168
|
+
return { success: false, commitHash: null, message: "", error: "Not a git repository" };
|
|
169
|
+
}
|
|
170
|
+
if (!status.hasUncommitted) {
|
|
171
|
+
return {
|
|
172
|
+
success: true,
|
|
173
|
+
commitHash: status.lastCommit?.hash ?? null,
|
|
174
|
+
message: "No changes to commit",
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const commitMsg = message || generateCommitMessage(projectDir);
|
|
178
|
+
try {
|
|
179
|
+
ensureGitIdentity(projectDir);
|
|
180
|
+
execGit(projectDir, ["add", "-A"]);
|
|
181
|
+
execGit(projectDir, ["commit", "-m", commitMsg]);
|
|
182
|
+
const hash = execGit(projectDir, ["rev-parse", "HEAD"]);
|
|
183
|
+
return { success: true, commitHash: hash, message: commitMsg };
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
commitHash: null,
|
|
189
|
+
message: commitMsg,
|
|
190
|
+
error: e instanceof Error ? e.message : "Commit failed",
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Public API — GitHub (via gh CLI)
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
/**
|
|
198
|
+
* Validate a GitHub PAT by logging in and checking auth status.
|
|
199
|
+
* Required scopes: repo, read:user
|
|
200
|
+
*/
|
|
201
|
+
export function validateGhToken(token) {
|
|
202
|
+
try {
|
|
203
|
+
// Login with the token via stdin
|
|
204
|
+
execFileSync("gh", ["auth", "login", "--with-token"], {
|
|
205
|
+
input: token,
|
|
206
|
+
encoding: "utf-8",
|
|
207
|
+
timeout: 15_000,
|
|
208
|
+
env: { ...process.env, GH_TOKEN: undefined },
|
|
209
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
210
|
+
});
|
|
211
|
+
// Check auth status
|
|
212
|
+
const statusOutput = execGh(["auth", "status"]);
|
|
213
|
+
// Extract username from status output
|
|
214
|
+
const userMatch = statusOutput.match(/Logged in to github\.com account (\S+)/) ||
|
|
215
|
+
statusOutput.match(/Logged in to github\.com as (\S+)/);
|
|
216
|
+
const username = userMatch?.[1] ?? "unknown";
|
|
217
|
+
return { valid: true, username };
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
// Clean up on failure
|
|
221
|
+
try {
|
|
222
|
+
execGh(["auth", "logout", "--hostname", "github.com"]);
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// ignore cleanup failures
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
valid: false,
|
|
229
|
+
error: e instanceof Error ? e.message : "Token validation failed",
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Publish a local project to a new GitHub repo.
|
|
235
|
+
* Creates a feature branch and pushes.
|
|
236
|
+
* If a token is provided, it is used instead of the ambient GH_TOKEN.
|
|
237
|
+
*/
|
|
238
|
+
export function ghPublish(projectDir, repoName, opts) {
|
|
239
|
+
const visibility = opts?.visibility ?? "private";
|
|
240
|
+
const ghOpts = { cwd: projectDir };
|
|
241
|
+
if (opts?.token)
|
|
242
|
+
ghOpts.env = { GH_TOKEN: opts.token };
|
|
243
|
+
try {
|
|
244
|
+
// Checkpoint any uncommitted changes first
|
|
245
|
+
const status = gitStatus(projectDir);
|
|
246
|
+
if (status.hasUncommitted) {
|
|
247
|
+
gitCheckpoint(projectDir, "checkpoint before publish");
|
|
248
|
+
}
|
|
249
|
+
// Create feature branch if on main
|
|
250
|
+
const currentBranch = execGit(projectDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
251
|
+
let targetBranch = currentBranch;
|
|
252
|
+
if (currentBranch === "main") {
|
|
253
|
+
const branchName = `electric-agent/${path.basename(projectDir)}`;
|
|
254
|
+
execGit(projectDir, ["checkout", "-b", branchName]);
|
|
255
|
+
targetBranch = branchName;
|
|
256
|
+
}
|
|
257
|
+
// Create repo and push
|
|
258
|
+
const output = execGh([
|
|
259
|
+
"repo",
|
|
260
|
+
"create",
|
|
261
|
+
repoName,
|
|
262
|
+
`--${visibility}`,
|
|
263
|
+
"--source",
|
|
264
|
+
".",
|
|
265
|
+
"--remote",
|
|
266
|
+
"origin",
|
|
267
|
+
"--push",
|
|
268
|
+
], ghOpts);
|
|
269
|
+
// Extract repo URL from output
|
|
270
|
+
const urlMatch = output.match(/(https:\/\/github\.com\/\S+)/);
|
|
271
|
+
const repoUrl = urlMatch?.[1] ?? `https://github.com/${repoName}`;
|
|
272
|
+
return { success: true, repoUrl, branch: targetBranch };
|
|
273
|
+
}
|
|
274
|
+
catch (e) {
|
|
275
|
+
return {
|
|
276
|
+
success: false,
|
|
277
|
+
repoUrl: null,
|
|
278
|
+
branch: null,
|
|
279
|
+
error: e instanceof Error ? e.message : "Publish failed",
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Create a PR from the current branch to the default branch.
|
|
285
|
+
* If a token is provided, it is used instead of the ambient GH_TOKEN.
|
|
286
|
+
*/
|
|
287
|
+
export function ghPrCreate(projectDir, title, body, token) {
|
|
288
|
+
const ghOpts = { cwd: projectDir };
|
|
289
|
+
if (token)
|
|
290
|
+
ghOpts.env = { GH_TOKEN: token };
|
|
291
|
+
try {
|
|
292
|
+
const branch = execGit(projectDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
293
|
+
const prTitle = title || `[electric-agent] ${branch}`;
|
|
294
|
+
const prBody = body || "Generated by electric-agent.\n\nReview the changes and merge when ready.";
|
|
295
|
+
// Push current branch first
|
|
296
|
+
execGit(projectDir, ["push", "-u", "origin", branch]);
|
|
297
|
+
const output = execGh(["pr", "create", "--title", prTitle, "--body", prBody], ghOpts);
|
|
298
|
+
// Extract PR URL from output
|
|
299
|
+
const urlMatch = output.match(/(https:\/\/github\.com\/\S+\/pull\/\d+)/);
|
|
300
|
+
return { prUrl: urlMatch?.[1] ?? output };
|
|
301
|
+
}
|
|
302
|
+
catch (e) {
|
|
303
|
+
return {
|
|
304
|
+
prUrl: null,
|
|
305
|
+
error: e instanceof Error ? e.message : "PR creation failed",
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* List the authenticated user's personal account + organizations.
|
|
311
|
+
* If a token is provided, it is used instead of the ambient GH_TOKEN.
|
|
312
|
+
*/
|
|
313
|
+
export function ghListAccounts(token) {
|
|
314
|
+
const ghOpts = token ? { env: { GH_TOKEN: token } } : undefined;
|
|
315
|
+
const accounts = [];
|
|
316
|
+
try {
|
|
317
|
+
// Get the authenticated user's login
|
|
318
|
+
const username = execGh(["api", "/user", "--jq", ".login"], ghOpts);
|
|
319
|
+
if (username) {
|
|
320
|
+
accounts.push({ login: username, type: "user" });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// ignore
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
// Get organizations the user belongs to
|
|
328
|
+
const output = execGh(["api", "/user/orgs", "--jq", "[.[] | .login]"], ghOpts);
|
|
329
|
+
const orgs = JSON.parse(output);
|
|
330
|
+
for (const org of orgs) {
|
|
331
|
+
accounts.push({ login: org, type: "org" });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// ignore
|
|
336
|
+
}
|
|
337
|
+
return accounts;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* List the authenticated user's GitHub repos.
|
|
341
|
+
* If a token is provided, it is used instead of the ambient GH_TOKEN.
|
|
342
|
+
*/
|
|
343
|
+
export function ghListRepos(limit = 50, token) {
|
|
344
|
+
const ghOpts = token ? { env: { GH_TOKEN: token } } : undefined;
|
|
345
|
+
try {
|
|
346
|
+
// Use /user/repos API to get ALL repos the token has access to
|
|
347
|
+
// (owned, collaborator, and org member), not just owned repos.
|
|
348
|
+
const output = execGh([
|
|
349
|
+
"api",
|
|
350
|
+
`/user/repos?sort=updated&per_page=${limit}&affiliation=owner,collaborator,organization_member`,
|
|
351
|
+
"--jq",
|
|
352
|
+
`[.[] | {nameWithOwner: .full_name, url: .html_url, updatedAt: .updated_at}]`,
|
|
353
|
+
], ghOpts);
|
|
354
|
+
return JSON.parse(output);
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
// Fallback to gh repo list (only owned repos)
|
|
358
|
+
try {
|
|
359
|
+
const output = execGh([
|
|
360
|
+
"repo",
|
|
361
|
+
"list",
|
|
362
|
+
"--json",
|
|
363
|
+
"nameWithOwner,url,updatedAt",
|
|
364
|
+
"--limit",
|
|
365
|
+
String(limit),
|
|
366
|
+
"--sort",
|
|
367
|
+
"updated",
|
|
368
|
+
], ghOpts);
|
|
369
|
+
return JSON.parse(output);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return [];
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* List branches for a GitHub repo.
|
|
378
|
+
* If a token is provided, it is used instead of the ambient GH_TOKEN.
|
|
379
|
+
*/
|
|
380
|
+
export function ghListBranches(repoFullName, token) {
|
|
381
|
+
const ghOpts = token ? { env: { GH_TOKEN: token } } : undefined;
|
|
382
|
+
try {
|
|
383
|
+
// Get the default branch name
|
|
384
|
+
const defaultBranch = execGh(["api", `/repos/${repoFullName}`, "--jq", ".default_branch"], ghOpts);
|
|
385
|
+
// Get all branches
|
|
386
|
+
const output = execGh(["api", `/repos/${repoFullName}/branches?per_page=100`, "--jq", `[.[] | {name: .name}]`], ghOpts);
|
|
387
|
+
const branches = JSON.parse(output);
|
|
388
|
+
return branches.map((b) => ({
|
|
389
|
+
name: b.name,
|
|
390
|
+
isDefault: b.name === defaultBranch,
|
|
391
|
+
}));
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Clone a GitHub repo into a target directory.
|
|
399
|
+
* If a token is provided, it is used instead of the ambient GH_TOKEN.
|
|
400
|
+
*/
|
|
401
|
+
export function ghClone(repoUrl, targetDir, branch, token) {
|
|
402
|
+
const args = ["repo", "clone", repoUrl, targetDir];
|
|
403
|
+
if (branch) {
|
|
404
|
+
args.push("--", "-b", branch);
|
|
405
|
+
}
|
|
406
|
+
execGh(args, token ? { env: { GH_TOKEN: token } } : undefined);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Check if gh CLI is available and authenticated.
|
|
410
|
+
* If a token is provided, it is used instead of the ambient GH_TOKEN.
|
|
411
|
+
*/
|
|
412
|
+
export function isGhAuthenticated(token) {
|
|
413
|
+
try {
|
|
414
|
+
execGh(["auth", "status"], token ? { env: { GH_TOKEN: token } } : undefined);
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Push the current branch to origin.
|
|
423
|
+
*/
|
|
424
|
+
export function gitPush(projectDir) {
|
|
425
|
+
try {
|
|
426
|
+
const branch = execGit(projectDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
427
|
+
execGit(projectDir, ["push", "-u", "origin", branch]);
|
|
428
|
+
return { success: true };
|
|
429
|
+
}
|
|
430
|
+
catch (e) {
|
|
431
|
+
return { success: false, error: e instanceof Error ? e.message : "Push failed" };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse hook: Block all Bash tool usage.
|
|
4
|
+
* Used by the planner agent which should only read playbooks, not explore the filesystem.
|
|
5
|
+
*/
|
|
6
|
+
export declare const blockBash: HookCallback;
|
|
7
|
+
//# sourceMappingURL=block-bash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-bash.d.ts","sourceRoot":"","sources":["../../src/hooks/block-bash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAElE;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,YAUvB,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreToolUse hook: Block all Bash tool usage.
|
|
3
|
+
* Used by the planner agent which should only read playbooks, not explore the filesystem.
|
|
4
|
+
*/
|
|
5
|
+
export const blockBash = async () => {
|
|
6
|
+
return {
|
|
7
|
+
suppressOutput: true,
|
|
8
|
+
hookSpecificOutput: {
|
|
9
|
+
hookEventName: "PreToolUse",
|
|
10
|
+
permissionDecision: "deny",
|
|
11
|
+
permissionDecisionReason: "Bash is not allowed. Use read_playbook and list_playbooks tools instead. Produce the plan from playbook knowledge.",
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=block-bash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-bash.js","sourceRoot":"","sources":["../../src/hooks/block-bash.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAiB,KAAK,IAAI,EAAE;IACjD,OAAO;QACN,cAAc,EAAE,IAAI;QACpB,kBAAkB,EAAE;YACnB,aAAa,EAAE,YAAqB;YACpC,kBAAkB,EAAE,MAAe;YACnC,wBAAwB,EACvB,oHAAoH;SACrH;KACD,CAAA;AACF,CAAC,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse hook: Prevent removal of existing dependencies from package.json.
|
|
4
|
+
* Allows additions, blocks removals.
|
|
5
|
+
*/
|
|
6
|
+
export declare const dependencyGuard: HookCallback;
|
|
7
|
+
//# sourceMappingURL=dependency-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-guard.d.ts","sourceRoot":"","sources":["../../src/hooks/dependency-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,gCAAgC,CAAA;AAEvF;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,YAgC7B,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreToolUse hook: Prevent removal of existing dependencies from package.json.
|
|
3
|
+
* Allows additions, blocks removals.
|
|
4
|
+
*/
|
|
5
|
+
export const dependencyGuard = async (input, _toolUseID, _opts) => {
|
|
6
|
+
const preInput = input;
|
|
7
|
+
const toolInput = preInput.tool_input;
|
|
8
|
+
const filePath = (toolInput?.file_path || "");
|
|
9
|
+
if (!filePath.endsWith("package.json"))
|
|
10
|
+
return {};
|
|
11
|
+
// For Write tool, compare new content against what exists
|
|
12
|
+
const newContent = toolInput?.content;
|
|
13
|
+
if (!newContent)
|
|
14
|
+
return {};
|
|
15
|
+
// For Edit tool, we get old_string and new_string
|
|
16
|
+
const oldString = toolInput?.old_string;
|
|
17
|
+
if (oldString) {
|
|
18
|
+
// Check if the edit removes dependency lines
|
|
19
|
+
const oldDeps = extractDependencyNames(oldString);
|
|
20
|
+
const newString = (toolInput?.new_string || "");
|
|
21
|
+
const newDeps = extractDependencyNames(newString);
|
|
22
|
+
const removed = oldDeps.filter((d) => !newDeps.includes(d));
|
|
23
|
+
if (removed.length > 0) {
|
|
24
|
+
return {
|
|
25
|
+
hookSpecificOutput: {
|
|
26
|
+
hookEventName: "PreToolUse",
|
|
27
|
+
permissionDecision: "deny",
|
|
28
|
+
permissionDecisionReason: `Cannot remove existing dependencies: ${removed.join(", ")}. Only additions are allowed.`,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {};
|
|
34
|
+
};
|
|
35
|
+
function extractDependencyNames(content) {
|
|
36
|
+
const deps = [];
|
|
37
|
+
const depRegex = /"([^"]+)":\s*"/g;
|
|
38
|
+
for (const match of content.matchAll(depRegex)) {
|
|
39
|
+
deps.push(match[1]);
|
|
40
|
+
}
|
|
41
|
+
return deps;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=dependency-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependency-guard.js","sourceRoot":"","sources":["../../src/hooks/dependency-guard.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAiB,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE;IAC/E,MAAM,QAAQ,GAAG,KAA4B,CAAA;IAC7C,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAiD,CAAA;IAE5E,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,SAAS,IAAI,EAAE,CAAW,CAAA;IACvD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,EAAE,CAAA;IAEjD,0DAA0D;IAC1D,MAAM,UAAU,GAAG,SAAS,EAAE,OAA6B,CAAA;IAC3D,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAA;IAE1B,kDAAkD;IAClD,MAAM,SAAS,GAAG,SAAS,EAAE,UAAgC,CAAA;IAC7D,IAAI,SAAS,EAAE,CAAC;QACf,6CAA6C;QAC7C,MAAM,OAAO,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAA;QACjD,MAAM,SAAS,GAAG,CAAC,SAAS,EAAE,UAAU,IAAI,EAAE,CAAW,CAAA;QACzD,MAAM,OAAO,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAA;QAEjD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;QAC3D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO;gBACN,kBAAkB,EAAE;oBACnB,aAAa,EAAE,YAAqB;oBACpC,kBAAkB,EAAE,MAAe;oBACnC,wBAAwB,EAAE,wCAAwC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,+BAA+B;iBACnH;aACD,CAAA;QACF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,CAAA;AACV,CAAC,CAAA;AAED,SAAS,sBAAsB,CAAC,OAAe;IAC9C,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,MAAM,QAAQ,GAAG,iBAAiB,CAAA;IAClC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IACpB,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* SessionStart hook: Auto-inject the electric-app-guardrails playbook content
|
|
4
|
+
* as additional context at the start of every coder session.
|
|
5
|
+
*
|
|
6
|
+
* This ensures the agent always has critical guardrail rules in context,
|
|
7
|
+
* regardless of whether the PLAN.md tells it to read the playbook or
|
|
8
|
+
* whether it skips Phase 0.
|
|
9
|
+
*/
|
|
10
|
+
export declare const guardrailInject: HookCallback;
|
|
11
|
+
/**
|
|
12
|
+
* Create a SessionStart hook that injects both guardrails AND ARCHITECTURE.md.
|
|
13
|
+
* ARCHITECTURE.md provides the coder with immediate structural knowledge of the app,
|
|
14
|
+
* saving 3-5 turns of file scanning on iterations.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSessionStartHook(projectDir: string): HookCallback;
|
|
17
|
+
//# sourceMappingURL=guardrail-inject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrail-inject.d.ts","sourceRoot":"","sources":["../../src/hooks/guardrail-inject.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAyBlE;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,YAU7B,CAAA;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAuBvE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the bundled guardrails SKILL.md that ships with electric-agent.
|
|
7
|
+
* Returns the file content stripped of YAML frontmatter.
|
|
8
|
+
*/
|
|
9
|
+
function loadGuardrails() {
|
|
10
|
+
const dirs = [
|
|
11
|
+
path.resolve(__dirname, "../../playbooks/electric-app-guardrails/SKILL.md"),
|
|
12
|
+
path.resolve(__dirname, "../playbooks/electric-app-guardrails/SKILL.md"),
|
|
13
|
+
];
|
|
14
|
+
for (const filePath of dirs) {
|
|
15
|
+
if (fs.existsSync(filePath)) {
|
|
16
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
17
|
+
// Strip YAML frontmatter
|
|
18
|
+
return raw.replace(/^---\n[\s\S]*?\n---\n*/, "");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* SessionStart hook: Auto-inject the electric-app-guardrails playbook content
|
|
25
|
+
* as additional context at the start of every coder session.
|
|
26
|
+
*
|
|
27
|
+
* This ensures the agent always has critical guardrail rules in context,
|
|
28
|
+
* regardless of whether the PLAN.md tells it to read the playbook or
|
|
29
|
+
* whether it skips Phase 0.
|
|
30
|
+
*/
|
|
31
|
+
export const guardrailInject = async () => {
|
|
32
|
+
const content = loadGuardrails();
|
|
33
|
+
if (!content)
|
|
34
|
+
return {};
|
|
35
|
+
return {
|
|
36
|
+
hookSpecificOutput: {
|
|
37
|
+
hookEventName: "SessionStart",
|
|
38
|
+
additionalContext: `<electric-app-guardrails>\n${content}\n</electric-app-guardrails>`,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Create a SessionStart hook that injects both guardrails AND ARCHITECTURE.md.
|
|
44
|
+
* ARCHITECTURE.md provides the coder with immediate structural knowledge of the app,
|
|
45
|
+
* saving 3-5 turns of file scanning on iterations.
|
|
46
|
+
*/
|
|
47
|
+
export function createSessionStartHook(projectDir) {
|
|
48
|
+
return async () => {
|
|
49
|
+
const parts = [];
|
|
50
|
+
const guardrails = loadGuardrails();
|
|
51
|
+
if (guardrails) {
|
|
52
|
+
parts.push(`<electric-app-guardrails>\n${guardrails}\n</electric-app-guardrails>`);
|
|
53
|
+
}
|
|
54
|
+
const archPath = path.join(projectDir, "ARCHITECTURE.md");
|
|
55
|
+
if (fs.existsSync(archPath)) {
|
|
56
|
+
const arch = fs.readFileSync(archPath, "utf-8");
|
|
57
|
+
parts.push(`<app-architecture>\n${arch}\n</app-architecture>`);
|
|
58
|
+
}
|
|
59
|
+
if (parts.length === 0)
|
|
60
|
+
return {};
|
|
61
|
+
return {
|
|
62
|
+
hookSpecificOutput: {
|
|
63
|
+
hookEventName: "SessionStart",
|
|
64
|
+
additionalContext: parts.join("\n\n"),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=guardrail-inject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrail-inject.js","sourceRoot":"","sources":["../../src/hooks/guardrail-inject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAE9D;;;GAGG;AACH,SAAS,cAAc;IACtB,MAAM,IAAI,GAAG;QACZ,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kDAAkD,CAAC;QAC3E,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,+CAA+C,CAAC;KACxE,CAAA;IAED,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC7B,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC9C,yBAAyB;YACzB,OAAO,GAAG,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAA;QACjD,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAA;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAiB,KAAK,IAAI,EAAE;IACvD,MAAM,OAAO,GAAG,cAAc,EAAE,CAAA;IAChC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAA;IAEvB,OAAO;QACN,kBAAkB,EAAE;YACnB,aAAa,EAAE,cAAuB;YACtC,iBAAiB,EAAE,8BAA8B,OAAO,8BAA8B;SACtF;KACD,CAAA;AACF,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACxD,OAAO,KAAK,IAAI,EAAE;QACjB,MAAM,KAAK,GAAa,EAAE,CAAA;QAE1B,MAAM,UAAU,GAAG,cAAc,EAAE,CAAA;QACnC,IAAI,UAAU,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,8BAA8B,UAAU,8BAA8B,CAAC,CAAA;QACnF,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;QACzD,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC/C,KAAK,CAAC,IAAI,CAAC,uBAAuB,IAAI,uBAAuB,CAAC,CAAA;QAC/D,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAA;QACjC,OAAO;YACN,kBAAkB,EAAE;gBACnB,aAAa,EAAE,cAAuB;gBACtC,iBAAiB,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;aACrC;SACD,CAAA;IACF,CAAC,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse hook: Validate imports in file content against known-correct table.
|
|
4
|
+
* Denies writes that contain hallucinated or incorrect imports.
|
|
5
|
+
*/
|
|
6
|
+
export declare const importValidation: HookCallback;
|
|
7
|
+
//# sourceMappingURL=import-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-validation.d.ts","sourceRoot":"","sources":["../../src/hooks/import-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,gCAAgC,CAAA;AAsJvF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAmD9B,CAAA"}
|