agentic-dev 0.2.12 → 0.2.14

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.
Files changed (48) hide show
  1. package/.agent/prd.json +29 -0
  2. package/.agent/progress.txt +1 -0
  3. package/.agent/prompt.md +21 -0
  4. package/.agent/ralph-loop-state.json +13 -0
  5. package/.agent/ralph-supervisor-state.json +12 -0
  6. package/.agent/ralph-supervisor.sh +238 -0
  7. package/.agent/ralph.sh +305 -0
  8. package/.agent/runs/README.md +7 -0
  9. package/.agent/sdd-build-ast-audit.json +13 -0
  10. package/.claude/CLAUDE.md +44 -0
  11. package/.claude/agentic-dev.json +3 -0
  12. package/.claude/agents/ai-dev.md +27 -0
  13. package/.claude/agents/backend-dev.md +26 -0
  14. package/.claude/agents/db-dev.md +26 -0
  15. package/.claude/agents/devops.md +27 -0
  16. package/.claude/agents/frontend-dev.md +25 -0
  17. package/.claude/agents/github-ops.md +25 -0
  18. package/.claude/agents/test-dev.md +26 -0
  19. package/.claude/agents/uiux-designer.md +25 -0
  20. package/.claude/settings.json +49 -0
  21. package/.claude/settings.local.json +8 -0
  22. package/.claude/skills/sdd/SKILL.md +189 -0
  23. package/.claude/skills/sdd/agents/openai.yaml +4 -0
  24. package/.claude/skills/sdd/references/section-map.md +67 -0
  25. package/.claude/workspace-config.json +3 -0
  26. package/.codex/agentic-dev.json +3 -0
  27. package/.codex/agents/README.md +22 -0
  28. package/.codex/agents/api.toml +11 -0
  29. package/.codex/agents/architecture.toml +11 -0
  30. package/.codex/agents/ci.toml +11 -0
  31. package/.codex/agents/gitops.toml +11 -0
  32. package/.codex/agents/orchestrator.toml +11 -0
  33. package/.codex/agents/quality.toml +11 -0
  34. package/.codex/agents/runtime.toml +11 -0
  35. package/.codex/agents/security.toml +11 -0
  36. package/.codex/agents/specs.toml +11 -0
  37. package/.codex/agents/ui.toml +11 -0
  38. package/.codex/config.toml +46 -0
  39. package/.codex/skills/SKILL.md +13 -0
  40. package/.codex/skills/sdd/SKILL.md +189 -0
  41. package/.codex/skills/sdd/agents/openai.yaml +4 -0
  42. package/.codex/skills/sdd/references/section-map.md +67 -0
  43. package/README.md +72 -58
  44. package/bin/agentic-dev.mjs +162 -11
  45. package/lib/github.mjs +246 -0
  46. package/lib/orchestration-assets.mjs +430 -0
  47. package/lib/scaffold.mjs +89 -0
  48. package/package.json +5 -2
