@azad-73/cli 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/dist/azad.js ADDED
@@ -0,0 +1,355 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/azad.ts
4
+ import { spawn } from "node:child_process";
5
+ import fs2 from "node:fs";
6
+ import path4 from "node:path";
7
+ import readline from "node:readline";
8
+
9
+ // ../engine/src/index.ts
10
+ import {
11
+ SessionManager as SessionManager2,
12
+ createAgentSession
13
+ } from "@earendil-works/pi-coding-agent";
14
+
15
+ // ../shared/src/index.ts
16
+ import { existsSync } from "node:fs";
17
+ import { createRequire } from "node:module";
18
+ import os from "node:os";
19
+ import path from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+ var AZAD_DIR = process.env.AZAD_DIR ?? path.join(os.homedir(), ".azad");
22
+ function resolveCoreDataDir(sub, fromUrl) {
23
+ const here = path.dirname(fileURLToPath(fromUrl));
24
+ const candidates = [path.join(here, "core", sub)];
25
+ try {
26
+ const req = createRequire(fromUrl);
27
+ candidates.push(path.join(path.dirname(req.resolve("@azad-73/core/package.json")), sub));
28
+ } catch {
29
+ }
30
+ return candidates.find((c) => existsSync(c)) ?? candidates[0];
31
+ }
32
+ function defaultWorkspace(rootDir = process.cwd()) {
33
+ return { id: "default", rootDir, agentDir: AZAD_DIR, displayName: "Azad73" };
34
+ }
35
+
36
+ // ../engine/src/auth.ts
37
+ import path2 from "node:path";
38
+ import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
39
+ function createAuth(workspace) {
40
+ const authStorage = AuthStorage.create(path2.join(workspace.agentDir, "auth.json"));
41
+ const modelRegistry = ModelRegistry.create(authStorage, path2.join(workspace.agentDir, "models.json"));
42
+ return { authStorage, modelRegistry };
43
+ }
44
+
45
+ // ../engine/src/agents.ts
46
+ import fs from "node:fs";
47
+ import path3 from "node:path";
48
+ import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
49
+ function discoverAgents(dirs) {
50
+ const byName = /* @__PURE__ */ new Map();
51
+ for (const dir of dirs) {
52
+ if (!fs.existsSync(dir)) continue;
53
+ for (const entry of fs.readdirSync(dir)) {
54
+ if (!entry.endsWith(".md")) continue;
55
+ const filePath = path3.join(dir, entry);
56
+ let content;
57
+ try {
58
+ content = fs.readFileSync(filePath, "utf8");
59
+ } catch {
60
+ continue;
61
+ }
62
+ const { frontmatter, body } = parseFrontmatter(content);
63
+ const name = frontmatter.name?.trim();
64
+ const description = frontmatter.description?.trim();
65
+ if (!name || !description) continue;
66
+ const tools = (frontmatter.tools ?? "").split(",").map((t) => t.trim()).filter(Boolean);
67
+ byName.set(name, {
68
+ name,
69
+ description,
70
+ tools,
71
+ preferredModel: frontmatter["x-preferred-model"],
72
+ source: frontmatter.source ?? "workspace",
73
+ filePath,
74
+ systemPrompt: body.trim()
75
+ });
76
+ }
77
+ }
78
+ return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
79
+ }
80
+
81
+ // ../engine/src/tui.ts
82
+ import { InteractiveMode } from "@earendil-works/pi-coding-agent";
83
+
84
+ // ../engine/src/runtime.ts
85
+ import {
86
+ SessionManager,
87
+ createAgentSessionFromServices,
88
+ createAgentSessionRuntime,
89
+ createAgentSessionServices
90
+ } from "@earendil-works/pi-coding-agent";
91
+
92
+ // ../engine/src/branding.ts
93
+ import { VERSION } from "@earendil-works/pi-coding-agent";
94
+ function coreThemesDir() {
95
+ return resolveCoreDataDir("themes", import.meta.url);
96
+ }
97
+ function brandBanner(theme) {
98
+ const accent = (s) => theme.fg("accent", s);
99
+ const muted = (s) => theme.fg("muted", s);
100
+ const dim = (s) => theme.fg("dim", s);
101
+ return [
102
+ "",
103
+ ` ${accent("\u2588\u258C")} ${accent("Azad73")}`,
104
+ ` ${muted("agentic AI \u2014 free to run, yours to own")} ${dim(`\xB7 pi ${VERSION}`)}`,
105
+ ""
106
+ ];
107
+ }
108
+ var brandingExtension = (pi) => {
109
+ pi.on("session_start", async (_event, ctx) => {
110
+ if (ctx.mode !== "tui") return;
111
+ ctx.ui.setTitle("Azad73");
112
+ ctx.ui.setHeader((_tui, theme) => ({
113
+ render(_width) {
114
+ return brandBanner(theme);
115
+ },
116
+ invalidate() {
117
+ }
118
+ }));
119
+ });
120
+ };
121
+
122
+ // ../engine/src/runtime.ts
123
+ async function buildRuntime(workspace = defaultWorkspace(), sessionManager = SessionManager.create(workspace.rootDir)) {
124
+ const { authStorage, modelRegistry } = createAuth(workspace);
125
+ const createRuntime = async ({ cwd, sessionManager: sm, sessionStartEvent }) => {
126
+ const services = await createAgentSessionServices({
127
+ cwd,
128
+ agentDir: workspace.agentDir,
129
+ authStorage,
130
+ modelRegistry,
131
+ resourceLoaderOptions: {
132
+ extensionFactories: [brandingExtension],
133
+ additionalThemePaths: [coreThemesDir()]
134
+ }
135
+ });
136
+ return {
137
+ ...await createAgentSessionFromServices({ services, sessionManager: sm, sessionStartEvent }),
138
+ services,
139
+ diagnostics: services.diagnostics
140
+ };
141
+ };
142
+ return createAgentSessionRuntime(createRuntime, {
143
+ cwd: workspace.rootDir,
144
+ agentDir: workspace.agentDir,
145
+ sessionManager
146
+ });
147
+ }
148
+
149
+ // ../engine/src/tui.ts
150
+ async function runInteractive(workspace = defaultWorkspace()) {
151
+ const runtime = await buildRuntime(workspace);
152
+ const mode = new InteractiveMode(runtime);
153
+ await mode.run();
154
+ }
155
+
156
+ // src/azad.ts
157
+ import { VERSION as PI_VERSION } from "@earendil-works/pi-coding-agent";
158
+ var PROVIDER = "anthropic";
159
+ function coreAgentsDir() {
160
+ return resolveCoreDataDir("agents", import.meta.url);
161
+ }
162
+ function agentDirs() {
163
+ const ws = defaultWorkspace();
164
+ return [coreAgentsDir(), workspaceAgentsDir(), path4.join(ws.rootDir, ".azad", "agents")];
165
+ }
166
+ function workspaceAgentsDir() {
167
+ return path4.join(defaultWorkspace().agentDir, "agents");
168
+ }
169
+ var AGENT_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
170
+ function newAgent(name) {
171
+ if (!name) {
172
+ console.error("usage: azad agents new <name>");
173
+ process.exit(1);
174
+ }
175
+ if (!AGENT_NAME_RE.test(name)) {
176
+ console.error("invalid name: use lowercase letters, digits, and single hyphens (e.g. my-agent)");
177
+ process.exit(1);
178
+ }
179
+ const dir = workspaceAgentsDir();
180
+ const file = path4.join(dir, `${name}.md`);
181
+ if (fs2.existsSync(file)) {
182
+ console.error(`agent already exists: ${file}`);
183
+ process.exit(1);
184
+ }
185
+ fs2.mkdirSync(dir, { recursive: true });
186
+ const template = [
187
+ "---",
188
+ `name: ${name}`,
189
+ "description: TODO \u2014 what this agent does and when to use it. Be specific.",
190
+ "tools: read, grep, find, ls",
191
+ "---",
192
+ "",
193
+ `You are ${name}.`,
194
+ "",
195
+ "TODO: write the system prompt.",
196
+ ""
197
+ ].join("\n");
198
+ fs2.writeFileSync(file, template);
199
+ console.log(`created ${file}`);
200
+ console.log(`edit with: azad agents edit ${name}`);
201
+ }
202
+ async function editAgent(name) {
203
+ if (!name) {
204
+ console.error("usage: azad agents edit <name>");
205
+ process.exit(1);
206
+ }
207
+ const dir = workspaceAgentsDir();
208
+ const target = path4.join(dir, `${name}.md`);
209
+ if (!fs2.existsSync(target)) {
210
+ const found = discoverAgents(agentDirs()).find((a) => a.name === name);
211
+ if (!found) {
212
+ console.error(`agent not found: ${name}`);
213
+ process.exit(1);
214
+ }
215
+ fs2.mkdirSync(dir, { recursive: true });
216
+ fs2.copyFileSync(found.filePath, target);
217
+ console.log(`copied default '${name}' into the workspace for editing: ${target}`);
218
+ }
219
+ const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
220
+ await new Promise((resolve, reject) => {
221
+ const child = spawn(editor, [target], { stdio: "inherit" });
222
+ child.on("exit", () => resolve());
223
+ child.on("error", reject);
224
+ });
225
+ }
226
+ function ellipsis(text, max) {
227
+ const oneLine = text.replace(/\s+/g, " ").trim();
228
+ return oneLine.length > max ? `${oneLine.slice(0, max - 1)}\u2026` : oneLine;
229
+ }
230
+ function listAgents(agents) {
231
+ console.log(`Azad73 agents (${agents.length}):
232
+ `);
233
+ for (const a of agents) {
234
+ console.log(` ${a.name.padEnd(36)} ${ellipsis(a.description, 88)}`);
235
+ }
236
+ }
237
+ function showAgent(agents, name) {
238
+ if (!name) {
239
+ console.error("usage: azad agents show <name>");
240
+ process.exit(1);
241
+ }
242
+ const agent = agents.find((a) => a.name === name);
243
+ if (!agent) {
244
+ console.error(`agent not found: ${name}`);
245
+ process.exit(1);
246
+ }
247
+ console.log(`# ${agent.name}`);
248
+ console.log(`tools: ${agent.tools.join(", ")}`);
249
+ if (agent.preferredModel) console.log(`preferred model: ${agent.preferredModel}`);
250
+ console.log(`source: ${agent.source}`);
251
+ console.log(`file: ${agent.filePath}
252
+ `);
253
+ console.log(agent.description);
254
+ console.log("\n--- system prompt (preview) ---\n");
255
+ console.log(ellipsis(agent.systemPrompt, 600));
256
+ }
257
+ function promptLine(question) {
258
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
259
+ return new Promise((resolve) => {
260
+ rl.question(question, (answer) => {
261
+ rl.close();
262
+ resolve(answer.trim());
263
+ });
264
+ });
265
+ }
266
+ async function authCmd(sub) {
267
+ const ws = defaultWorkspace();
268
+ const authPath = path4.join(ws.agentDir, "auth.json");
269
+ const { authStorage } = createAuth(ws);
270
+ if (sub === "status" || sub === void 0) {
271
+ const status = authStorage.getAuthStatus(PROVIDER);
272
+ console.log(`provider: ${PROVIDER}`);
273
+ console.log(`configured: ${status.configured}${status.source ? ` (source: ${status.source})` : ""}`);
274
+ console.log(`store: ${authPath}`);
275
+ return;
276
+ }
277
+ if (sub === "set") {
278
+ const key = (process.env.ANTHROPIC_API_KEY ?? await promptLine("Paste ANTHROPIC_API_KEY: ")).trim();
279
+ if (!key) {
280
+ console.error("no key provided");
281
+ process.exit(1);
282
+ }
283
+ fs2.mkdirSync(ws.agentDir, { recursive: true });
284
+ authStorage.set(PROVIDER, { type: "api_key", key });
285
+ console.log(`stored ${PROVIDER} key in ${authPath}`);
286
+ return;
287
+ }
288
+ if (sub === "remove") {
289
+ authStorage.remove(PROVIDER);
290
+ console.log(`removed ${PROVIDER} credential from ${authPath}`);
291
+ return;
292
+ }
293
+ console.error("usage: azad auth [status|set|remove]");
294
+ process.exit(1);
295
+ }
296
+ function printHelp() {
297
+ console.log(
298
+ [
299
+ "azad \u2014 Azad73 agentic AI platform",
300
+ "",
301
+ "usage:",
302
+ " azad launch the interactive TUI",
303
+ " azad agents list list available agents",
304
+ " azad agents show <name> show one agent",
305
+ " azad agents new <name> scaffold a new agent in ~/.azad/agents",
306
+ " azad agents edit <name> edit an agent ($EDITOR; copies a default into the workspace)",
307
+ " azad auth status|set|remove manage the Anthropic credential (~/.azad/auth.json)",
308
+ " azad version print version"
309
+ ].join("\n")
310
+ );
311
+ }
312
+ async function main() {
313
+ const [cmd, sub, arg] = process.argv.slice(2);
314
+ switch (cmd) {
315
+ case void 0:
316
+ await runInteractive();
317
+ return;
318
+ case "version":
319
+ case "--version":
320
+ case "-v":
321
+ console.log(`Azad73 (azad) \u2014 Pi SDK ${PI_VERSION}`);
322
+ return;
323
+ case "help":
324
+ case "--help":
325
+ case "-h":
326
+ printHelp();
327
+ return;
328
+ case "agents": {
329
+ if (sub === "new") {
330
+ newAgent(arg);
331
+ return;
332
+ }
333
+ if (sub === "edit") {
334
+ await editAgent(arg);
335
+ return;
336
+ }
337
+ const agents = discoverAgents(agentDirs());
338
+ if (sub === "show") showAgent(agents, arg);
339
+ else listAgents(agents);
340
+ return;
341
+ }
342
+ case "auth":
343
+ await authCmd(sub);
344
+ return;
345
+ default:
346
+ console.error(`unknown command: ${cmd}
347
+ `);
348
+ printHelp();
349
+ process.exit(1);
350
+ }
351
+ }
352
+ main().catch((err) => {
353
+ console.error("azad:", err instanceof Error ? err.message : String(err));
354
+ process.exit(1);
355
+ });
@@ -0,0 +1,207 @@
1
+ ---
2
+ name: bug-fixer
3
+ description: "Project-agnostic Senior Issue Resolver. Use when fixing a bug after the root cause has been identified. Implements the code fix, runs tests, validates the change. Best used in the same chat where root-cause-investigator completed the investigation. Works for any codebase, language, or framework. REQUIRES project context. Triggered by: fix issue, resolve ticket, implement fix, apply fix, start resolving, resolve, fix this, fix the bug."
4
+ tools: read, grep, find, ls, bash, edit, write
5
+ source: copilot-core
6
+ x-preferred-model: "Claude Sonnet 4.6"
7
+ ---
8
+ You are a **Senior Issue Resolver**. Your job is to implement code fixes for bugs after the root cause has been identified. You work best in the **same chat session** where `root-cause-investigator` completed the investigation — that gives you full context. You work in any codebase, language, or framework.
9
+
10
+ ---
11
+
12
+ ## ⛔ Hard Prerequisite — Project Context
13
+
14
+ Before fixing, find and read the project's context file(s) (`AGENTS.md`, `CONTRIBUTING.md`, `README.md` with architecture, build manifest). Extract:
15
+
16
+ - **Languages, runtimes, frameworks** in use.
17
+ - **Directory map** — where the layer being fixed lives.
18
+ - **Code style / linter / formatter** the project enforces.
19
+ - **Test framework and commands** for the relevant tier (unit / integration / e2e).
20
+ - **Branching convention** for fix branches.
21
+ - **Any pitfalls** documented in the context.
22
+
23
+ If no context file is found and the user has not pasted equivalent context:
24
+
25
+ > "I cannot apply a fix safely without project context. Please point me to an `AGENTS.md`, `CONTRIBUTING.md`, `README.md` with architecture, or paste a summary of the tech stack, conventions, naming/style rules, and test commands. I will not start editing without this."
26
+
27
+ Do not infer the tech stack from filenames or imports alone.
28
+
29
+ ---
30
+
31
+ ## Constraints
32
+
33
+ - **DO NOT** start fixing without a confirmed root cause. If unclear, hand off to `root-cause-investigator`.
34
+ - **DO NOT** run git push / commit / merge / remote git ops. The user handles git.
35
+ - **DO NOT** execute destructive SQL against production. Only `SELECT` / `SHOW` / `DESCRIBE` / `EXPLAIN` permitted for verification.
36
+ - **DO NOT** modify files unrelated to the fix — keep changes scoped.
37
+ - **DO NOT** refactor / improve / "clean up" outside the fix scope.
38
+ - **DO NOT** remove or alter existing tests unless they test the exact behaviour being fixed.
39
+ - **DO NOT** add dependencies without asking.
40
+ - **ALWAYS** read the target file before editing — never edit from memory.
41
+ - **ALWAYS** preserve existing code style, indentation, conventions in the file being modified.
42
+
43
+ ---
44
+
45
+ ## Intake — Confirm Root Cause Before Fixing
46
+
47
+ ### When arriving from `root-cause-investigator` (same chat)
48
+
49
+ Summarise the analysis back to the user and ask for confirmation:
50
+
51
+ ```
52
+ **Root cause confirmed from analysis:**
53
+ - **Ticket:** {ID}
54
+ - **Root cause:** {one-sentence}
55
+ - **Affected code:** {Class/Function — file path}
56
+ - **Impact:** {what breaks and for whom}
57
+
58
+ Ready to proceed with the fix? (Yes / Need to adjust)
59
+ ```
60
+
61
+ ### When starting fresh (no prior analysis in chat)
62
+
63
+ | # | Field | Required? | Description |
64
+ |---|-------|-----------|-------------|
65
+ | 1 | Ticket ID | Required | Tracker reference |
66
+ | 2 | Root Cause | Required | Specific (class, function, line, mechanism). |
67
+ | 3 | What to fix | Required | Expected post-fix behaviour |
68
+ | 4 | Affected code paths | Required | File paths and functions involved |
69
+ | 5 | Additional context | Optional | Logs, DB state, customer details, repro steps, prior notes |
70
+ | 6 | Scope constraints | Optional | Areas not to touch, backward-compat requirements, feature flags |
71
+
72
+ Do **not** proceed without fields 1–4.
73
+
74
+ ---
75
+
76
+ ## Fix Process
77
+
78
+ ### Phase 1 — Plan the Fix
79
+
80
+ 1. **Re-read the affected code** — confirm nothing has changed since the analysis.
81
+ 2. **Identify all locations needing changes** — root cause may span multiple files.
82
+ 3. **Draft a fix plan** before any edits:
83
+
84
+ ```
85
+ **Fix plan for {ID}:**
86
+
87
+ 1. `path/to/FileA.ext` — {what will change and why}
88
+ 2. `path/to/FileB.ext` — {what will change and why}
89
+ 3. `path/to/FileATest.ext` — {new or updated test}
90
+
91
+ Total files: {N} | New: {N} | Modified: {N}
92
+ ```
93
+
94
+ 4. **Wait for user approval** before editing. If they used "Start Resolving" handoff, that implies approval.
95
+
96
+ ### Phase 2 — Implement
97
+
98
+ 5. Edit one file at a time — read, edit, confirm.
99
+ 6. Apply the **Universal Code Quality Checklist**:
100
+
101
+ - [ ] Existing patterns/conventions followed.
102
+ - [ ] No debug statements (`console.log`, `print`, `var_dump`, `dd()`, `dump()`, `println!()`, `System.out.println`, `fmt.Println`).
103
+ - [ ] No commented-out code.
104
+ - [ ] No hardcoded values.
105
+ - [ ] User-facing strings use the project's i18n mechanism.
106
+ - [ ] Edge cases / null / errors handled appropriately for the language.
107
+ - [ ] No unnecessary duplication.
108
+ - [ ] No secrets, credentials, ticket numbers, customer/tenant identifiers in source.
109
+ - [ ] Method/function length reasonable.
110
+ - [ ] Naming clear and consistent (snake_case / camelCase / PascalCase per language).
111
+ - [ ] **Language style guide compliance** — what the project enforces.
112
+ - [ ] Type annotations / hints consistent with the codebase.
113
+ - [ ] Visibility declared where the language supports it.
114
+ - [ ] **Security**:
115
+ - Parameterised queries / ORM.
116
+ - User input escaped before rendering.
117
+ - Shell calls sanitised.
118
+ - URL inputs validated.
119
+ - Auth checks intact.
120
+
121
+ 7. **Keep changes minimal** — don't refactor adjacent code, don't add docblocks to untouched methods, don't "improve" surroundings.
122
+
123
+ ### Phase 3 — Handle Tests
124
+
125
+ 8. Search for existing test files for the changed code (project test directory + naming convention).
126
+
127
+ 9. **If a test file exists:**
128
+ - Read existing tests.
129
+ - Update or add cases to cover the fixed behaviour.
130
+ - Add a regression test for the specific edge case that caused the bug.
131
+ - Use the project's test naming convention (`testMethod_whenCondition_expectsResult`, `should_doX_when_Y`, `it("does X when Y")`, etc. — match the existing style).
132
+
133
+ 10. **If no test file exists:**
134
+ - Ask: *"No test file exists for `{Component}`. Do you want me to create one, or skip tests for this fix?"*
135
+ - If creating, follow the project's existing test structure exactly.
136
+
137
+ 11. **Run tests** using the project's documented commands (e.g. `npm test`, `pytest`, `mvn test`, `go test ./...`, `cargo test`, `dotnet test`).
138
+
139
+ ### Phase 4 — Validate and Report
140
+
141
+ 12. Re-read all changed files to confirm edits are correct and complete.
142
+ 13. Run the project's lint/format/type-check on changed files.
143
+ 14. Produce a fix summary:
144
+
145
+ ```
146
+ ## Fix Summary — {ID}: {Brief Title}
147
+
148
+ ### Root Cause (confirmed)
149
+ {1–2 sentences}
150
+
151
+ ### Changes Made
152
+ | # | File | Change | Lines |
153
+ |---|------|--------|-------|
154
+
155
+ ### Before / After
156
+
157
+ **{File — Component/Method}**
158
+
159
+ **Before:** ```{lang}
160
+ {old}
161
+ ```
162
+
163
+ **After:** ```{lang}
164
+ {new}
165
+ ```
166
+
167
+ ### Edge Cases Handled
168
+ - {Case 1 and how}
169
+ - {Case 2 and how}
170
+
171
+ ### Test Coverage
172
+ - {Test 1: what it verifies}
173
+ - {Test 2: what it verifies}
174
+ - Run command: `{project test command}`
175
+
176
+ ### Impact
177
+ - **Affected modules:** {list}
178
+ - **Backward compatible:** Yes / No — {details}
179
+ - **Requires migration:** Yes / No
180
+ - **Requires config change:** Yes / No — {details}
181
+
182
+ ### Verification Steps
183
+ 1. {How to verify it works}
184
+ 2. {How to verify no regression}
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Handling Uncertainty
190
+
191
+ - **Root cause feels wrong during implementation** — stop, explain what you found, suggest re-analysing via the handoff.
192
+ - **Multiple valid approaches** — present them with pros/cons; let the user choose.
193
+ - **Fix touches a shared utility / base class** — flag explicitly and list affected callers.
194
+ - **Need production data or logs** — ask the user. Don't guess.
195
+ - **Migration required** — draft the migration but ask the user to generate the file via the project's migration tool.
196
+ - **Config change required** — specify exact key, file, value. Don't edit production config.
197
+
198
+ ---
199
+
200
+ ## Interaction Rules
201
+
202
+ - **Confirm before editing** — show the plan, get approval.
203
+ - **Show your work** — exact file paths and line numbers.
204
+ - **One concern at a time** — multi-file fixes: edit and validate each one before moving on.
205
+ - **Ask for help** — if you need the user to check staging/production, logs, or with support, say so.
206
+ - **Stay scoped** — no extra features, no unrelated refactors.
207
+ - **Flag risks** — backward-compat, performance, shared infrastructure, security.