ai-git-tools 2.0.81 → 2.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/README.md
CHANGED
|
@@ -238,7 +238,7 @@ npx ai-git-tools model-info [選項]
|
|
|
238
238
|
|
|
239
239
|
\`\`\`bash
|
|
240
240
|
npx ai-git-tools model-info
|
|
241
|
-
npx ai-git-tools model-info --model
|
|
241
|
+
npx ai-git-tools model-info --model claude-haiku-4.5
|
|
242
242
|
npx ai-git-tools model-info --filter claude
|
|
243
243
|
npx ai-git-tools model-info --json
|
|
244
244
|
\`\`\`
|
|
@@ -253,7 +253,7 @@ npx ai-git-tools model-info --json
|
|
|
253
253
|
export default {
|
|
254
254
|
// AI 設定
|
|
255
255
|
ai: {
|
|
256
|
-
model: '
|
|
256
|
+
model: 'claude-haiku-4.5', // AI 模型
|
|
257
257
|
maxDiffLength: 8000, // 最大 diff 長度
|
|
258
258
|
maxRetries: 3, // 失敗重試次數
|
|
259
259
|
},
|
|
@@ -449,7 +449,7 @@ A: 需要。本工具使用 GitHub Copilot SDK,需要有 GitHub Copilot 訂閱
|
|
|
449
449
|
|
|
450
450
|
### Q: 可以使用其他 AI 模型嗎?
|
|
451
451
|
|
|
452
|
-
A: 可以。支援
|
|
452
|
+
A: 可以。支援Claude Haiku 4.5、Claude Sonnet 4.5 等模型,可在配置檔中設定。
|
|
453
453
|
|
|
454
454
|
### Q: 配置檔一定要放在專案根目錄嗎?
|
|
455
455
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-git-tools",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "AI-powered Git automation tools for commit messages and PR generation",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"ai-git-tools": "./bin/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "
|
|
11
|
+
"test": "node --test test/*.test.js",
|
|
12
12
|
"prepare": "chmod +x bin/cli.js",
|
|
13
13
|
"lint": "eslint src/**/*.js",
|
|
14
14
|
"format": "prettier --write \"src/**/*.js\" \"bin/**/*.js\"",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
"CHANGELOG.md"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@github/copilot-sdk": "^0.
|
|
43
|
-
"commander": "^12.0.0",
|
|
42
|
+
"@github/copilot-sdk": "^1.0.11",
|
|
44
43
|
"chalk": "^5.3.0",
|
|
45
|
-
"
|
|
46
|
-
"inquirer": "^9.2.0"
|
|
44
|
+
"commander": "^12.0.0",
|
|
45
|
+
"inquirer": "^9.2.0",
|
|
46
|
+
"ora": "^8.0.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"eslint": "^8.57.0",
|
package/src/commands/init.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
filterModels,
|
|
13
13
|
formatPrice,
|
|
14
14
|
formatTokenCount,
|
|
15
|
+
getTokenPrices,
|
|
15
16
|
} from '../data/copilot-models.js';
|
|
16
17
|
|
|
17
18
|
const logger = new Logger();
|
|
@@ -38,6 +39,10 @@ function formatCapabilities(supports = {}) {
|
|
|
38
39
|
if (supports.reasoningEffort || supports.reasoning_effort) caps.push('reasoning');
|
|
39
40
|
if (supports.streaming) caps.push('streaming');
|
|
40
41
|
if (supports.parallel_tool_calls) caps.push('parallel-tools');
|
|
42
|
+
if (supports.structured_outputs) caps.push('structured-output');
|
|
43
|
+
if (supports.adaptive_thinking && supports.adaptive_thinking !== 'unsupported') {
|
|
44
|
+
caps.push('adaptive-thinking');
|
|
45
|
+
}
|
|
41
46
|
return caps.length > 0 ? caps.join('、') : '—';
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -64,33 +69,49 @@ function policyStateLabel(state) {
|
|
|
64
69
|
* @param {object} model
|
|
65
70
|
*/
|
|
66
71
|
function printModel(model) {
|
|
67
|
-
const billing = model.billing
|
|
72
|
+
const billing = model.tokenPrices || getTokenPrices(model.billing);
|
|
73
|
+
const capabilities = model.capabilities || {};
|
|
68
74
|
|
|
69
75
|
console.log(`\n ${logger.colors?.cyan ?? ''}${model.id}${logger.colors?.reset ?? ''}`);
|
|
70
76
|
console.log(' ─────────────────────────────────────────────────────────────');
|
|
71
77
|
console.log(` 名稱: ${model.name}`);
|
|
72
78
|
console.log(` 供應商: ${model.provider}`);
|
|
79
|
+
console.log(` 模型家族: ${capabilities.family || '—'}`);
|
|
80
|
+
console.log(` 模型類型: ${capabilities.type || '—'}`);
|
|
81
|
+
console.log(` Tokenizer: ${capabilities.tokenizer || '—'}`);
|
|
82
|
+
console.log(` 模型分類: ${model.modelPickerCategory || '—'}`);
|
|
83
|
+
console.log(` 價格分類: ${model.modelPickerPriceCategory || '—'}`);
|
|
73
84
|
console.log(` 描述: ${model.description}`);
|
|
74
85
|
console.log(` 上下文: ${model.contextWindow}`);
|
|
75
86
|
console.log(` 最大輸出: ${model.maxOutputTokens}`);
|
|
76
87
|
console.log(` 建議用途: ${formatRecommendedFor(model.recommendedFor)}`);
|
|
77
88
|
console.log(` 速度: ${model.speed}`);
|
|
78
89
|
console.log(` 狀態: ${policyStateLabel(model.policy?.state)}`);
|
|
79
|
-
console.log(` 能力: ${formatCapabilities(
|
|
90
|
+
console.log(` 能力: ${formatCapabilities(capabilities.supports)}`);
|
|
80
91
|
if (billing && billing.batch_size > 0) {
|
|
81
92
|
const hasAnyPrice =
|
|
82
93
|
billing.input_price > 0 || billing.output_price > 0 || billing.cache_price > 0;
|
|
83
94
|
if (hasAnyPrice) {
|
|
84
95
|
console.log(` 價格(預估):`);
|
|
85
96
|
console.log(
|
|
86
|
-
` 輸入: ${formatPrice(billing.input_price, billing.batch_size)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
97
|
+
` 輸入: ${formatPrice(billing.input_price, billing.batch_size, billing.price_scale)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
87
98
|
);
|
|
88
99
|
console.log(
|
|
89
|
-
` 輸出: ${formatPrice(billing.output_price, billing.batch_size)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
100
|
+
` 輸出: ${formatPrice(billing.output_price, billing.batch_size, billing.price_scale)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
90
101
|
);
|
|
91
102
|
if (billing.cache_price !== undefined) {
|
|
92
103
|
console.log(
|
|
93
|
-
` 快取: ${formatPrice(billing.cache_price, billing.batch_size)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
104
|
+
` 快取: ${formatPrice(billing.cache_price, billing.batch_size, billing.price_scale)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (billing.cache_read_price !== undefined) {
|
|
108
|
+
console.log(
|
|
109
|
+
` 快取讀取: ${formatPrice(billing.cache_read_price, billing.batch_size, billing.price_scale)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if (billing.cache_write_price !== undefined) {
|
|
113
|
+
console.log(
|
|
114
|
+
` 快取寫入: ${formatPrice(billing.cache_write_price, billing.batch_size, billing.price_scale)} / ${formatTokenCount(billing.batch_size)} tokens`
|
|
94
115
|
);
|
|
95
116
|
}
|
|
96
117
|
}
|
package/src/core/ai-client.js
CHANGED
|
@@ -29,7 +29,7 @@ export class AIClient {
|
|
|
29
29
|
/**
|
|
30
30
|
* 發送 prompt 並等待回應(帶重試機制和超時保護)
|
|
31
31
|
*/
|
|
32
|
-
static async sendAndWait(prompt, model = '
|
|
32
|
+
static async sendAndWait(prompt, model = 'claude-haiku-4.5', maxRetries = 3, timeout = 150000) {
|
|
33
33
|
let lastError = null;
|
|
34
34
|
|
|
35
35
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -12,7 +12,7 @@ import { resolve } from 'path';
|
|
|
12
12
|
*/
|
|
13
13
|
const DEFAULT_CONFIG = {
|
|
14
14
|
ai: {
|
|
15
|
-
model: '
|
|
15
|
+
model: 'claude-haiku-4.5',
|
|
16
16
|
maxDiffLength: 8000,
|
|
17
17
|
maxRetries: 3,
|
|
18
18
|
},
|
|
@@ -193,7 +193,7 @@ function showHelp() {
|
|
|
193
193
|
|
|
194
194
|
選項:
|
|
195
195
|
--base <branch> 指定目標分支 (預設: 使用配置檔的 defaultBase 或自動偵測)
|
|
196
|
-
--model <model> 指定 AI 模型 (預設:
|
|
196
|
+
--model <model> 指定 AI 模型 (預設: claude-haiku-4.5)
|
|
197
197
|
--preview 僅預覽 PR 內容,不實際創建
|
|
198
198
|
--no-confirm 跳過確認直接創建
|
|
199
199
|
--auto-labels 自動添加 Labels (預設啟用)
|
|
@@ -13,6 +13,7 @@ const MODEL_ENHANCEMENTS = {};
|
|
|
13
13
|
export function inferProvider(modelId) {
|
|
14
14
|
if (modelId.startsWith('claude')) return 'Anthropic';
|
|
15
15
|
if (modelId.startsWith('gpt')) return 'OpenAI';
|
|
16
|
+
if (modelId.startsWith('gemini')) return 'Google';
|
|
16
17
|
return 'Unknown';
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -44,6 +45,30 @@ export function getModelEnhancement(modelId) {
|
|
|
44
45
|
);
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* 正規化 Copilot SDK 的價格欄位
|
|
50
|
+
* @param {object} billing
|
|
51
|
+
* @returns {object | null}
|
|
52
|
+
*/
|
|
53
|
+
export function getTokenPrices(billing = {}) {
|
|
54
|
+
const prices = billing.tokenPrices || billing.token_prices;
|
|
55
|
+
if (!prices) return null;
|
|
56
|
+
|
|
57
|
+
const isCurrentFormat = Boolean(billing.tokenPrices);
|
|
58
|
+
return {
|
|
59
|
+
input_price: prices.inputPrice ?? prices.input_price,
|
|
60
|
+
output_price: prices.outputPrice ?? prices.output_price,
|
|
61
|
+
cache_price: prices.cachePrice ?? prices.cache_price,
|
|
62
|
+
cache_read_price: prices.cacheReadPrice ?? prices.cache_read_price,
|
|
63
|
+
cache_write_price: prices.cacheWritePrice ?? prices.cache_write_price,
|
|
64
|
+
batch_size: prices.batchSize ?? prices.batch_size,
|
|
65
|
+
context_max: prices.contextMax ?? prices.context_max,
|
|
66
|
+
max_prompt_tokens: prices.maxPromptTokens ?? prices.max_prompt_tokens,
|
|
67
|
+
long_context: prices.longContext ?? prices.long_context,
|
|
68
|
+
price_scale: isCurrentFormat ? 'cents' : 'sdk',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
47
72
|
/**
|
|
48
73
|
* 合併 SDK 模型資料與本機增強資料
|
|
49
74
|
* @param {object} sdkModel
|
|
@@ -62,6 +87,7 @@ export function enrichModel(sdkModel) {
|
|
|
62
87
|
notes: enhancement.notes,
|
|
63
88
|
contextWindow: formatTokenCount(limits.max_context_window_tokens),
|
|
64
89
|
maxOutputTokens: formatTokenCount(limits.max_output_tokens),
|
|
90
|
+
tokenPrices: getTokenPrices(sdkModel.billing),
|
|
65
91
|
};
|
|
66
92
|
}
|
|
67
93
|
|
|
@@ -69,13 +95,13 @@ export function enrichModel(sdkModel) {
|
|
|
69
95
|
* 格式化價格(將 SDK 內部單位轉換為美元 / 1M tokens)
|
|
70
96
|
* @param {number} price
|
|
71
97
|
* @param {number} batchSize
|
|
98
|
+
* @param {'sdk' | 'cents'} priceScale
|
|
72
99
|
* @returns {string}
|
|
73
100
|
*/
|
|
74
|
-
export function formatPrice(price, batchSize) {
|
|
101
|
+
export function formatPrice(price, batchSize, priceScale = 'sdk') {
|
|
75
102
|
if (price === undefined || price === null) return '—';
|
|
76
103
|
if (!batchSize || batchSize === 0 || price === 0) return '—';
|
|
77
|
-
|
|
78
|
-
const dollars = price / 100_000_000_000;
|
|
104
|
+
const dollars = priceScale === 'cents' ? price / 100 : price / 100_000_000_000;
|
|
79
105
|
return `$${dollars.toFixed(2)}`;
|
|
80
106
|
}
|
|
81
107
|
|