@@ -0,0 +1,430 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ const SPECIALIZED_AGENTS = [
5
+ { id: "architecture", description: "Drive boundaries, system shape, and migration decisions." },
6
+ { id: "specs", description: "Translate SDD planning artifacts into actionable task structure." },
7
+ { id: "runtime", description: "Own application/runtime implementation work." },
8
+ { id: "ui", description: "Own screen and UI delivery tasks." },
9
+ { id: "api", description: "Own contract and API work." },
10
+ { id: "quality", description: "Run verification and regression closure." },
11
+ { id: "gitops", description: "Own GitHub Projects, workflow automation, and delivery closure." },
12
+ ];
13
+
14
+ function repoRootDir() {
15
+ return path.resolve(new URL("..", import.meta.url).pathname);
16
+ }
17
+
18
+ function copyRecursive(sourceRoot, destinationRoot) {
19
+ fs.mkdirSync(destinationRoot, { recursive: true });
20
+ for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) {
21
+ const sourcePath = path.join(sourceRoot, entry.name);
22
+ const destinationPath = path.join(destinationRoot, entry.name);
23
+ if (entry.isDirectory()) {
24
+ copyRecursive(sourcePath, destinationPath);
25
+ } else {
26
+ fs.copyFileSync(sourcePath, destinationPath);
27
+ }
28
+ }
29
+ }
30
+
31
+ function writeFile(filePath, content) {
32
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
33
+ fs.writeFileSync(filePath, content);
34
+ }
35
+
36
+ function workflowYaml() {
37
+ return `name: Agentic Orchestration
38
+
39
+ on:
40
+ push:
41
+ paths:
42
+ - "sdd/02_plan/**"
43
+ - ".agentic-dev/orchestration.json"
44
+ - ".agentic-dev/runtime/**"
45
+ workflow_dispatch:
46
+
47
+ jobs:
48
+ orchestrate:
49
+ runs-on: ubuntu-latest
50
+ permissions:
51
+ contents: read
52
+ issues: write
53
+ repository-projects: write
54
+ steps:
55
+ - name: Checkout
56
+ uses: actions/checkout@v4
57
+
58
+ - name: Setup Node
59
+ uses: actions/setup-node@v4
60
+ with:
61
+ node-version: "20"
62
+
63
+ - name: Start orchestration server
64
+ env:
65
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
66
+ run: |
67
+ node .agentic-dev/runtime/server.mjs > /tmp/agentic-orchestration.log 2>&1 &
68
+ echo $! > /tmp/agentic-orchestration.pid
69
+ for i in $(seq 1 30); do
70
+ if curl -fsS http://127.0.0.1:4310/health >/dev/null; then
71
+ exit 0
72
+ fi
73
+ sleep 1
74
+ done
75
+ cat /tmp/agentic-orchestration.log
76
+ exit 1
77
+
78
+ - name: Build task IR from SDD planning
79
+ run: curl -fsS -X POST http://127.0.0.1:4310/sync/ir
80
+
81
+ - name: Sync GitHub project tasks
82
+ env:
83
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
84
+ run: curl -fsS -X POST http://127.0.0.1:4310/sync/tasks
85
+
86
+ - name: Plan multi-agent queue
87
+ env:
88
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
89
+ run: curl -fsS -X POST http://127.0.0.1:4310/queue/plan
90
+
91
+ - name: Build dispatch plan
92
+ env:
93
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
94
+ run: curl -fsS -X POST http://127.0.0.1:4310/queue/dispatch
95
+
96
+ - name: Close completed tasks
97
+ env:
98
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
99
+ run: curl -fsS -X POST http://127.0.0.1:4310/tasks/close
100
+
101
+ - name: Stop orchestration server
102
+ if: always()
103
+ run: |
104
+ if [ -f /tmp/agentic-orchestration.pid ]; then
105
+ kill $(cat /tmp/agentic-orchestration.pid) || true
106
+ fi
107
+ `;
108
+ }
109
+
110
+ function runtimeLibScript() {
111
+ return `#!/usr/bin/env node
112
+ import fs from "node:fs";
113
+ import path from "node:path";
114
+ import { execFileSync } from "node:child_process";
115
+
116
+ export function ensureDir(dir) {
117
+ fs.mkdirSync(dir, { recursive: true });
118
+ }
119
+
120
+ export function readJson(filePath, fallback = null) {
121
+ if (!fs.existsSync(filePath)) return fallback;
122
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
123
+ }
124
+
125
+ export function writeJson(filePath, payload) {
126
+ ensureDir(path.dirname(filePath));
127
+ fs.writeFileSync(filePath, JSON.stringify(payload, null, 2) + "\\n");
128
+ }
129
+
130
+ export function generatedDir() {
131
+ return path.resolve(".agentic-dev/generated");
132
+ }
133
+
134
+ export function generatedPath(name) {
135
+ return path.join(generatedDir(), name);
136
+ }
137
+
138
+ export function orchestrationConfig() {
139
+ return readJson(path.resolve(".agentic-dev/orchestration.json"), {});
140
+ }
141
+
142
+ export function ghJson(args) {
143
+ return JSON.parse(execFileSync("gh", args, { encoding: "utf-8" }));
144
+ }
145
+
146
+ export function gh(args) {
147
+ return execFileSync("gh", args, { encoding: "utf-8" }).trim();
148
+ }
149
+
150
+ export function loadTaskIr() {
151
+ return readJson(generatedPath("task-ir.json"), { tasks: [] });
152
+ }
153
+
154
+ export function loadQueue() {
155
+ return readJson(generatedPath("agent-queue.json"), { queue: [] });
156
+ }
157
+ `;
158
+ }
159
+
160
+ function sddToIrScript() {
161
+ return `#!/usr/bin/env node
162
+ import fs from "node:fs";
163
+ import path from "node:path";
164
+ import { generatedPath, writeJson } from "./runtime-lib.mjs";
165
+
166
+ function walk(root) {
167
+ if (!fs.existsSync(root)) return [];
168
+ const files = [];
169
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
170
+ const full = path.join(root, entry.name);
171
+ if (entry.isDirectory()) files.push(...walk(full));
172
+ else if (entry.name.endsWith(".md")) files.push(full);
173
+ }
174
+ return files;
175
+ }
176
+
177
+ function parseChecklist(markdown, source) {
178
+ const tasks = [];
179
+ const lines = markdown.split(/\\r?\\n/);
180
+ for (const [index, line] of lines.entries()) {
181
+ const match = line.match(/^- \\[( |x)\\] (.+)$/);
182
+ if (!match) continue;
183
+ const status = match[1] === "x" ? "closed" : "open";
184
+ const title = match[2].trim();
185
+ const id = \`\${path.basename(source, ".md")}:\${index + 1}\`;
186
+ tasks.push({ id, title, status, source });
187
+ }
188
+ return tasks;
189
+ }
190
+
191
+ const planRoot = path.resolve("sdd/02_plan");
192
+ const files = walk(planRoot);
193
+ const tasks = files.flatMap((file) => parseChecklist(fs.readFileSync(file, "utf-8"), path.relative(process.cwd(), file)));
194
+ const outputPath = generatedPath("task-ir.json");
195
+ writeJson(outputPath, { generated_at: new Date().toISOString(), tasks });
196
+ console.log(\`task_ir=\${outputPath}\`);
197
+ console.log(\`tasks=\${tasks.length}\`);
198
+ `;
199
+ }
200
+
201
+ function syncProjectTasksScript() {
202
+ return `#!/usr/bin/env node
203
+ import { gh, ghJson, loadTaskIr, orchestrationConfig, writeJson, generatedPath } from "./runtime-lib.mjs";
204
+
205
+ const orchestration = orchestrationConfig();
206
+ const taskIr = loadTaskIr();
207
+ const existingIssues = ghJson(["issue", "list", "--repo", orchestration.github.repository.slug, "--state", "all", "--limit", "200", "--json", "number,title,state"]);
208
+ const syncResult = { created: [], closed: [] };
209
+
210
+ for (const task of taskIr.tasks) {
211
+ const issueTitle = \`[agentic-task] \${task.id} \${task.title}\`;
212
+ const found = existingIssues.find((issue) => issue.title.startsWith(\`[agentic-task] \${task.id} \`));
213
+ if (!found && task.status === "open") {
214
+ const url = gh(["issue", "create", "--repo", orchestration.github.repository.slug, "--title", issueTitle, "--body", \`SDD source: \${task.source}\\n\\nManaged by agentic-dev orchestration.\`, "--label", "agentic-task"]);
215
+ syncResult.created.push({ id: task.id, title: task.title, issue_url: url });
216
+ }
217
+ if (found && task.status === "closed" && found.state !== "CLOSED") {
218
+ gh(["issue", "close", String(found.number), "--repo", orchestration.github.repository.slug, "--comment", "Closed from SDD task IR."]);
219
+ syncResult.closed.push({ id: task.id, issue_number: found.number });
220
+ }
221
+ }
222
+
223
+ writeJson(generatedPath("task-sync.json"), syncResult);
224
+ console.log(\`synced_tasks=\${taskIr.tasks.length}\`);
225
+ `;
226
+ }
227
+
228
+ function runQueueScript() {
229
+ return `#!/usr/bin/env node
230
+ import { generatedPath, loadTaskIr, orchestrationConfig, writeJson } from "./runtime-lib.mjs";
231
+
232
+ const orchestration = orchestrationConfig();
233
+ const taskIr = loadTaskIr();
234
+
235
+ function classifyAgent(title) {
236
+ const lower = title.toLowerCase();
237
+ if (lower.includes("api") || lower.includes("contract")) return "api";
238
+ if (lower.includes("screen") || lower.includes("ui")) return "ui";
239
+ if (lower.includes("verify") || lower.includes("test") || lower.includes("proof")) return "quality";
240
+ if (lower.includes("workflow") || lower.includes("project") || lower.includes("github")) return "gitops";
241
+ if (lower.includes("arch") || lower.includes("boundary") || lower.includes("structure")) return "architecture";
242
+ return "runtime";
243
+ }
244
+
245
+ const openTasks = taskIr.tasks.filter((task) => task.status === "open");
246
+ const queue = openTasks.map((task) => ({
247
+ ...task,
248
+ assigned_agent: classifyAgent(task.title),
249
+ preferred_provider:
250
+ orchestration.providers.find((provider) => provider.startsWith("codex")) ||
251
+ orchestration.providers[0] ||
252
+ "codex-subscription",
253
+ }));
254
+
255
+ const outputPath = generatedPath("agent-queue.json");
256
+ writeJson(outputPath, { providers: orchestration.providers, queue });
257
+ console.log(\`agent_queue=\${outputPath}\`);
258
+ console.log(\`queued=\${queue.length}\`);
259
+ `;
260
+ }
261
+
262
+ function dispatchQueueScript() {
263
+ return `#!/usr/bin/env node
264
+ import { generatedPath, loadQueue, orchestrationConfig, writeJson } from "./runtime-lib.mjs";
265
+
266
+ const orchestration = orchestrationConfig();
267
+ const queuePayload = loadQueue();
268
+
269
+ const dispatch = queuePayload.queue.map((task) => ({
270
+ task_id: task.id,
271
+ title: task.title,
272
+ assigned_agent: task.assigned_agent,
273
+ provider: task.preferred_provider,
274
+ repository: orchestration.github.repository.slug,
275
+ project_title: orchestration.github.project.title,
276
+ execution_state: "planned",
277
+ }));
278
+
279
+ const outputPath = generatedPath("dispatch-plan.json");
280
+ writeJson(outputPath, {
281
+ generated_at: new Date().toISOString(),
282
+ dispatch,
283
+ });
284
+ console.log(\`dispatch_plan=\${outputPath}\`);
285
+ console.log(\`planned=\${dispatch.length}\`);
286
+ `;
287
+ }
288
+
289
+ function closeTasksScript() {
290
+ return `#!/usr/bin/env node
291
+ import { gh, ghJson, generatedPath, loadTaskIr, orchestrationConfig, writeJson } from "./runtime-lib.mjs";
292
+
293
+ const orchestration = orchestrationConfig();
294
+ const taskIr = loadTaskIr();
295
+ const closedIds = new Set(taskIr.tasks.filter((task) => task.status === "closed").map((task) => task.id));
296
+ const issues = ghJson(["issue", "list", "--repo", orchestration.github.repository.slug, "--state", "open", "--limit", "200", "--json", "number,title"]);
297
+ const closed = [];
298
+
299
+ for (const issue of issues) {
300
+ const match = issue.title.match(/^\\[agentic-task\\] ([^ ]+) /);
301
+ if (!match) continue;
302
+ if (!closedIds.has(match[1])) continue;
303
+ gh(["issue", "close", String(issue.number), "--repo", orchestration.github.repository.slug, "--comment", "Closed from SDD orchestration close pass."]);
304
+ closed.push({ id: match[1], issue_number: issue.number });
305
+ }
306
+
307
+ writeJson(generatedPath("closed-tasks.json"), { closed });
308
+ console.log(\`closed_candidates=\${closedIds.size}\`);
309
+ `;
310
+ }
311
+
312
+ function serverScript() {
313
+ return `#!/usr/bin/env node
314
+ import http from "node:http";
315
+ import { spawn } from "node:child_process";
316
+
317
+ const port = Number(process.env.AGENTIC_ORCHESTRATION_PORT || 4310);
318
+
319
+ function runNodeScript(script) {
320
+ return new Promise((resolve, reject) => {
321
+ const child = spawn(process.execPath, [script], {
322
+ stdio: ["ignore", "pipe", "pipe"],
323
+ });
324
+
325
+ let stdout = "";
326
+ let stderr = "";
327
+
328
+ child.stdout.on("data", (chunk) => {
329
+ stdout += chunk.toString();
330
+ });
331
+
332
+ child.stderr.on("data", (chunk) => {
333
+ stderr += chunk.toString();
334
+ });
335
+
336
+ child.on("close", (code) => {
337
+ if (code !== 0) {
338
+ reject(new Error(stderr || stdout || \`Script failed: \${script}\`));
339
+ return;
340
+ }
341
+ resolve({ stdout, stderr });
342
+ });
343
+ });
344
+ }
345
+
346
+ const routes = {
347
+ "POST /sync/ir": ".agentic-dev/runtime/sdd_to_ir.mjs",
348
+ "POST /sync/tasks": ".agentic-dev/runtime/sync_project_tasks.mjs",
349
+ "POST /queue/plan": ".agentic-dev/runtime/run_multi_agent_queue.mjs",
350
+ "POST /queue/dispatch": ".agentic-dev/runtime/dispatch_agents.mjs",
351
+ "POST /tasks/close": ".agentic-dev/runtime/close_completed_tasks.mjs",
352
+ };
353
+
354
+ const server = http.createServer(async (req, res) => {
355
+ const routeKey = \`\${req.method} \${req.url}\`;
356
+ if (req.method === "GET" && req.url === "/health") {
357
+ res.writeHead(200, { "Content-Type": "application/json" });
358
+ res.end(JSON.stringify({ ok: true, port }));
359
+ return;
360
+ }
361
+
362
+ const script = routes[routeKey];
363
+ if (!script) {
364
+ res.writeHead(404, { "Content-Type": "application/json" });
365
+ res.end(JSON.stringify({ error: "not_found" }));
366
+ return;
367
+ }
368
+
369
+ try {
370
+ const result = await runNodeScript(script);
371
+ res.writeHead(200, { "Content-Type": "application/json" });
372
+ res.end(JSON.stringify({ ok: true, stdout: result.stdout.trim() }));
373
+ } catch (error) {
374
+ res.writeHead(500, { "Content-Type": "application/json" });
375
+ res.end(JSON.stringify({ ok: false, error: String(error.message || error) }));
376
+ }
377
+ });
378
+
379
+ server.listen(port, "127.0.0.1", () => {
380
+ console.log(\`agentic_orchestration_server=http://127.0.0.1:\${port}\`);
381
+ });
382
+ `;
383
+ }
384
+
385
+ export function installSharedAgentAssets(destinationRoot) {
386
+ const root = repoRootDir();
387
+ copyRecursive(path.join(root, ".agent"), path.join(destinationRoot, ".agent"));
388
+ copyRecursive(path.join(root, ".claude"), path.join(destinationRoot, ".claude"));
389
+ copyRecursive(path.join(root, ".codex"), path.join(destinationRoot, ".codex"));
390
+ }
391
+
392
+ export function installOrchestrationAssets(destinationRoot) {
393
+ writeFile(path.join(destinationRoot, ".github/workflows/agentic-orchestration.yml"), workflowYaml());
394
+ writeFile(path.join(destinationRoot, ".agentic-dev/runtime/runtime-lib.mjs"), runtimeLibScript());
395
+ writeFile(path.join(destinationRoot, ".agentic-dev/runtime/sdd_to_ir.mjs"), sddToIrScript());
396
+ writeFile(path.join(destinationRoot, ".agentic-dev/runtime/sync_project_tasks.mjs"), syncProjectTasksScript());
397
+ writeFile(path.join(destinationRoot, ".agentic-dev/runtime/run_multi_agent_queue.mjs"), runQueueScript());
398
+ writeFile(path.join(destinationRoot, ".agentic-dev/runtime/dispatch_agents.mjs"), dispatchQueueScript());
399
+ writeFile(path.join(destinationRoot, ".agentic-dev/runtime/close_completed_tasks.mjs"), closeTasksScript());
400
+ writeFile(path.join(destinationRoot, ".agentic-dev/runtime/server.mjs"), serverScript());
401
+ }
402
+
403
+ export function buildOrchestrationConfig(setupSelections = {}) {
404
+ return {
405
+ project_name: setupSelections.projectName || "",
406
+ specialized_agents: SPECIALIZED_AGENTS,
407
+ providers: Array.isArray(setupSelections.providerProfiles) ? setupSelections.providerProfiles : [],
408
+ github: {
409
+ repository: setupSelections.githubRepository || {},
410
+ project: setupSelections.githubProject || {},
411
+ project_mode: setupSelections.githubProjectMode || "create-if-missing",
412
+ },
413
+ workflow: {
414
+ ir_output: ".agentic-dev/generated/task-ir.json",
415
+ queue_output: ".agentic-dev/generated/agent-queue.json",
416
+ dispatch_output: ".agentic-dev/generated/dispatch-plan.json",
417
+ workflow_file: ".github/workflows/agentic-orchestration.yml",
418
+ server_entry: ".agentic-dev/runtime/server.mjs",
419
+ server_port: 4310,
420
+ },
421
+ };
422
+ }
423
+
424
+ export function writeOrchestrationConfig(destinationRoot, setupSelections = {}) {
425
+ const config = buildOrchestrationConfig(setupSelections);
426
+ writeFile(
427
+ path.join(destinationRoot, ".agentic-dev/orchestration.json"),
428
+ `${JSON.stringify(config, null, 2)}\n`,
429
+ );
430
+ }
package/lib/scaffold.mjs CHANGED
@@ -3,6 +3,12 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { spawnSync } from "node:child_process";
6
+ import { ensureGitHubProject, ensureGitHubRepository } from "./github.mjs";
7
+ import {
8
+ installOrchestrationAssets,
9
+ installSharedAgentAssets,
10
+ writeOrchestrationConfig,
11
+ } from "./orchestration-assets.mjs";
6
12
 
