@link-assistant/hive-mind 2.2.0 → 2.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.
- package/CHANGELOG.md +12 -0
- package/README.hi.md +12 -11
- package/README.md +9 -9
- package/README.ru.md +12 -11
- package/README.zh.md +12 -11
- package/package.json +1 -1
- package/src/agent.prompts.lib.mjs +6 -0
- package/src/claude.prompts.lib.mjs +6 -0
- package/src/codex.prompts.lib.mjs +6 -0
- package/src/config.lib.mjs +4 -0
- package/src/development-log.finalize.lib.mjs +11 -0
- package/src/development-log.lib.mjs +297 -0
- package/src/gemini.prompts.lib.mjs +6 -0
- package/src/lib.mjs +13 -0
- package/src/opencode.prompts.lib.mjs +6 -0
- package/src/option-suggestions.lib.mjs +1 -0
- package/src/qwen.prompts.lib.mjs +6 -0
- package/src/solve.config.lib.mjs +15 -2
- package/src/solve.mjs +17 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 2.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f9f30d6: Treat an omitted `--think` option as `--think off` across tools, using a zero budget or the lowest supported effort when reasoning cannot be disabled.
|
|
8
|
+
|
|
9
|
+
## 2.2.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- e86a40e: Add a `--development-log` solve option that prompts agents to collect issue data, then preserves and commits resumable tool sessions under per-session UUID directories.
|
|
14
|
+
|
|
3
15
|
## 2.2.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.hi.md
CHANGED
|
@@ -173,6 +173,7 @@ claude
|
|
|
173
173
|
# Use /config command and set:
|
|
174
174
|
# Reduce motion true # Will save your ssh trafic, and make Claude Code more responsive (less latency)
|
|
175
175
|
# Thinking mode false # Anthropic models perform better and cheaper without thinking
|
|
176
|
+
# `--think` न देने पर Hive Mind इसे `--think off` मानता है।
|
|
176
177
|
# Model haiku # chepear for connection testing manually
|
|
177
178
|
# Claude in Chrome enabled by default false # No need for Chrome support on server
|
|
178
179
|
|
|
@@ -346,11 +347,11 @@ solve <issue-url> [options]
|
|
|
346
347
|
|
|
347
348
|
**सबसे अधिक उपयोग किए जाने वाले विकल्प:**
|
|
348
349
|
|
|
349
|
-
| विकल्प | संक्षिप्त | विवरण
|
|
350
|
-
| --------------- | --------- |
|
|
351
|
-
| `--model` | `-m` | उपयोग करने वाला AI मॉडल (sonnet, opus, haiku)
|
|
352
|
-
| `--think` | | सोचने का स्तर (low, medium, high, max)
|
|
353
|
-
| `--base-branch` | `-b` | PR के लिए टार्गेट ब्रांच
|
|
350
|
+
| विकल्प | संक्षिप्त | विवरण | डिफ़ॉल्ट |
|
|
351
|
+
| --------------- | --------- | --------------------------------------------------------- | ---------- |
|
|
352
|
+
| `--model` | `-m` | उपयोग करने वाला AI मॉडल (sonnet, opus, haiku) | sonnet |
|
|
353
|
+
| `--think` | | सोचने का स्तर (off, low, medium, high, xhigh, ultra, max) | off |
|
|
354
|
+
| `--base-branch` | `-b` | PR के लिए टार्गेट ब्रांच | (डिफ़ॉल्ट) |
|
|
354
355
|
|
|
355
356
|
**अन्य उपयोगी विकल्प:**
|
|
356
357
|
|
|
@@ -372,12 +373,12 @@ hive <github-url> [options]
|
|
|
372
373
|
|
|
373
374
|
**सबसे अधिक उपयोग किए जाने वाले विकल्प:**
|
|
374
375
|
|
|
375
|
-
| विकल्प | संक्षिप्त | विवरण
|
|
376
|
-
| -------------- | --------- |
|
|
377
|
-
| `--model` | `-m` | उपयोग करने वाला AI मॉडल (sonnet, opus, haiku)
|
|
378
|
-
| `--think` | | सोचने का स्तर (low, medium, high, max)
|
|
379
|
-
| `--all-issues` | `-a` | सभी इश्यू निगरानी करें (लेबल अनदेखा करें)
|
|
380
|
-
| `--once` | | एकल रन (लगातार निगरानी न करें)
|
|
376
|
+
| विकल्प | संक्षिप्त | विवरण | डिफ़ॉल्ट |
|
|
377
|
+
| -------------- | --------- | --------------------------------------------------------- | -------- |
|
|
378
|
+
| `--model` | `-m` | उपयोग करने वाला AI मॉडल (sonnet, opus, haiku) | sonnet |
|
|
379
|
+
| `--think` | | सोचने का स्तर (off, low, medium, high, xhigh, ultra, max) | off |
|
|
380
|
+
| `--all-issues` | `-a` | सभी इश्यू निगरानी करें (लेबल अनदेखा करें) | false |
|
|
381
|
+
| `--once` | | एकल रन (लगातार निगरानी न करें) | false |
|
|
381
382
|
|
|
382
383
|
**अन्य उपयोगी विकल्प:**
|
|
383
384
|
|
package/README.md
CHANGED
|
@@ -507,15 +507,15 @@ Free Models via Kilo Gateway (with --tool agent):
|
|
|
507
507
|
|
|
508
508
|
Current tool defaults in Hive Mind:
|
|
509
509
|
|
|
510
|
-
| Tool | Default model | Default reasoning behavior
|
|
511
|
-
| ---------- | --------------------------------------------------------------------------- |
|
|
512
|
-
| `claude` | `sonnet` |
|
|
513
|
-
| `codex` | `gpt-5.6-sol` preferred, with runtime fallback to local catalog (`gpt-5.5`) |
|
|
514
|
-
| `opencode` | `grok-code-fast-1` |
|
|
515
|
-
| `agent` | `nemotron-3-super-free` |
|
|
516
|
-
| `gemini` | `flash` |
|
|
517
|
-
| `qwen` | `qwen3-coder-plus` |
|
|
518
|
-
| `gemini` | `gemini-2.5-flash` |
|
|
510
|
+
| Tool | Default model | Default reasoning behavior |
|
|
511
|
+
| ---------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
512
|
+
| `claude` | `sonnet` | Defaults to `--think off`: zero budget when supported, otherwise the lowest effort |
|
|
513
|
+
| `codex` | `gpt-5.6-sol` preferred, with runtime fallback to local catalog (`gpt-5.5`) | Defaults to `--think off`, mapped to `reasoning_effort=none` |
|
|
514
|
+
| `opencode` | `grok-code-fast-1` | Defaults to `--think off`; no positive thinking prompt is added |
|
|
515
|
+
| `agent` | `nemotron-3-super-free` | Defaults to `--think off`; no positive thinking prompt is added |
|
|
516
|
+
| `gemini` | `flash` | Defaults to `--think off`; no positive thinking prompt is added |
|
|
517
|
+
| `qwen` | `qwen3-coder-plus` | Defaults to `--think off`; no positive thinking prompt is added |
|
|
518
|
+
| `gemini` | `gemini-2.5-flash` | Defaults to `--think off`; no positive thinking prompt is added |
|
|
519
519
|
|
|
520
520
|
See [docs/CONFIGURATION.md](./docs/CONFIGURATION.md) for the full per-tool defaults and reasoning mappings.
|
|
521
521
|
|
package/README.ru.md
CHANGED
|
@@ -173,6 +173,7 @@ claude
|
|
|
173
173
|
# Use /config command and set:
|
|
174
174
|
# Reduce motion true # Will save your ssh trafic, and make Claude Code more responsive (less latency)
|
|
175
175
|
# Thinking mode false # Anthropic models perform better and cheaper without thinking
|
|
176
|
+
# Если `--think` не указан, Hive Mind трактует это как `--think off`.
|
|
176
177
|
# Model haiku # chepear for connection testing manually
|
|
177
178
|
# Claude in Chrome enabled by default false # No need for Chrome support on server
|
|
178
179
|
|
|
@@ -346,11 +347,11 @@ solve <issue-url> [options]
|
|
|
346
347
|
|
|
347
348
|
**Наиболее часто используемые параметры:**
|
|
348
349
|
|
|
349
|
-
| Параметр | Сокр. | Описание
|
|
350
|
-
| --------------- | ----- |
|
|
351
|
-
| `--model` | `-m` | Используемая модель ИИ (sonnet, opus, haiku)
|
|
352
|
-
| `--think` | | Уровень мышления (low, medium, high, max)
|
|
353
|
-
| `--base-branch` | `-b` | Целевая ветка для PR
|
|
350
|
+
| Параметр | Сокр. | Описание | По умолчанию |
|
|
351
|
+
| --------------- | ----- | ------------------------------------------------------------ | -------------- |
|
|
352
|
+
| `--model` | `-m` | Используемая модель ИИ (sonnet, opus, haiku) | sonnet |
|
|
353
|
+
| `--think` | | Уровень мышления (off, low, medium, high, xhigh, ultra, max) | off |
|
|
354
|
+
| `--base-branch` | `-b` | Целевая ветка для PR | (по умолчанию) |
|
|
354
355
|
|
|
355
356
|
**Другие полезные параметры:**
|
|
356
357
|
|
|
@@ -372,12 +373,12 @@ hive <github-url> [options]
|
|
|
372
373
|
|
|
373
374
|
**Наиболее часто используемые параметры:**
|
|
374
375
|
|
|
375
|
-
| Параметр | Сокр. | Описание
|
|
376
|
-
| -------------- | ----- |
|
|
377
|
-
| `--model` | `-m` | Используемая модель ИИ (sonnet, opus, haiku)
|
|
378
|
-
| `--think` | | Уровень мышления (low, medium, high, max)
|
|
379
|
-
| `--all-issues` | `-a` | Мониторинг всех задач (игнорировать метки)
|
|
380
|
-
| `--once` | | Одиночный запуск (без непрерывного мониторинга)
|
|
376
|
+
| Параметр | Сокр. | Описание | По умолчанию |
|
|
377
|
+
| -------------- | ----- | ------------------------------------------------------------ | ------------ |
|
|
378
|
+
| `--model` | `-m` | Используемая модель ИИ (sonnet, opus, haiku) | sonnet |
|
|
379
|
+
| `--think` | | Уровень мышления (off, low, medium, high, xhigh, ultra, max) | off |
|
|
380
|
+
| `--all-issues` | `-a` | Мониторинг всех задач (игнорировать метки) | false |
|
|
381
|
+
| `--once` | | Одиночный запуск (без непрерывного мониторинга) | false |
|
|
381
382
|
|
|
382
383
|
**Другие полезные параметры:**
|
|
383
384
|
|
package/README.zh.md
CHANGED
|
@@ -173,6 +173,7 @@ claude
|
|
|
173
173
|
# Use /config command and set:
|
|
174
174
|
# Reduce motion true # Will save your ssh trafic, and make Claude Code more responsive (less latency)
|
|
175
175
|
# Thinking mode false # Anthropic models perform better and cheaper without thinking
|
|
176
|
+
# 未指定 `--think` 时,Hive Mind 按 `--think off` 处理。
|
|
176
177
|
# Model haiku # chepear for connection testing manually
|
|
177
178
|
# Claude in Chrome enabled by default false # No need for Chrome support on server
|
|
178
179
|
|
|
@@ -346,11 +347,11 @@ solve <issue-url> [options]
|
|
|
346
347
|
|
|
347
348
|
**最常用选项:**
|
|
348
349
|
|
|
349
|
-
| 选项 | 简写 | 描述
|
|
350
|
-
| --------------- | ---- |
|
|
351
|
-
| `--model` | `-m` | 使用的 AI 模型(sonnet、opus、haiku)
|
|
352
|
-
| `--think` | | 思考级别(low、medium、high、max)
|
|
353
|
-
| `--base-branch` | `-b` | PR 的目标分支
|
|
350
|
+
| 选项 | 简写 | 描述 | 默认值 |
|
|
351
|
+
| --------------- | ---- | ----------------------------------------------------- | -------- |
|
|
352
|
+
| `--model` | `-m` | 使用的 AI 模型(sonnet、opus、haiku) | sonnet |
|
|
353
|
+
| `--think` | | 思考级别(off、low、medium、high、xhigh、ultra、max) | off |
|
|
354
|
+
| `--base-branch` | `-b` | PR 的目标分支 | (默认) |
|
|
354
355
|
|
|
355
356
|
**其他常用选项:**
|
|
356
357
|
|
|
@@ -372,12 +373,12 @@ hive <github-url> [options]
|
|
|
372
373
|
|
|
373
374
|
**最常用选项:**
|
|
374
375
|
|
|
375
|
-
| 选项 | 简写 | 描述
|
|
376
|
-
| -------------- | ---- |
|
|
377
|
-
| `--model` | `-m` | 使用的 AI 模型(sonnet、opus、haiku)
|
|
378
|
-
| `--think` | | 思考级别(low、medium、high、max)
|
|
379
|
-
| `--all-issues` | `-a` | 监控所有 Issue(忽略标签)
|
|
380
|
-
| `--once` | | 单次运行(不持续监控)
|
|
376
|
+
| 选项 | 简写 | 描述 | 默认值 |
|
|
377
|
+
| -------------- | ---- | ----------------------------------------------------- | ------ |
|
|
378
|
+
| `--model` | `-m` | 使用的 AI 模型(sonnet、opus、haiku) | sonnet |
|
|
379
|
+
| `--think` | | 思考级别(off、low、medium、high、xhigh、ultra、max) | off |
|
|
380
|
+
| `--all-issues` | `-a` | 监控所有 Issue(忽略标签) | false |
|
|
381
|
+
| `--once` | | 单次运行(不持续监控) | false |
|
|
381
382
|
|
|
382
383
|
**其他常用选项:**
|
|
383
384
|
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.
|
|
|
8
8
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
9
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
10
10
|
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
11
|
+
import { buildDevelopmentLogPrompt } from './development-log.lib.mjs';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Build the user prompt for Agent
|
|
@@ -66,6 +67,11 @@ export const buildUserPrompt = params => {
|
|
|
66
67
|
promptLines.push('');
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
const developmentLogPrompt = buildDevelopmentLogPrompt({ argv, issueNumber, prNumber }).trim();
|
|
71
|
+
if (developmentLogPrompt) {
|
|
72
|
+
promptLines.push(developmentLogPrompt, '');
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'agent', argv });
|
|
70
76
|
if (thinkingPromptInstruction) {
|
|
71
77
|
promptLines.push(thinkingPromptInstruction);
|
|
@@ -10,6 +10,7 @@ import { primaryModelNames } from './models/index.mjs';
|
|
|
10
10
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
11
11
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
12
12
|
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
13
|
+
import { buildDevelopmentLogPrompt } from './development-log.lib.mjs';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Build the user prompt for Claude
|
|
@@ -79,6 +80,11 @@ export const buildUserPrompt = params => {
|
|
|
79
80
|
promptLines.push('');
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
const developmentLogPrompt = buildDevelopmentLogPrompt({ argv, issueNumber, prNumber }).trim();
|
|
84
|
+
if (developmentLogPrompt) {
|
|
85
|
+
promptLines.push(developmentLogPrompt, '');
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'claude', argv, claudeVersion });
|
|
83
89
|
if (thinkingPromptInstruction) {
|
|
84
90
|
promptLines.push(thinkingPromptInstruction);
|
|
@@ -9,6 +9,7 @@ import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.
|
|
|
9
9
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
10
10
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
11
11
|
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
12
|
+
import { buildDevelopmentLogPrompt } from './development-log.lib.mjs';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Build the user prompt for Codex
|
|
@@ -67,6 +68,11 @@ export const buildUserPrompt = params => {
|
|
|
67
68
|
promptLines.push('');
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
const developmentLogPrompt = buildDevelopmentLogPrompt({ argv, issueNumber, prNumber }).trim();
|
|
72
|
+
if (developmentLogPrompt) {
|
|
73
|
+
promptLines.push(developmentLogPrompt, '');
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'codex', argv });
|
|
71
77
|
if (thinkingPromptInstruction) {
|
|
72
78
|
promptLines.push(thinkingPromptInstruction);
|
package/src/config.lib.mjs
CHANGED
|
@@ -614,6 +614,10 @@ export const getClaudeEnv = (options = {}) => {
|
|
|
614
614
|
|
|
615
615
|
if (effortLevel) {
|
|
616
616
|
env.CLAUDE_CODE_EFFORT_LEVEL = effortLevel;
|
|
617
|
+
} else if (options.thinkLevel === 'off' && adaptiveThinkingOnly) {
|
|
618
|
+
// Issue #2032: adaptive-only models reject/ignore a zero manual-thinking budget.
|
|
619
|
+
// Use the lowest supported effort as the strongest safe best-effort equivalent.
|
|
620
|
+
env.CLAUDE_CODE_EFFORT_LEVEL = 'low';
|
|
617
621
|
}
|
|
618
622
|
}
|
|
619
623
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a once-only finalizer so both the normal and error completion paths can
|
|
3
|
+
* preserve a development log without creating duplicate commits.
|
|
4
|
+
*/
|
|
5
|
+
export const createDevelopmentLogFinalizer = ({ collect, getParams }) => {
|
|
6
|
+
let resultPromise = null;
|
|
7
|
+
return () => {
|
|
8
|
+
if (!resultPromise) resultPromise = Promise.resolve().then(() => collect(getParams()));
|
|
9
|
+
return resultPromise;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const sanitizePathSegment = (value, fallback) => {
|
|
6
|
+
const raw = value === null || value === undefined || value === '' ? fallback : String(value);
|
|
7
|
+
const sanitized = raw.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
8
|
+
return sanitized || fallback;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const stripDotSlash = value => value.replace(/^\.\//, '');
|
|
12
|
+
const toPosixPath = value => value.split(path.sep).join('/');
|
|
13
|
+
const addDotSlash = value => (value.startsWith('./') ? value : `./${value}`);
|
|
14
|
+
|
|
15
|
+
const safeFileName = value => sanitizePathSegment(value, 'session');
|
|
16
|
+
|
|
17
|
+
export const buildDevelopmentLogDirectory = ({ issueNumber, prNumber }) => {
|
|
18
|
+
const issueSegment = sanitizePathSegment(issueNumber, 'unknown');
|
|
19
|
+
const prSegment = sanitizePathSegment(prNumber, 'pending');
|
|
20
|
+
return `./dev/log/issues/${issueSegment}/pulls/${prSegment}`;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const buildCaseStudyDirectory = ({ issueNumber }) => {
|
|
24
|
+
const issueSegment = sanitizePathSegment(issueNumber, 'unknown');
|
|
25
|
+
return `./docs/case-studies/issue-${issueSegment}`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Normalize a GitHub issue type (or label) into one of the buckets the
|
|
29
|
+
// development-log prompt distinguishes. Bug issues get the stronger
|
|
30
|
+
// "download all logs" wording; everything else (feature, task, or an
|
|
31
|
+
// unspecified/unknown type) gets the universal data-collection wording.
|
|
32
|
+
export const isBugIssueType = issueType => {
|
|
33
|
+
if (issueType === null || issueType === undefined) return false;
|
|
34
|
+
const normalized = String(issueType).trim().toLowerCase();
|
|
35
|
+
if (!normalized) return false;
|
|
36
|
+
return normalized === 'bug' || normalized === 'bugs' || normalized.includes('bug') || normalized === 'defect';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// True when the run requested the development log via --development-log
|
|
40
|
+
// (yargs exposes both the camelCase and kebab-case keys).
|
|
41
|
+
export const isDevelopmentLogEnabled = argv => argv?.developmentLog === true || argv?.['development-log'] === true;
|
|
42
|
+
|
|
43
|
+
export const buildDevelopmentLogPrompt = ({ argv, issueNumber, prNumber, issueType }) => {
|
|
44
|
+
if (!(argv?.developmentLog || argv?.['development-log'])) return '';
|
|
45
|
+
|
|
46
|
+
const developmentLogDirectory = buildDevelopmentLogDirectory({ issueNumber, prNumber });
|
|
47
|
+
// Automatic support for issue types: when the issue type is "bug" the
|
|
48
|
+
// instruction asks to download all logs as well; for feature/task issues, or
|
|
49
|
+
// when no issue type is selected, the universal data-collection wording is used.
|
|
50
|
+
const resolvedIssueType = issueType ?? argv?.issueType ?? null;
|
|
51
|
+
const collectionInstruction = isBugIssueType(resolvedIssueType) ? `Download all logs and collect data related about the issue to this repository, make sure we compile that data into the ${developmentLogDirectory} folder.` : `Collect data related about the issue to this repository, make sure we compile that data into the ${developmentLogDirectory} folder.`;
|
|
52
|
+
|
|
53
|
+
return `\n${collectionInstruction}\n`;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Fetch the GitHub issue type (e.g. "Bug", "Feature", "Task") for an issue.
|
|
57
|
+
// Returns null when the type cannot be determined (no type selected, command
|
|
58
|
+
// failure, or non-issue targets). Accepts an injectable command runner so the
|
|
59
|
+
// behavior can be unit tested without hitting the network.
|
|
60
|
+
export const fetchIssueType = async ({ owner, repo, issueNumber, $, log }) => {
|
|
61
|
+
if (!owner || !repo || !issueNumber || typeof $ !== 'function') return null;
|
|
62
|
+
try {
|
|
63
|
+
// eslint-disable-next-line gh-rate-limit/no-direct-gh-exec -- $ is the injected, rate-limit-safe runner (wrapDollarWithGhRetry) passed in by the caller.
|
|
64
|
+
const result = await $`gh issue view ${issueNumber} --repo ${owner}/${repo} --json issueType`;
|
|
65
|
+
if (result?.code && result.code !== 0) return null;
|
|
66
|
+
const stdout = result?.stdout?.toString?.() ?? String(result?.stdout ?? '');
|
|
67
|
+
if (!stdout.trim()) return null;
|
|
68
|
+
const parsed = JSON.parse(stdout);
|
|
69
|
+
const name = parsed?.issueType?.name;
|
|
70
|
+
return name ? String(name) : null;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
await log?.(`ℹ️ Could not determine issue type: ${error.message}`, { verbose: true });
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const fileExists = async filePath => {
|
|
78
|
+
try {
|
|
79
|
+
const stat = await fs.stat(filePath);
|
|
80
|
+
return stat.isFile();
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const copyIfExists = async ({ sourcePath, destinationPath }) => {
|
|
87
|
+
if (!(await fileExists(sourcePath))) return false;
|
|
88
|
+
await fs.copyFile(sourcePath, destinationPath);
|
|
89
|
+
return true;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getClaudeSessionFile = ({ repositoryPath, sessionId, homeDir }) => {
|
|
93
|
+
if (!repositoryPath || !sessionId || !homeDir) return null;
|
|
94
|
+
const projectDirName = repositoryPath.replace(/\//g, '-');
|
|
95
|
+
return path.join(homeDir, '.claude', 'projects', projectDirName, `${sessionId}.jsonl`);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Codex CLI stores its transcript ("rollout") under
|
|
99
|
+
// ~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<sessionId>.jsonl. The date
|
|
100
|
+
// path and timestamp are not derivable from the sessionId, so locate the file
|
|
101
|
+
// by recursively matching the sessionId suffix instead.
|
|
102
|
+
const findCodexSessionFile = async ({ sessionId, homeDir }) => {
|
|
103
|
+
if (!sessionId || !homeDir) return null;
|
|
104
|
+
const sessionsRoot = path.join(homeDir, '.codex', 'sessions');
|
|
105
|
+
try {
|
|
106
|
+
const entries = await fs.readdir(sessionsRoot, { recursive: true });
|
|
107
|
+
const match = entries.find(entry => typeof entry === 'string' && entry.includes('rollout-') && entry.endsWith(`-${sessionId}.jsonl`));
|
|
108
|
+
return match ? path.join(sessionsRoot, match) : null;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const copyKnownSessionFiles = async ({ repositoryPath, sessionRelativeDirectory, logFile, sessionId, tool, homeDir }) => {
|
|
115
|
+
if (!sessionId) return [];
|
|
116
|
+
|
|
117
|
+
const sessionDirectory = path.join(repositoryPath, sessionRelativeDirectory);
|
|
118
|
+
const candidates = [];
|
|
119
|
+
const logDirectory = logFile ? path.dirname(logFile) : null;
|
|
120
|
+
|
|
121
|
+
if (logDirectory) {
|
|
122
|
+
candidates.push({
|
|
123
|
+
sourcePath: path.join(logDirectory, `${sessionId}.log`),
|
|
124
|
+
destinationName: `${tool || 'tool'}-${sessionId}.log`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (tool === 'claude') {
|
|
129
|
+
const claudeSessionFile = getClaudeSessionFile({ repositoryPath, sessionId, homeDir });
|
|
130
|
+
if (claudeSessionFile) {
|
|
131
|
+
candidates.push({
|
|
132
|
+
sourcePath: claudeSessionFile,
|
|
133
|
+
destinationName: `claude-${sessionId}.jsonl`,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (tool === 'codex') {
|
|
139
|
+
const codexSessionFile = await findCodexSessionFile({ sessionId, homeDir });
|
|
140
|
+
if (codexSessionFile) {
|
|
141
|
+
candidates.push({
|
|
142
|
+
sourcePath: codexSessionFile,
|
|
143
|
+
destinationName: `codex-${sessionId}.jsonl`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const copied = [];
|
|
149
|
+
const seenSources = new Set();
|
|
150
|
+
for (const candidate of candidates) {
|
|
151
|
+
if (!candidate.sourcePath || seenSources.has(candidate.sourcePath)) continue;
|
|
152
|
+
seenSources.add(candidate.sourcePath);
|
|
153
|
+
|
|
154
|
+
const relativePath = `${sessionRelativeDirectory}/${safeFileName(candidate.destinationName)}`;
|
|
155
|
+
const copiedPath = path.join(sessionDirectory, safeFileName(candidate.destinationName));
|
|
156
|
+
if (await copyIfExists({ sourcePath: candidate.sourcePath, destinationPath: copiedPath })) {
|
|
157
|
+
copied.push(addDotSlash(toPosixPath(relativePath)));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return copied;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const writeDevelopmentLogArtifacts = async ({ repositoryPath, logFile, issueNumber, prNumber, tool, sessionId, branchName, rawCommand, now = new Date(), homeDir = os.homedir() }) => {
|
|
165
|
+
if (!repositoryPath) {
|
|
166
|
+
throw new Error('repositoryPath is required to write development-log artifacts');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const developmentLogDirectory = buildDevelopmentLogDirectory({ issueNumber, prNumber });
|
|
170
|
+
const caseStudyDirectory = buildCaseStudyDirectory({ issueNumber });
|
|
171
|
+
const relativeDirectory = stripDotSlash(developmentLogDirectory);
|
|
172
|
+
const timestamp = now.toISOString().replace(/[:.]/g, '-');
|
|
173
|
+
const sessionDirectoryName = safeFileName(sessionId || `run-${timestamp}`);
|
|
174
|
+
const sessionRelativeDirectory = `${relativeDirectory}/sessions/${sessionDirectoryName}`;
|
|
175
|
+
const sessionDirectory = path.join(repositoryPath, sessionRelativeDirectory);
|
|
176
|
+
|
|
177
|
+
await fs.mkdir(sessionDirectory, { recursive: true });
|
|
178
|
+
|
|
179
|
+
let copiedLogRelativePath = null;
|
|
180
|
+
if (logFile) {
|
|
181
|
+
copiedLogRelativePath = `${sessionRelativeDirectory}/solve.log`;
|
|
182
|
+
await fs.copyFile(logFile, path.join(repositoryPath, copiedLogRelativePath));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const sessionFiles = await copyKnownSessionFiles({
|
|
186
|
+
repositoryPath,
|
|
187
|
+
sessionRelativeDirectory,
|
|
188
|
+
logFile,
|
|
189
|
+
sessionId,
|
|
190
|
+
tool,
|
|
191
|
+
homeDir,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const metadataRelativePath = `${sessionRelativeDirectory}/metadata.json`;
|
|
195
|
+
const metadata = {
|
|
196
|
+
schemaVersion: 2,
|
|
197
|
+
collectedAt: now.toISOString(),
|
|
198
|
+
issueNumber: issueNumber ?? null,
|
|
199
|
+
prNumber: prNumber ?? null,
|
|
200
|
+
branchName: branchName || null,
|
|
201
|
+
tool: tool || null,
|
|
202
|
+
sessionId: sessionId || null,
|
|
203
|
+
rawCommand: rawCommand || null,
|
|
204
|
+
developmentLogDirectory,
|
|
205
|
+
caseStudyDirectory,
|
|
206
|
+
artifacts: {
|
|
207
|
+
solveLog: copiedLogRelativePath ? addDotSlash(toPosixPath(copiedLogRelativePath)) : null,
|
|
208
|
+
sessionFiles,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
await fs.writeFile(path.join(repositoryPath, metadataRelativePath), `${JSON.stringify(metadata, null, 2)}\n`, 'utf8');
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
developmentLogDirectory,
|
|
216
|
+
caseStudyDirectory,
|
|
217
|
+
relativeDirectory,
|
|
218
|
+
sessionRelativeDirectory,
|
|
219
|
+
copiedLogRelativePath: copiedLogRelativePath ? toPosixPath(copiedLogRelativePath) : null,
|
|
220
|
+
metadataRelativePath: toPosixPath(metadataRelativePath),
|
|
221
|
+
sessionFiles,
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const getCommandOutput = result => (result?.stderr?.toString?.() || result?.stdout?.toString?.() || '').trim();
|
|
226
|
+
|
|
227
|
+
export const collectAndCommitDevelopmentLogArtifacts = async ({ enabled, repositoryPath, logFile, issueNumber, prNumber, tool, sessionId, branchName, rawCommand, $, log }) => {
|
|
228
|
+
if (!enabled) {
|
|
229
|
+
return { skipped: 'disabled' };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!repositoryPath) {
|
|
233
|
+
await log?.('⚠️ Development log requested but no repository path is available', { level: 'warning' });
|
|
234
|
+
return { skipped: 'missing-repository-path' };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const artifacts = await writeDevelopmentLogArtifacts({
|
|
239
|
+
repositoryPath,
|
|
240
|
+
logFile,
|
|
241
|
+
issueNumber,
|
|
242
|
+
prNumber,
|
|
243
|
+
tool,
|
|
244
|
+
sessionId,
|
|
245
|
+
branchName,
|
|
246
|
+
rawCommand,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
await log?.(`🧾 Development log artifacts written to ${artifacts.developmentLogDirectory}`);
|
|
250
|
+
|
|
251
|
+
if (!$) {
|
|
252
|
+
return { ...artifacts, committed: false, pushed: false };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const addResult = await $({ cwd: repositoryPath })`git add -f -- ${artifacts.relativeDirectory}`;
|
|
256
|
+
if (addResult.code !== 0) {
|
|
257
|
+
await log?.(`⚠️ Could not stage development log: ${getCommandOutput(addResult)}`, { level: 'warning' });
|
|
258
|
+
return { ...artifacts, committed: false, pushed: false };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const diffResult = await $({ cwd: repositoryPath })`git diff --cached --quiet -- ${artifacts.relativeDirectory}`;
|
|
262
|
+
if (diffResult.code === 0) {
|
|
263
|
+
await log?.('ℹ️ Development log artifacts already committed');
|
|
264
|
+
return { ...artifacts, committed: false, pushed: false };
|
|
265
|
+
}
|
|
266
|
+
if (diffResult.code !== 1) {
|
|
267
|
+
await log?.(`⚠️ Could not inspect staged development log changes: ${getCommandOutput(diffResult)}`, { level: 'warning' });
|
|
268
|
+
return { ...artifacts, committed: false, pushed: false };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const commitMessage = prNumber ? `Add development log for issue #${issueNumber} PR #${prNumber}` : `Add development log for issue #${issueNumber}`;
|
|
272
|
+
const commitResult = await $({ cwd: repositoryPath })`git commit -m ${commitMessage} -- ${artifacts.relativeDirectory}`;
|
|
273
|
+
if (commitResult.code !== 0) {
|
|
274
|
+
await log?.(`⚠️ Could not commit development log: ${getCommandOutput(commitResult)}`, { level: 'warning' });
|
|
275
|
+
return { ...artifacts, committed: false, pushed: false };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await log?.('✅ Development log committed');
|
|
279
|
+
|
|
280
|
+
if (!branchName) {
|
|
281
|
+
await log?.('ℹ️ Development log committed locally; no branch name available for push');
|
|
282
|
+
return { ...artifacts, committed: true, pushed: false };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const pushResult = await $({ cwd: repositoryPath })`git push origin ${branchName}`;
|
|
286
|
+
if (pushResult.code !== 0) {
|
|
287
|
+
await log?.(`⚠️ Could not push development log commit: ${getCommandOutput(pushResult)}`, { level: 'warning' });
|
|
288
|
+
return { ...artifacts, committed: true, pushed: false };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
await log?.('✅ Development log pushed');
|
|
292
|
+
return { ...artifacts, committed: true, pushed: true };
|
|
293
|
+
} catch (error) {
|
|
294
|
+
await log?.(`⚠️ Development log collection failed: ${error.message}`, { level: 'warning' });
|
|
295
|
+
return { skipped: 'error', error };
|
|
296
|
+
}
|
|
297
|
+
};
|
|
@@ -8,6 +8,7 @@ import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.
|
|
|
8
8
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
9
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
10
10
|
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
11
|
+
import { buildDevelopmentLogPrompt } from './development-log.lib.mjs';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Build the user prompt for Gemini
|
|
@@ -57,6 +58,11 @@ export const buildUserPrompt = params => {
|
|
|
57
58
|
promptLines.push('');
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
const developmentLogPrompt = buildDevelopmentLogPrompt({ argv, issueNumber, prNumber }).trim();
|
|
62
|
+
if (developmentLogPrompt) {
|
|
63
|
+
promptLines.push(developmentLogPrompt, '');
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'gemini', argv });
|
|
61
67
|
if (thinkingPromptInstruction) {
|
|
62
68
|
promptLines.push(thinkingPromptInstruction);
|
package/src/lib.mjs
CHANGED
|
@@ -937,6 +937,19 @@ export default {
|
|
|
937
937
|
setupStdioLogInterceptor,
|
|
938
938
|
};
|
|
939
939
|
|
|
940
|
+
// Issue #1596: log the solve startup banner (version + raw command) and return
|
|
941
|
+
// the raw command string for reuse. Extracted from solve.mjs to keep that file
|
|
942
|
+
// under the 1500-line limit enforced by scripts/check-file-line-limits.sh.
|
|
943
|
+
export const logSolveStartup = async versionInfo => {
|
|
944
|
+
const rawCommand = process.argv.join(' ');
|
|
945
|
+
await log('');
|
|
946
|
+
await log(`🚀 solve v${versionInfo}`);
|
|
947
|
+
await log('🔧 Raw command executed:');
|
|
948
|
+
await log(` ${rawCommand}`);
|
|
949
|
+
await log('');
|
|
950
|
+
return rawCommand;
|
|
951
|
+
};
|
|
952
|
+
|
|
940
953
|
/**
|
|
941
954
|
* Get version information for logging
|
|
942
955
|
* @returns {Promise<string>} Version string
|
|
@@ -8,6 +8,7 @@ import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.
|
|
|
8
8
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
9
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
10
10
|
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
11
|
+
import { buildDevelopmentLogPrompt } from './development-log.lib.mjs';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Build the user prompt for OpenCode
|
|
@@ -66,6 +67,11 @@ export const buildUserPrompt = params => {
|
|
|
66
67
|
promptLines.push('');
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
const developmentLogPrompt = buildDevelopmentLogPrompt({ argv, issueNumber, prNumber }).trim();
|
|
71
|
+
if (developmentLogPrompt) {
|
|
72
|
+
promptLines.push(developmentLogPrompt, '');
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'opencode', argv });
|
|
70
76
|
if (thinkingPromptInstruction) {
|
|
71
77
|
promptLines.push(thinkingPromptInstruction);
|
package/src/qwen.prompts.lib.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.
|
|
|
8
8
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
9
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
10
10
|
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
11
|
+
import { buildDevelopmentLogPrompt } from './development-log.lib.mjs';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Build the user prompt for Qwen Code
|
|
@@ -57,6 +58,11 @@ export const buildUserPrompt = params => {
|
|
|
57
58
|
promptLines.push('');
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
const developmentLogPrompt = buildDevelopmentLogPrompt({ argv, issueNumber, prNumber }).trim();
|
|
62
|
+
if (developmentLogPrompt) {
|
|
63
|
+
promptLines.push(developmentLogPrompt, '');
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool, argv });
|
|
61
67
|
if (thinkingPromptInstruction) {
|
|
62
68
|
promptLines.push(thinkingPromptInstruction);
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -299,9 +299,9 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
299
299
|
},
|
|
300
300
|
think: {
|
|
301
301
|
type: 'string',
|
|
302
|
-
description: 'Thinking level hint. For Claude, translated to --thinking-budget for Claude Code >= 2.1.12 (off=0, low=~8000, medium=~16000, high=~24000, xhigh/ultra/max=31999) and to CLAUDE_CODE_EFFORT_LEVEL when supported. Fable 5/Mythos 5/Sonnet 5/Opus 4.8/4.7 support xhigh and max; Opus 4.6/Sonnet 4.6/Mythos Preview support max; Opus 4.5 uses high for xhigh/max. `ultra` maps to the highest supported Claude effort (Claude "ultracode"-class reasoning). For Codex (GPT-5.6 Sol), mapped 1:1 to reasoning effort (off=none, low=low, medium=medium, high=high, xhigh=xhigh, ultra=ultra, max=max); GPT-5.6 keeps xhigh and adds max above it, and ultra runs the multi-agent mode paired with a rollout token budget cap. Default:
|
|
302
|
+
description: 'Thinking level hint. For Claude, translated to --thinking-budget for Claude Code >= 2.1.12 (off=0, low=~8000, medium=~16000, high=~24000, xhigh/ultra/max=31999) and to CLAUDE_CODE_EFFORT_LEVEL when supported. Adaptive-only models that cannot disable thinking use their lowest effort for off. Fable 5/Mythos 5/Sonnet 5/Opus 4.8/4.7 support xhigh and max; Opus 4.6/Sonnet 4.6/Mythos Preview support max; Opus 4.5 uses high for xhigh/max. `ultra` maps to the highest supported Claude effort (Claude "ultracode"-class reasoning). For Codex (GPT-5.6 Sol), mapped 1:1 to reasoning effort (off=none, low=low, medium=medium, high=high, xhigh=xhigh, ultra=ultra, max=max); GPT-5.6 keeps xhigh and adds max above it, and ultra runs the multi-agent mode paired with a rollout token budget cap. Default: off.',
|
|
303
303
|
choices: ['off', 'low', 'medium', 'high', 'xhigh', 'ultra', 'max'],
|
|
304
|
-
default:
|
|
304
|
+
default: 'off',
|
|
305
305
|
},
|
|
306
306
|
'thinking-budget': {
|
|
307
307
|
type: 'number',
|
|
@@ -504,6 +504,11 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
504
504
|
description: 'Create comprehensive case study documentation for the issue including logs, analysis, timeline, root cause investigation, and proposed solutions. Organizes findings into ./docs/case-studies/issue-{id}/ directory. Supported for --tool claude and --tool codex.',
|
|
505
505
|
default: false,
|
|
506
506
|
},
|
|
507
|
+
'development-log': {
|
|
508
|
+
type: 'boolean',
|
|
509
|
+
description: 'Prompt for issue-data collection under ./dev/log/issues/{issue-id}/pulls/{pull-id}, preserve native tool state under sessions/{UUID}, and commit the artifacts when solve finishes. Supported for --tool claude, --tool codex, --tool opencode, --tool agent, --tool qwen, and --tool gemini.',
|
|
510
|
+
default: false,
|
|
511
|
+
},
|
|
507
512
|
'use-handoff': {
|
|
508
513
|
type: 'boolean',
|
|
509
514
|
description: '[EXPERIMENTAL] Enable the HANDOFF.md continuity Agent Skill so a session can continue the work of a previous session — even when a different AI tool is used (e.g. Claude and Codex continuing each other in the same pull request). A real SKILL.md (the open Agent Skills standard) is deployed into the working directory so each tool loads it natively (.claude/skills/handoff/ for Claude, .agents/skills/handoff/ for Codex). The AI reads HANDOFF.md (repository root) first when present and keeps it updated with task, current state, decisions, next steps, gotchas, and critical files. HANDOFF.md is committed to the PR branch so it persists across the ephemeral per-session working directories; the SKILL.md itself is re-deployed each session and git-excluded so it never pollutes the PR. The same skill file is used identically for --tool claude and --tool codex. Disabled by default (issue #1877).',
|
|
@@ -838,6 +843,14 @@ export const parseArguments = async (yargs = getLinoYargsFactory(), hideBinFn =
|
|
|
838
843
|
const modelExplicitlyProvided = rawArgs.includes('--model') || rawArgs.includes('-m') || rawArgs.includes('--worker-model');
|
|
839
844
|
const fallbackModelExplicitlyProvided = rawArgs.includes('--fallback-model');
|
|
840
845
|
const planModelExplicitlyProvided = rawArgs.includes('--plan-model');
|
|
846
|
+
const thinkExplicitlyProvided = hasRawOption(rawArgs, '--think');
|
|
847
|
+
const thinkingBudgetExplicitlyProvided = hasRawOption(rawArgs, '--thinking-budget');
|
|
848
|
+
|
|
849
|
+
// Issue #2032: an omitted --think is equivalent to --think off, except when
|
|
850
|
+
// an explicit token budget is the user's chosen reasoning control.
|
|
851
|
+
if (!thinkExplicitlyProvided && thinkingBudgetExplicitlyProvided) {
|
|
852
|
+
argv.think = undefined;
|
|
853
|
+
}
|
|
841
854
|
|
|
842
855
|
// --plan flag expansion (Issue #1223)
|
|
843
856
|
// When --plan is set, it acts as a shortcut for --plan-model opus --worker-model sonnet
|
package/src/solve.mjs
CHANGED
|
@@ -21,7 +21,7 @@ const fs = (await use('fs')).promises;
|
|
|
21
21
|
const crypto = (await use('crypto')).default;
|
|
22
22
|
const memoryCheck = await import('./memory-check.mjs');
|
|
23
23
|
const lib = await import('./lib.mjs');
|
|
24
|
-
const { log, setLogFile, getLogFile, getAbsoluteLogPath, cleanErrorMessage, formatAligned, formatToolExecutionFailure, getVersionInfo, setupVerboseLogInterceptor, setupStdioLogInterceptor } = lib;
|
|
24
|
+
const { log, setLogFile, getLogFile, getAbsoluteLogPath, cleanErrorMessage, formatAligned, formatToolExecutionFailure, getVersionInfo, logSolveStartup, setupVerboseLogInterceptor, setupStdioLogInterceptor } = lib;
|
|
25
25
|
const githubLib = await import('./github.lib.mjs');
|
|
26
26
|
const { sanitizeLogContent, attachLogToGitHub, getToolDisplayName } = githubLib;
|
|
27
27
|
const validation = await import('./solve.validation.lib.mjs');
|
|
@@ -62,6 +62,8 @@ const { recordAfterCloneSize, recordAfterAgentSize } = await import('./solve.dis
|
|
|
62
62
|
const { createOrCheckoutBranch } = await import('./solve.branch.lib.mjs');
|
|
63
63
|
const { startWorkSession, endWorkSession, SESSION_TYPES } = await import('./solve.session.lib.mjs');
|
|
64
64
|
const { attachFinalLogIfMissing } = await import('./attach-logs-guarantee.lib.mjs'); // Issue #1952
|
|
65
|
+
const { collectAndCommitDevelopmentLogArtifacts, fetchIssueType, isDevelopmentLogEnabled } = await import('./development-log.lib.mjs');
|
|
66
|
+
const { createDevelopmentLogFinalizer } = await import('./development-log.finalize.lib.mjs');
|
|
65
67
|
// Issue #1625: centralized markers + tracked comment posting for solve.mjs's
|
|
66
68
|
// own usage-limit notifications (so they're excluded from the
|
|
67
69
|
// "did the AI post anything?" check in --auto-attach-solution-summary).
|
|
@@ -72,12 +74,7 @@ const { autoAcceptInviteForRepo } = await import('./solve.accept-invite.lib.mjs'
|
|
|
72
74
|
const { handleAutoForkOption, handleMaintainerForkAccess } = await import('./solve.fork-detection.lib.mjs');
|
|
73
75
|
const logFile = await initializeLogFile(null);
|
|
74
76
|
const versionInfo = await getVersionInfo();
|
|
75
|
-
await
|
|
76
|
-
await log(`🚀 solve v${versionInfo}`);
|
|
77
|
-
const rawCommand = process.argv.join(' ');
|
|
78
|
-
await log('🔧 Raw command executed:');
|
|
79
|
-
await log(` ${rawCommand}`);
|
|
80
|
-
await log('');
|
|
77
|
+
const rawCommand = await logSolveStartup(versionInfo);
|
|
81
78
|
|
|
82
79
|
let finalResourceSnapshotRecorded = false;
|
|
83
80
|
const safeExit = async (code = 0, reason = 'Process completed', options = {}) => {
|
|
@@ -489,6 +486,8 @@ if (isPrUrl) {
|
|
|
489
486
|
}
|
|
490
487
|
// Issues #1212, #1462: Store issueNumber globally for error handlers (attach failure logs to issue when no PR exists)
|
|
491
488
|
global.issueNumber = issueNumber;
|
|
489
|
+
// Issue #1596: detect the issue type so the development-log prompt automatically uses bug vs feature/task wording.
|
|
490
|
+
if (isDevelopmentLogEnabled(argv) && issueNumber) argv.issueType = await fetchIssueType({ owner, repo, issueNumber, $, log });
|
|
492
491
|
const workspaceInfo = argv.enableWorkspaces ? { owner, repo, issueNumber } : null;
|
|
493
492
|
const { tempDir, workspaceTmpDir, needsClone } = await setupTempDirectory(argv, workspaceInfo);
|
|
494
493
|
cleanupContext.tempDir = tempDir;
|
|
@@ -497,6 +496,12 @@ cleanupContext.owner = owner;
|
|
|
497
496
|
cleanupContext.repo = repo;
|
|
498
497
|
if (prNumber) cleanupContext.prNumber = prNumber;
|
|
499
498
|
let limitReached = false;
|
|
499
|
+
let sessionId = null;
|
|
500
|
+
let branchName = null;
|
|
501
|
+
const finalizeDevelopmentLog = createDevelopmentLogFinalizer({
|
|
502
|
+
collect: collectAndCommitDevelopmentLogArtifacts,
|
|
503
|
+
getParams: () => ({ enabled: isDevelopmentLogEnabled(argv), repositoryPath: tempDir, logFile: getLogFile(), issueNumber, prNumber, tool: argv.tool || 'claude', sessionId, branchName, rawCommand, $, log }), // prettier-ignore
|
|
504
|
+
});
|
|
500
505
|
try {
|
|
501
506
|
// Set up repository and clone using the new module
|
|
502
507
|
// If --working-directory points to existing repo, needsClone is false and we skip cloning
|
|
@@ -531,7 +536,7 @@ try {
|
|
|
531
536
|
issueUrl,
|
|
532
537
|
});
|
|
533
538
|
// Create or checkout branch using the new module
|
|
534
|
-
|
|
539
|
+
branchName = await createOrCheckoutBranch({
|
|
535
540
|
isContinueMode,
|
|
536
541
|
prBranch,
|
|
537
542
|
issueNumber,
|
|
@@ -861,7 +866,7 @@ try {
|
|
|
861
866
|
}
|
|
862
867
|
|
|
863
868
|
const { success } = toolResult;
|
|
864
|
-
|
|
869
|
+
sessionId = toolResult.sessionId;
|
|
865
870
|
let anthropicTotalCostUSD = toolResult.anthropicTotalCostUSD;
|
|
866
871
|
let publicPricingEstimate = toolResult.publicPricingEstimate; // Used by agent tool
|
|
867
872
|
let pricingInfo = toolResult.pricingInfo; // Used by agent tool for detailed pricing
|
|
@@ -1461,17 +1466,10 @@ try {
|
|
|
1461
1466
|
// Issue #1516: Cleanup after all signals (was before verifyResults, caused premature commits)
|
|
1462
1467
|
await cleanupClaudeFile(tempDir, branchName, claudeCommitHash, argv);
|
|
1463
1468
|
|
|
1464
|
-
//
|
|
1465
|
-
await endWorkSession({
|
|
1466
|
-
isContinueMode,
|
|
1467
|
-
prNumber,
|
|
1468
|
-
argv,
|
|
1469
|
-
log,
|
|
1470
|
-
formatAligned,
|
|
1471
|
-
$,
|
|
1472
|
-
logsAttached,
|
|
1473
|
-
});
|
|
1469
|
+
await finalizeDevelopmentLog(); // Issue #1596: preserve session before ending work.
|
|
1470
|
+
await endWorkSession({ isContinueMode, prNumber, argv, log, formatAligned, $, logsAttached });
|
|
1474
1471
|
} catch (error) {
|
|
1472
|
+
await finalizeDevelopmentLog(); // Preserve failed/interrupted sessions too.
|
|
1475
1473
|
// Don't report authentication errors to Sentry as they are user configuration issues
|
|
1476
1474
|
if (!error.isAuthError) {
|
|
1477
1475
|
reportError(error, {
|