@matthugh1/conductor-cli 0.1.0 → 0.2.2

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.
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../../src/core/runner-prompt.ts
4
+ var AUTONOMOUS_WORKFLOW = `
5
+ ## Identity
6
+
7
+ You are an autonomous implementation engineer spawned by Conductor's runner.
8
+ When you need human input, use conductor_log_decision followed by
9
+ conductor_await_decision \u2014 this blocks until the human responds in the
10
+ Conductor panel. Work carefully and methodically.
11
+
12
+ ## Workflow (follow in order)
13
+
14
+ ### 1. Start your session
15
+
16
+ Call conductor_session_brief with:
17
+ - projectRoot: the project name given in your assignment below
18
+ - deliverableId: the deliverable ID given in your assignment below
19
+ - agentRole: "implementation-engineer"
20
+ - agentType: "claude-code"
21
+
22
+ Save the returned sessionId \u2014 pass it to every subsequent Conductor call.
23
+
24
+ ### 2. Read the brief and understand your assignment
25
+
26
+ The session brief contains your deliverable title, description, and
27
+ acceptance criteria. Read them carefully before writing any code.
28
+
29
+ ### 3. Branch from dev
30
+
31
+ Run: git fetch origin && git checkout -b <branch-name> origin/dev
32
+
33
+ Name the branch after the deliverable (e.g., feat/prompt-template).
34
+ Branch from origin/dev (not main) so you pick up any prior autonomous
35
+ work that has been merged to dev but not yet reviewed into main.
36
+ Never use git checkout dev \u2014 use origin/dev directly to avoid worktree conflicts.
37
+ Never branch from another feature branch.
38
+
39
+ ### 4. Mark your deliverable in-progress
40
+
41
+ Call conductor_update_deliverable with status "in_progress".
42
+
43
+ ### 5. Do the work
44
+
45
+ Implement the deliverable according to its acceptance criteria.
46
+ After each meaningful chunk of work, call conductor_log_built with
47
+ a plain English summary and the sessionId.
48
+
49
+ ### 6. Self-check before submitting
50
+
51
+ Before creating a PR, run ALL of these checks:
52
+
53
+ 1. npx tsc --noEmit (type check)
54
+ 2. npm run build (build)
55
+ 3. npm test (tests)
56
+ 4. git status (working tree clean?)
57
+ 5. Review your own code for:
58
+ - No \`any\` type assertions
59
+ - No console.log in production code
60
+ - No large files (>200 lines of code)
61
+ - No large functions (>50 lines)
62
+ - No unused imports
63
+ - No secrets or .env files staged
64
+
65
+ ### 7. Retry on failure (up to 3 attempts)
66
+
67
+ If any check fails:
68
+ - Read the error output carefully
69
+ - Fix the issue
70
+ - Re-run the failing check
71
+ - Repeat up to 3 total attempts per check
72
+
73
+ If a check still fails after 3 attempts, go to the parking procedure (step 10).
74
+
75
+ ### 8. Create a PR and merge to dev
76
+
77
+ When all checks pass:
78
+ - git add the relevant files (never use git add -A)
79
+ - git commit with a descriptive message
80
+ - git push -u origin <branch-name>
81
+ - gh pr create --title "<deliverable title>" --body "<summary>"
82
+ - Save the PR URL \u2014 you need it for the completion report
83
+
84
+ After the PR is created, merge your branch to dev so the next agent
85
+ in the chain can build on your work without waiting for human review.
86
+ Use a temporary local branch to avoid worktree checkout conflicts:
87
+
88
+ git fetch origin
89
+ git checkout -b _dev-merge origin/dev
90
+ git merge --no-ff <your-branch-name> -m "chore: merge <branch-name> to dev"
91
+ git push origin HEAD:dev
92
+ git checkout <your-branch-name>
93
+ git branch -D _dev-merge
94
+
95
+ Never merge your own PR to main. Always merge to dev after creating the PR.
96
+
97
+ ### 9. Submit for review
98
+
99
+ Call conductor_update_deliverable with:
100
+ - status: "review"
101
+ - completionReport with all check results and the prUrl
102
+
103
+ Then call conductor_end_session with the sessionId.
104
+
105
+ ### 10. When you need a human decision
106
+
107
+ If you hit a judgment call, ambiguous requirement, or blocker that needs human input:
108
+ 1. Call conductor_log_decision with the options and your reasoning.
109
+ This returns a decisionId.
110
+ 2. Call conductor_await_decision with that decisionId.
111
+ This blocks until the human approves in the Conductor panel (polls every
112
+ 3 seconds, up to 5 minutes per call).
113
+ 3. If it returns "pending" (timeout), call conductor_await_decision again.
114
+ Keep calling until you get a resolution.
115
+ 4. Once resolved, read the chosenOption and continue your work accordingly.
116
+
117
+ CRITICAL: You MUST call conductor_log_decision to create the decision.
118
+ Do not just say "I need a decision" in text \u2014 the human cannot see your
119
+ text output. The only way they know you need input is through the
120
+ Conductor decision panel.
121
+
122
+ ### 11. Parking procedure (when truly stuck)
123
+
124
+ If you cannot complete the work and waiting for a decision won't help:
125
+ - Call conductor_update_deliverable with status "parked".
126
+ - Call conductor_log_next describing what remains and why you stopped.
127
+ - Call conductor_end_session with the sessionId.
128
+ - Exit cleanly.
129
+
130
+ ## Rules
131
+
132
+ - Follow all rules in CLAUDE.md (included above if present).
133
+ - Never merge your own PR to main \u2014 only create it.
134
+ - Always merge your branch to dev after creating the PR (see step 8).
135
+ - Never make silent decisions \u2014 log every judgment call via conductor_log_decision.
136
+ - If a subsequent deliverable exists in the same outcome, create a handoff
137
+ prompt via conductor_create_prompt before submitting for review.
138
+ - Keep commits atomic and messages descriptive.
139
+ - Do not install new dependencies without logging a decision.
140
+ - Do not modify files outside the scope of your deliverable.
141
+ - If you encounter merge conflicts, resolve them yourself.
142
+ - Work within the existing code style and patterns.
143
+ `.trim();
144
+ var FREEFORM_TASK_WORKFLOW = `
145
+ ## Identity
146
+
147
+ You are an agent spawned by Conductor's daemon to execute a task.
148
+ When you need human input, use conductor_log_decision followed by
149
+ conductor_await_decision \u2014 this blocks until the human responds in the
150
+ Conductor panel. Work carefully and methodically.
151
+
152
+ ## Workflow
153
+
154
+ 1. Read the task description below carefully.
155
+ 2. Execute the task as described.
156
+ 3. If you hit a judgment call or blocker, log a decision via conductor_log_decision
157
+ and wait via conductor_await_decision. Do not proceed without resolution.
158
+ 4. When finished, report what you accomplished clearly.
159
+
160
+ ## Rules
161
+
162
+ - Follow all rules in CLAUDE.md (included above if present).
163
+ - Never make silent decisions \u2014 log every judgment call.
164
+ - Work within the existing code style and patterns.
165
+ - Do not install new dependencies without logging a decision.
166
+ `.trim();
167
+ function assembleAutonomousPrompt(ctx) {
168
+ const parts = [];
169
+ if (ctx.claudeMd.length > 0) {
170
+ parts.push("# Project Rules (CLAUDE.md)\n\n" + ctx.claudeMd);
171
+ }
172
+ parts.push("# Autonomous Runner Instructions\n\n" + AUTONOMOUS_WORKFLOW);
173
+ parts.push(
174
+ `# Your Assignment
175
+
176
+ Project: ${ctx.projectName}
177
+ Deliverable: ${ctx.item.title}
178
+ Deliverable ID: ${ctx.item.entityId}
179
+ Initiative: ${ctx.item.initiativeTitle ?? "unknown"}
180
+ Priority: P${ctx.item.priority}
181
+ Action: ${ctx.item.action}
182
+
183
+ Start by calling conductor_session_brief with:
184
+ - projectRoot: "${ctx.projectName}"
185
+ - deliverableId: "${ctx.item.entityId}"
186
+ - agentRole: "implementation-engineer"
187
+ - agentType: "claude-code"`
188
+ );
189
+ return parts.join("\n\n---\n\n");
190
+ }
191
+ function assembleTaskPrompt(agent, task, claudeMd) {
192
+ const parts = [];
193
+ if (claudeMd.length > 0) {
194
+ parts.push("# Project Rules (CLAUDE.md)\n\n" + claudeMd);
195
+ }
196
+ if (agent.systemPrompt.length > 0) {
197
+ parts.push(
198
+ `# Agent: ${agent.name}
199
+
200
+ ## System Prompt
201
+
202
+ ` + agent.systemPrompt
203
+ );
204
+ }
205
+ if (task.deliverableId) {
206
+ parts.push("# Autonomous Runner Instructions\n\n" + AUTONOMOUS_WORKFLOW);
207
+ } else {
208
+ parts.push("# Task Instructions\n\n" + FREEFORM_TASK_WORKFLOW);
209
+ }
210
+ const assignmentLines = [
211
+ `# Your Assignment`,
212
+ ``,
213
+ `Task: ${task.title}`,
214
+ `Project: ${task.projectName}`
215
+ ];
216
+ if (task.deliverableId) {
217
+ assignmentLines.push(`Deliverable ID: ${task.deliverableId}`);
218
+ assignmentLines.push(``);
219
+ assignmentLines.push(`Start by calling conductor_session_brief with:`);
220
+ assignmentLines.push(`- projectRoot: "${task.projectName}"`);
221
+ assignmentLines.push(`- deliverableId: "${task.deliverableId}"`);
222
+ assignmentLines.push(`- agentRole: "implementation-engineer"`);
223
+ assignmentLines.push(`- agentType: "claude-code"`);
224
+ }
225
+ if (task.prompt.length > 0) {
226
+ assignmentLines.push(``);
227
+ assignmentLines.push(`## Task Details`);
228
+ assignmentLines.push(``);
229
+ assignmentLines.push(task.prompt);
230
+ }
231
+ parts.push(assignmentLines.join("\n"));
232
+ return parts.join("\n\n---\n\n");
233
+ }
234
+
235
+ export {
236
+ assembleAutonomousPrompt,
237
+ assembleTaskPrompt
238
+ };
@@ -339,8 +339,9 @@ async function getConflictBranchesForWorkQueue(projectRoot, options) {
339
339
  }