7
13
  export const DEFAULT_TEMPLATE_OWNER = "say828";
8
14
 
@@ -64,6 +70,10 @@ export function parseArgs(argv) {
64
70
  owner: DEFAULT_TEMPLATE_OWNER,
65
71
  appMode: "",
66
72
  aiProviders: [],
73
+ providerProfiles: [],
74
+ githubRepositoryInput: "",
75
+ githubProjectMode: "",
76
+ githubProjectTitle: "",
67
77
  githubAuthMode: "",
68
78
  githubPat: "",
69
79
  force: false,
@@ -100,6 +110,23 @@ export function parseArgs(argv) {
100
110
  .filter(Boolean);
101
111
  break;
102
112
  }
113
+ case "--provider-profiles": {
114
+ const value = args.shift() ?? "";
115
+ options.providerProfiles = value
116
+ .split(",")
117
+ .map((entry) => entry.trim())
118
+ .filter(Boolean);
119
+ break;
120
+ }
121
+ case "--github-repo":
122
+ options.githubRepositoryInput = args.shift() ?? "";
123
+ break;
124
+ case "--github-project-mode":
125
+ options.githubProjectMode = args.shift() ?? "";
126
+ break;
127
+ case "--github-project-title":
128
+ options.githubProjectTitle = args.shift() ?? "";
129
+ break;
103
130
  case "--github-auth":
104
131
  options.githubAuthMode = args.shift() ?? "";
105
132
  break;
@@ -143,6 +170,7 @@ export function usage() {
143
170
  " npx agentic-dev init my-app",
144
171
  " npx agentic-dev init my-app --template template-web --yes",
145
172
  " npx agentic-dev init my-app --template template-web --project-name my-app --providers codex,claude",
173
+ " npx agentic-dev init my-app --github-repo say828/my-app --github-project-title 'My App Delivery' --provider-profiles codex-subscription,openai-api",
146
174
  "",
147
175
  "Options:",
148
176
  " --template, -t Template repo name or suffix to use",
@@ -150,6 +178,10 @@ export function usage() {
150
178
  " --owner GitHub owner for template-* repos (default: say828)",
151
179
  " --app-mode App mode: fullstack, frontend, backend",
152
180
  " --providers Comma-separated AI providers: codex, claude, ollama",
181
+ " --provider-profiles Provider/runtime list such as codex-subscription, claude-api, openai-api",
182
+ " --github-repo GitHub repo slug or URL to create/use",
183
+ " --github-project-mode create-if-missing or use-existing",
184
+ " --github-project-title GitHub Project title to bind orchestration to",
153
185
  " --github-auth GitHub auth mode: public, env, pat",
154
186
  " --github-pat GitHub PAT for this run only",
155
187
  " --force Allow scaffolding into a non-empty directory",
@@ -174,6 +206,33 @@ export function ensureTargetDir(targetDir, { force = false } = {}) {
174
206
  return resolved;
175
207
  }
176
208
 
209
+ function initializeGitRepository(destinationRoot, githubRepository) {
210
+ const gitDir = path.join(destinationRoot, ".git");
211
+ if (!fs.existsSync(gitDir)) {
212
+ runCommand("git", ["init", "-b", "main"], {
213
+ cwd: destinationRoot,
214
+ label: "Initializing git repository",
215
+ });
216
+ }
217
+
218
+ const remotes = spawnSync("git", ["remote"], {
219
+ cwd: destinationRoot,
220
+ encoding: "utf-8",
221
+ });
222
+ const existingRemotes = new Set((remotes.stdout || "").split(/\r?\n/).filter(Boolean));
223
+ if (existingRemotes.has("origin")) {
224
+ runCommand("git", ["remote", "set-url", "origin", githubRepository.cloneUrl], {
225
+ cwd: destinationRoot,
226
+ label: "Updating origin remote",
227
+ });
228
+ } else {
229
+ runCommand("git", ["remote", "add", "origin", githubRepository.cloneUrl], {
230
+ cwd: destinationRoot,
231
+ label: "Adding origin remote",
232
+ });
233
+ }
234
+ }
235
+
177
236
  export async function fetchTemplateRepos({ owner = DEFAULT_TEMPLATE_OWNER } = {}) {
178
237
  const repos = [];
179
238
  let page = 1;
@@ -383,7 +442,12 @@ function writeSetupConfig(destinationRoot, setupSelections = {}) {
383
442
  template_owner: setupSelections.owner || DEFAULT_TEMPLATE_OWNER,
384
443
  app_mode: setupSelections.appMode || "fullstack",
385
444
  ai_providers: Array.isArray(setupSelections.aiProviders) ? setupSelections.aiProviders : [],
445
+ provider_profiles: Array.isArray(setupSelections.providerProfiles)
446
+ ? setupSelections.providerProfiles
447
+ : [],
386
448
  github_auth_mode: setupSelections.githubAuthMode || "public",
449
+ github_repository: setupSelections.githubRepository || null,
450
+ github_project: setupSelections.githubProject || null,
387
451
  github_pat_supplied: Boolean(setupSelections.githubPat),
388
452
  skip_bootstrap: Boolean(setupSelections.skipBootstrap),
389
453
  };
@@ -492,8 +556,11 @@ export function installTemplateRepo({
492
556
  skipBootstrap = false,
493
557
  }) {
494
558
  scaffoldFromTemplateRepo({ destinationRoot, templateRepo });
559
+ installSharedAgentAssets(destinationRoot);
560
+ installOrchestrationAssets(destinationRoot);
495
561
  applyProviderSelections(destinationRoot, setupSelections.aiProviders);
496
562
  applyProjectMetadata(destinationRoot, setupSelections);
563
+ writeOrchestrationConfig(destinationRoot, setupSelections);
497
564
  writeSetupConfig(destinationRoot, {
498
565
  ...setupSelections,
499
566
  templateRepo: templateRepo.name,
@@ -518,3 +585,25 @@ export function installTemplateRepo({
518
585
  nextSteps: effectiveSkipBootstrap ? [bootstrapHint] : [],
519
586
  };
520
587
  }
588
+
589
+ export async function resolveGitHubOrchestration(setupSelections = {}) {
590
+ const repository = await ensureGitHubRepository(
591
+ setupSelections.githubRepositoryInput || setupSelections.projectName,
592
+ );
593
+ const project = await ensureGitHubProject({
594
+ ownerLogin: repository.owner,
595
+ title: setupSelections.githubProjectTitle || `${setupSelections.projectName} Delivery`,
596
+ mode: setupSelections.githubProjectMode || "create-if-missing",
597
+ });
598
+
599
+ return {
600
+ githubRepository: repository,
601
+ githubProject: project,
602
+ };
603
+ }
604
+
605
+ export function finalizeRepositoryGit(destinationRoot, setupSelections = {}) {
606
+ if (setupSelections.githubRepository) {
607
+ initializeGitRepository(destinationRoot, setupSelections.githubRepository);
608
+ }
609
+ }
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "agentic-dev",
3
- "version": "0.2.12",
4
- "description": "GitHub-based installer CLI for public say828/template-* repos.",
3
+ "version": "0.2.14",
4
+ "description": "GitHub Project based installer and multi-agent orchestration CLI for public say828/template-* repos.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.32.0",
7
7
  "bin": {
8
8
  "agentic-dev": "./bin/agentic-dev.mjs"
9
9
  },
10
10
  "files": [
11
+ ".agent",
12
+ ".claude",
13
+ ".codex",
11
14
  "bin",
12
15
  "lib",
13
16
  "README.md"