acdev 1.0.16 → 1.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acdev",
3
- "version": "1.0.16",
3
+ "version": "1.1.1",
4
4
  "description": "Local CLI + web UI for running AI agents on GitHub issues via git worktrees",
5
5
  "type": "module",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -240,6 +240,7 @@ const els = {
240
240
  overviewTicketSource: document.getElementById('overview-ticket-source'),
241
241
  enqueueFeedback: document.getElementById('enqueue-feedback'),
242
242
  preferredBranch: document.getElementById('preferred-branch'),
243
+ enqueueUserPrompt: document.getElementById('enqueue-user-prompt'),
243
244
  addBtn: document.getElementById('add-btn'),
244
245
  statsGrid: document.getElementById('stats-grid'),
245
246
  overviewRunning: document.getElementById('overview-running'),
@@ -301,6 +302,8 @@ const els = {
301
302
  settingsModelValue: document.getElementById('settings-model-value'),
302
303
  settingsModelPanel: document.getElementById('settings-model-panel'),
303
304
  settingsModelList: document.getElementById('settings-model-list'),
305
+ settingsModelSearch: document.getElementById('settings-model-search'),
306
+ settingsModelHint: document.getElementById('settings-model-hint'),
304
307
  reviewPrLink: document.getElementById('review-pr-link'),
305
308
  reviewPrStatusLine: document.getElementById('review-pr-status-line'),
306
309
  reviewPrStatusNotes: document.getElementById('review-pr-status-notes'),
@@ -308,6 +311,10 @@ const els = {
308
311
  reviewTerminalMessage: document.getElementById('review-terminal-message'),
309
312
  reviewRetryBtn: document.getElementById('review-retry-btn'),
310
313
  settingsTools: document.getElementById('settings-tools'),
314
+ settingsMaxTurns: document.getElementById('settings-max-turns'),
315
+ settingsTimeout: document.getElementById('settings-timeout'),
316
+ settingsTimeoutMs: document.getElementById('settings-timeout-ms'),
317
+ settingsTestCommand: document.getElementById('settings-test-command'),
311
318
  settingsFeedback: document.getElementById('settings-feedback'),
312
319
  settingsSaveBtn: document.getElementById('settings-save-btn'),
313
320
  settingsTicketSource: document.getElementById('settings-ticket-source'),
@@ -1572,6 +1579,24 @@ function formatLogEvent(event) {
1572
1579
  };
1573
1580
  }
1574
1581
 
1582
+ if (event.type === 'warn') {
1583
+ return {
1584
+ kind: 'warn',
1585
+ label: 'Warning',
1586
+ text: String(event.payload || 'Warning'),
1587
+ raw,
1588
+ };
1589
+ }
1590
+
1591
+ if (event.type === 'info') {
1592
+ return {
1593
+ kind: 'status',
1594
+ label: 'Info',
1595
+ text: String(event.payload || ''),
1596
+ raw,
1597
+ };
1598
+ }
1599
+
1575
1600
  if (event.type === 'agent_event') {
1576
1601
  const msg = event.payload;
1577
1602
  if (msg && typeof msg === 'object') {
@@ -2842,6 +2867,7 @@ function renderReview(jobs) {
2842
2867
  if (els.reviewPrStatusNotes) els.reviewPrStatusNotes.innerHTML = '';
2843
2868
  }
2844
2869
  els.reviewTerminalSection.classList.toggle('hidden', !isTerminal);
2870
+ const prMeta = resolvePrMeta(job);
2845
2871
  if (document.activeElement !== els.prTitle && document.activeElement !== els.prBody) {
2846
2872
  els.prTitle.value = prMeta.prTitle;
2847
2873
  els.prBody.value = prMeta.prBody;
@@ -5143,6 +5169,7 @@ els.addBtn.addEventListener('click', async () => {
5143
5169
 
5144
5170
  const urls = splitIssueUrls(text);
5145
5171
  const branchName = els.preferredBranch?.value?.trim() || '';
5172
+ const userPrompt = els.enqueueUserPrompt?.value?.trim() || '';
5146
5173
 
5147
5174
  try {
5148
5175
  const res = await fetch('/api/issues', {
@@ -5152,6 +5179,7 @@ els.addBtn.addEventListener('click', async () => {
5152
5179
  urls,
5153
5180
  ticketSource,
5154
5181
  ...(branchName ? { branchName } : {}),
5182
+ ...(userPrompt ? { userPrompt } : {}),
5155
5183
  }),
5156
5184
  });
5157
5185
  const data = await res.json();
@@ -5164,6 +5192,7 @@ els.addBtn.addEventListener('click', async () => {
5164
5192
  }
5165
5193
  els.issueUrls.value = '';
5166
5194
  if (els.preferredBranch) els.preferredBranch.value = '';
5195
+ if (els.enqueueUserPrompt) els.enqueueUserPrompt.value = '';
5167
5196
  const parts = [];
5168
5197
  if (data.jobs?.length) parts.push(`Added ${data.jobs.length}`);
5169
5198
  if (data.skipped?.length) {
package/public/index.html CHANGED
@@ -156,6 +156,22 @@
156
156
  </p>
157
157
  </div>
158
158
 
159
+ <div class="enqueue-branch-field">
160
+ <div class="enqueue-branch-label-row">
161
+ <label class="field-label" for="enqueue-user-prompt">Additional instructions</label>
162
+ <span class="enqueue-branch-optional">Optional</span>
163
+ </div>
164
+ <textarea
165
+ id="enqueue-user-prompt"
166
+ class="input textarea enqueue-textarea"
167
+ rows="3"
168
+ placeholder="Anything specific you want the agent to know or do…"
169
+ ></textarea>
170
+ <p class="enqueue-input-hint">
171
+ Appended to the agent's instructions for these tickets. Leave empty to use the issue content only.
172
+ </p>
173
+ </div>
174
+
159
175
  <div class="enqueue-info-banner" id="enqueue-info-banner" role="note">
160
176
  <svg class="enqueue-info-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
161
177
  <circle cx="12" cy="12" r="10"/>
package/public/styles.css CHANGED
@@ -1444,6 +1444,13 @@ a { color: var(--primary); text-underline-offset: 3px; }
1444
1444
  .log-line.is-error .log-kind,
1445
1445
  .log-line.is-error .log-msg { color: var(--accent-800); }
1446
1446
 
1447
+ .log-line.is-warn {
1448
+ border-left-color: var(--amber);
1449
+ background: var(--amber-100);
1450
+ }
1451
+ .log-line.is-warn .log-kind,
1452
+ .log-line.is-warn .log-msg { color: var(--amber-800); }
1453
+
1447
1454
  .log-raw { margin-top: 4px; }
1448
1455
 
1449
1456
  .log-raw > summary {
package/src/agent.js CHANGED
@@ -89,6 +89,7 @@ export function stripAiAttribution(text) {
89
89
  * comments?: Array<{ author: string, body: string }>,
90
90
  * },
91
91
  * jiraPrLinkPhrase?: string,
92
+ * userPrompt?: string,
92
93
  * }} [context]
93
94
  */
94
95
  export function buildPrompt(issueUrl, config, context = {}) {
@@ -163,6 +164,14 @@ export function buildPrompt(issueUrl, config, context = {}) {
163
164
  );
164
165
  }
165
166
 
167
+ if (context.userPrompt && context.userPrompt.trim()) {
168
+ parts.push(
169
+ '',
170
+ 'Additional instructions from the user (optional — follow them unless they conflict with the rules above):',
171
+ context.userPrompt.trim()
172
+ );
173
+ }
174
+
166
175
  // Drop empty strings from optional browse URL line
167
176
  const cleaned = parts.filter((p) => p !== '');
168
177
 
@@ -579,6 +588,7 @@ function stubAgentResult(onEvent, title, body) {
579
588
  * ticketSource?: 'github' | 'jira',
580
589
  * jiraKey?: string,
581
590
  * jiraIssue?: object,
591
+ * userPrompt?: string,
582
592
  * queryFn?: typeof query,
583
593
  * callModelFn?: (args: object) => object,
584
594
  * }} params
@@ -594,6 +604,7 @@ export async function runAgentOnIssue({
594
604
  ticketSource,
595
605
  jiraKey,
596
606
  jiraIssue,
607
+ userPrompt,
597
608
  queryFn = query,
598
609
  callModelFn,
599
610
  }) {
@@ -613,6 +624,7 @@ export async function runAgentOnIssue({
613
624
  jiraKey,
614
625
  jiraIssue,
615
626
  jiraPrLinkPhrase: config.jiraPrLinkPhrase,
627
+ userPrompt,
616
628
  });
617
629
 
618
630
  const { resultText, meta, usage } = await runConfiguredQuery({
package/src/server.js CHANGED
@@ -68,6 +68,9 @@ const CLEARABLE_STATUSES = new Set([
68
68
  'failed',
69
69
  ]);
70
70
 
71
+ /** Max length for optional user-supplied prompt appended to the agent instructions. */
72
+ const USER_PROMPT_MAX_LEN = 4000;
73
+
71
74
  /**
72
75
  * Worktree directory id: Jira key or GitHub issue number.
73
76
  * @param {{ jiraKey?: string, issueNumber?: number }} job
@@ -471,6 +474,7 @@ export function createServer({ repoRoot, config, store, useStubAgent = false, de
471
474
  ticketSource: isJira ? 'jira' : 'github',
472
475
  jiraKey: job.jiraKey,
473
476
  jiraIssue,
477
+ userPrompt: job.userPrompt,
474
478
  });
475
479
 
476
480
  if (!store.getJob(jobId)) return;
@@ -833,6 +837,17 @@ export function createServer({ repoRoot, config, store, useStubAgent = false, de
833
837
  }
834
838
  const preferredBranchName = preferredParsed.value;
835
839
 
840
+ const rawUserPrompt = req.body?.userPrompt;
841
+ if (rawUserPrompt != null && typeof rawUserPrompt !== 'string') {
842
+ return res.status(400).json({ error: 'userPrompt must be a string' });
843
+ }
844
+ const userPrompt = typeof rawUserPrompt === 'string' ? rawUserPrompt.trim() : '';
845
+ if (userPrompt.length > USER_PROMPT_MAX_LEN) {
846
+ return res.status(400).json({
847
+ error: `userPrompt must be ${USER_PROMPT_MAX_LEN} characters or fewer.`,
848
+ });
849
+ }
850
+
836
851
  const ticketSource =
837
852
  req.body?.ticketSource === 'jira' || req.body?.ticketSource === 'github'
838
853
  ? req.body.ticketSource
@@ -929,6 +944,7 @@ export function createServer({ repoRoot, config, store, useStubAgent = false, de
929
944
  ticketSource: item.ticketSource,
930
945
  jiraKey: item.jiraKey,
931
946
  ...(preferredBranchName ? { preferredBranchName } : {}),
947
+ ...(userPrompt ? { userPrompt } : {}),
932
948
  llmProvider: llm.llmProvider,
933
949
  model: llm.model,
934
950
  });
package/src/store.js CHANGED
@@ -53,19 +53,21 @@ export class Store {
53
53
  * @param {{
54
54
  * issueUrl: string,
55
55
  * issueNumber?: number,
56
- * ticketSource?: 'github' | 'jira',
57
- * jiraKey?: string,
58
- * preferredBranchName?: string,
59
- * llmProvider?: 'claude' | 'openrouter',
60
- * model?: string,
61
- * }} data
62
- * @returns {Job}
63
- */
56
+ * ticketSource?: 'github' | 'jira',
57
+ * jiraKey?: string,
58
+ * preferredBranchName?: string,
59
+ * llmProvider?: 'claude' | 'openrouter',
60
+ * model?: string,
61
+ * userPrompt?: string,
62
+ * }} data
63
+ * @returns {Job}
64
+ */
64
65
  addJob(data) {
65
66
  const now = new Date().toISOString();
66
67
  const ticketSource = data.ticketSource === 'jira' ? 'jira' : 'github';
67
68
  const llmProvider = data.llmProvider === 'openrouter' ? 'openrouter' : 'claude';
68
69
  const model = typeof data.model === 'string' ? data.model.trim() : '';
70
+ const userPrompt = typeof data.userPrompt === 'string' ? data.userPrompt.trim() : '';
69
71
  /** @type {Job} */
70
72
  const job = {
71
73
  id: randomUUID(),
@@ -76,6 +78,7 @@ export class Store {
76
78
  ...(model ? { model } : {}),
77
79
  ...(data.jiraKey ? { jiraKey: data.jiraKey } : {}),
78
80
  ...(data.preferredBranchName ? { preferredBranchName: data.preferredBranchName } : {}),
81
+ ...(userPrompt ? { userPrompt } : {}),
79
82
  status: 'queued',
80
83
  createdAt: now,
81
84
  updatedAt: now,