340
340
  }
341
341
  const conflicts = [];
342
+ const SKIP_BRANCHES = /* @__PURE__ */ new Set(["dev", "develop", "staging"]);
342
343
  for (const name of namesToScan) {
343
- if (name === defaultBranch) {
344
+ if (name === defaultBranch || SKIP_BRANCHES.has(name)) {
344
345
  continue;
345
346
  }
346
347
  let lastIso = "";
@@ -4,18 +4,16 @@
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
5
  import { dirname, join } from "path";
6
6
  import { fileURLToPath } from "url";
7
- import { homedir } from "os";
8
7
  var __filename = fileURLToPath(import.meta.url);
9
8
  var __dirname = dirname(__filename);
10
- var CONFIG_DIR = join(homedir(), ".conductor");
11
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
12
- var KEY_FILE = join(CONFIG_DIR, "api-key");
13
- function readConfig() {
14
- if (!existsSync(CONFIG_FILE)) {
9
+ function readConfig(projectRoot) {
10
+ const root = projectRoot ?? process.cwd();
11
+ const configFile = join(root, ".conductor", "config.json");
12
+ if (!existsSync(configFile)) {
15
13
  return {};
16
14
  }
17
15
  try {
18
- const raw = readFileSync(CONFIG_FILE, "utf8");
16
+ const raw = readFileSync(configFile, "utf8");
19
17
  const parsed = JSON.parse(raw);
20
18
  if (parsed !== null && typeof parsed === "object") {
21
19
  return parsed;
@@ -25,22 +23,21 @@ function readConfig() {
25
23
  return {};
26
24
  }
27
25
  }
28
- function writeConfig(config) {
29
- mkdirSync(CONFIG_DIR, { recursive: true });
30
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", {
26
+ function writeConfig(config, projectRoot) {
27
+ const root = projectRoot ?? process.cwd();
28
+ const dir = join(root, ".conductor");
29
+ mkdirSync(dir, { recursive: true });
30
+ writeFileSync(join(dir, "config.json"), JSON.stringify(config, null, 2) + "\n", {
31
31
  mode: 384
32
32
  });
33
33
  }
34
- function readApiKey() {
35
- if (!existsSync(KEY_FILE)) {
36
- return void 0;
37
- }
38
- try {
39
- const key = readFileSync(KEY_FILE, "utf8").trim();
40
- return key.length > 0 ? key : void 0;
41
- } catch {
42
- return void 0;
43
- }
34
+ function readApiKey(projectRoot) {
35
+ const config = readConfig(projectRoot);
36
+ return config.apiKey;
37
+ }
38
+ function readProjectId(projectRoot) {
39
+ const config = readConfig(projectRoot);
40
+ return config.projectId;
44
41
  }
45
42
  function getConfigEndpoint() {
46
43
  const candidates = [
@@ -130,6 +127,8 @@ async function ensureDatabaseUrl() {
130
127
  export {
131
128
  readConfig,
132
129
  writeConfig,
130
+ readApiKey,
131
+ readProjectId,
133
132
  getServerBaseUrl,
134
133
  fetchConfigFromServer,
135
134
  postToServer,
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ createNotification
4
+ } from "./chunk-3MJBQK2F.js";
2
5
  import {
3
6
  runGit
4
7
  } from "./chunk-FAZ7FCZQ.js";
@@ -54,73 +57,6 @@ async function logGitActivity(input) {
54
57
  return rowToEvent(rows[0]);
55
58
  }
56
59
 
57
- // ../../src/core/notifications.ts
58
- var VALID_EVENT_TYPES = /* @__PURE__ */ new Set([
59
- "gate_rejection",
60
- "gate_bypassed",
61
- "review_needed",
62
- "handoff_needed",
63
- "decision_pending",
64
- "stale_session",
65
- "abandoned_session",
66
- "missing_checkin",
67
- "orphan_work",
68
- "stage_transition",
69
- "watchdog_flag",
70
- "stale_worktree",
71
- "autonomous_flag_changed"
72
- ]);
73
- var VALID_PRIORITIES = /* @__PURE__ */ new Set([
74
- "info",
75
- "warning",
76
- "action_needed"
77
- ]);
78
- var VALID_LINK_TYPES = /* @__PURE__ */ new Set([
79
- "deliverable",
80
- "decision",
81
- "initiative",
82
- "session"
83
- ]);
84
- async function createNotification(params) {
85
- if (!params.projectId || params.projectId.trim().length === 0) {
86
- throw new Error("A project id is required to create a notification.");
87
- }
88
- if (!VALID_EVENT_TYPES.has(params.eventType)) {
89
- throw new Error(
90
- `Unknown event type "${params.eventType}". Expected one of: ${[...VALID_EVENT_TYPES].join(", ")}.`
91
- );
92
- }
93
- if (!params.message || params.message.trim().length === 0) {
94
- throw new Error("A notification needs a message.");
95
- }
96
- if (!VALID_PRIORITIES.has(params.priority)) {
97
- throw new Error(
98
- `Unknown priority "${params.priority}". Expected info, warning, or action_needed.`
99
- );
100
- }
101
- if (params.linkType !== void 0 && !VALID_LINK_TYPES.has(params.linkType)) {
102
- throw new Error(
103
- `Unknown link type "${params.linkType}". Expected deliverable, decision, initiative, or session.`
104
- );
105
- }
106
- const rows = await query(
107
- `INSERT INTO notifications (project_id, event_type, message, priority, link_type, link_id, agent_type, agent_name)
108
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
109
- RETURNING id`,
110
- [
111
- params.projectId,
112
- params.eventType,
113
- params.message.trim(),
114
- params.priority,
115
- params.linkType ?? null,
116
- params.linkId ?? null,
117
- params.agentType ?? null,
118
- params.agentName ?? null
119
- ]
120
- );
121
- return rows[0].id;
122
- }
123
-
124
60
  // ../../src/core/worktree-manager.ts
125
61
  var STALE_DAYS = 7;
126
62
  var STALE_MS = STALE_DAYS * 24 * 60 * 60 * 1e3;
@@ -176,6 +112,11 @@ async function createWorktree(projectId, projectRoot, initiativeId, initiativeTi
176
112
  `A worktree already exists for "${initiativeTitle}" at ${existing.worktreePath}`
177
113
  );
178
114
  }
115
+ await query(
116
+ `DELETE FROM worktrees
117
+ WHERE project_id = $1 AND initiative_id = $2 AND status IN ('removed', 'merged')`,
118
+ [projectId, initiativeId]
119
+ );
179
120
  const slug = slugifyForBranch(initiativeTitle);
180
121
  const branchName = slug;
181
122
  const projectDir = path.basename(projectRoot);
@@ -217,8 +158,21 @@ async function createWorktree(projectId, projectRoot, initiativeId, initiativeTi
217
158
  return record;
218
159
  }
219
160
  async function listWorktrees(projectId, opts) {
220
- const statusFilter = opts?.status;
221
- const baseQuery = `
161
+ const conditions = ["w.project_id = $1"];
162
+ const params = [projectId];
163
+ let paramIndex = 2;
164
+ if (opts?.status) {
165
+ conditions.push(`w.status = $${paramIndex}`);
166
+ params.push(opts.status);
167
+ paramIndex++;
168
+ }
169
+ if (opts?.initiativeId) {
170
+ conditions.push(`w.initiative_id = $${paramIndex}`);
171
+ params.push(opts.initiativeId);
172
+ paramIndex++;
173
+ }
174
+ const rows = await query(
175
+ `
222
176
  SELECT w.*, i.title AS initiative_title,
223
177
  (
224
178
  SELECT d.title FROM deliverables d
@@ -229,18 +183,10 @@ async function listWorktrees(projectId, opts) {
229
183
  ) AS deliverable_title
230
184
  FROM worktrees w
231
185
  LEFT JOIN initiatives i ON i.id = w.initiative_id
232
- WHERE w.project_id = $1
233
- `;
234
- if (statusFilter) {
235
- const rows2 = await query(
236
- `${baseQuery} AND w.status = $2 ORDER BY w.created_at DESC`,
237
- [projectId, statusFilter]
238
- );
239
- return rows2.map(rowToWithInitiative);
240
- }
241
- const rows = await query(
242
- `${baseQuery} ORDER BY w.created_at DESC`,
243
- [projectId]
186
+ WHERE ${conditions.join(" AND ")}
187
+ ORDER BY w.created_at DESC
188
+ `,
189
+ params
244
190
  );
245
191
  return rows.map(rowToWithInitiative);
246
192
  }
@@ -4,14 +4,18 @@ import {
4
4
  fetchConfigFromServer,
5
5
  getServerBaseUrl,
6
6
  postToServer,
7
+ readApiKey,
7
8
  readConfig,
9
+ readProjectId,
8
10
  writeConfig
9
- } from "./chunk-MJKFQIYA.js";
11
+ } from "./chunk-B2WDTKD7.js";
10
12
  export {
11
13
  ensureDatabaseUrl,
12
14
  fetchConfigFromServer,
13
15
  getServerBaseUrl,
14
16
  postToServer,
17
+ readApiKey,
15
18
  readConfig,
19
+ readProjectId,
16
20
  writeConfig
17
21
  };
@@ -2,10 +2,11 @@
2
2
  import {
3
3
  logGitActivity,
4
4
  removeWorktree
5
- } from "./chunk-JZT526HU.js";
5
+ } from "./chunk-KB2DTST2.js";
6
+ import "./chunk-3MJBQK2F.js";
6
7
  import {
7
8
  getProjectPathById
8
- } from "./chunk-VYINBHPQ.js";
9
+ } from "./chunk-6VMREHG4.js";
9
10
  import {
10
11
  runGit
11
12
  } from "./chunk-FAZ7FCZQ.js";