@howlil/ez-agents 3.4.1 → 3.5.0
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/LICENSE +21 -21
- package/README.md +84 -20
- package/agents/ez-observer-agent.md +260 -0
- package/agents/ez-release-agent.md +333 -0
- package/agents/ez-requirements-agent.md +377 -0
- package/agents/ez-scrum-master-agent.md +242 -0
- package/agents/ez-tech-lead-agent.md +267 -0
- package/bin/install.js +3221 -3230
- package/commands/ez/arch-review.md +102 -0
- package/commands/ez/execute-phase.md +11 -0
- package/commands/ez/export-session.md +79 -0
- package/commands/ez/gather-requirements.md +117 -0
- package/commands/ez/git-workflow.md +72 -0
- package/commands/ez/hotfix.md +120 -0
- package/commands/ez/import-session.md +82 -0
- package/commands/ez/join-discord.md +18 -18
- package/commands/ez/list-sessions.md +96 -0
- package/commands/ez/package-manager.md +316 -0
- package/commands/ez/plan-phase.md +9 -1
- package/commands/ez/preflight.md +79 -0
- package/commands/ez/progress.md +13 -1
- package/commands/ez/release.md +153 -0
- package/commands/ez/resume.md +107 -0
- package/commands/ez/standup.md +85 -0
- package/ez-agents/bin/ez-tools.cjs +1095 -716
- package/ez-agents/bin/lib/assistant-adapter.cjs +264 -264
- package/ez-agents/bin/lib/audit-exec.cjs +7 -2
- package/ez-agents/bin/lib/bdd-validator.cjs +622 -0
- package/ez-agents/bin/lib/circuit-breaker.cjs +118 -118
- package/ez-agents/bin/lib/config.cjs +190 -190
- package/ez-agents/bin/lib/content-scanner.cjs +238 -0
- package/ez-agents/bin/lib/context-cache.cjs +154 -0
- package/ez-agents/bin/lib/context-errors.cjs +71 -0
- package/ez-agents/bin/lib/context-manager.cjs +220 -0
- package/ez-agents/bin/lib/discussion-synthesizer.cjs +458 -0
- package/ez-agents/bin/lib/file-access.cjs +207 -0
- package/ez-agents/bin/lib/file-lock.cjs +236 -236
- package/ez-agents/bin/lib/frontmatter.cjs +299 -299
- package/ez-agents/bin/lib/fs-utils.cjs +153 -153
- package/ez-agents/bin/lib/git-errors.cjs +83 -0
- package/ez-agents/bin/lib/git-utils.cjs +118 -0
- package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -0
- package/ez-agents/bin/lib/index.cjs +157 -113
- package/ez-agents/bin/lib/init.cjs +757 -757
- package/ez-agents/bin/lib/lockfile-validator.cjs +227 -0
- package/ez-agents/bin/lib/logger.cjs +124 -124
- package/ez-agents/bin/lib/memory-compression.cjs +256 -0
- package/ez-agents/bin/lib/metrics-tracker.cjs +406 -0
- package/ez-agents/bin/lib/milestone.cjs +241 -241
- package/ez-agents/bin/lib/model-provider.cjs +241 -241
- package/ez-agents/bin/lib/package-manager-detector.cjs +203 -0
- package/ez-agents/bin/lib/package-manager-executor.cjs +385 -0
- package/ez-agents/bin/lib/package-manager-service.cjs +216 -0
- package/ez-agents/bin/lib/phase.cjs +925 -925
- package/ez-agents/bin/lib/planning-write.cjs +107 -107
- package/ez-agents/bin/lib/release-validator.cjs +614 -0
- package/ez-agents/bin/lib/retry.cjs +119 -119
- package/ez-agents/bin/lib/roadmap.cjs +306 -306
- package/ez-agents/bin/lib/safe-exec.cjs +128 -128
- package/ez-agents/bin/lib/safe-path.cjs +130 -130
- package/ez-agents/bin/lib/session-chain.cjs +304 -0
- package/ez-agents/bin/lib/session-errors.cjs +81 -0
- package/ez-agents/bin/lib/session-export.cjs +251 -0
- package/ez-agents/bin/lib/session-import.cjs +262 -0
- package/ez-agents/bin/lib/session-manager.cjs +280 -0
- package/ez-agents/bin/lib/state.cjs +736 -736
- package/ez-agents/bin/lib/temp-file.cjs +239 -239
- package/ez-agents/bin/lib/template.cjs +223 -223
- package/ez-agents/bin/lib/test-file-lock.cjs +112 -112
- package/ez-agents/bin/lib/test-graceful.cjs +93 -93
- package/ez-agents/bin/lib/test-logger.cjs +60 -60
- package/ez-agents/bin/lib/test-safe-exec.cjs +38 -38
- package/ez-agents/bin/lib/test-safe-path.cjs +33 -33
- package/ez-agents/bin/lib/test-temp-file.cjs +125 -125
- package/ez-agents/bin/lib/tier-manager.cjs +428 -0
- package/ez-agents/bin/lib/timeout-exec.cjs +63 -63
- package/ez-agents/bin/lib/url-fetch.cjs +170 -0
- package/ez-agents/bin/lib/verify.cjs +15 -1
- package/ez-agents/references/checkpoints.md +776 -776
- package/ez-agents/references/continuation-format.md +249 -249
- package/ez-agents/references/metrics-schema.md +118 -0
- package/ez-agents/references/planning-config.md +140 -0
- package/ez-agents/references/questioning.md +162 -162
- package/ez-agents/references/tdd.md +263 -263
- package/ez-agents/references/tier-strategy.md +103 -0
- package/ez-agents/templates/bdd-feature.md +173 -0
- package/ez-agents/templates/codebase/concerns.md +310 -310
- package/ez-agents/templates/codebase/conventions.md +307 -307
- package/ez-agents/templates/codebase/integrations.md +280 -280
- package/ez-agents/templates/codebase/stack.md +186 -186
- package/ez-agents/templates/codebase/testing.md +480 -480
- package/ez-agents/templates/config.json +37 -37
- package/ez-agents/templates/continue-here.md +78 -78
- package/ez-agents/templates/discussion.md +68 -0
- package/ez-agents/templates/incident-runbook.md +205 -0
- package/ez-agents/templates/milestone-archive.md +123 -123
- package/ez-agents/templates/milestone.md +115 -115
- package/ez-agents/templates/release-checklist.md +133 -0
- package/ez-agents/templates/requirements.md +231 -231
- package/ez-agents/templates/research-project/ARCHITECTURE.md +204 -204
- package/ez-agents/templates/research-project/FEATURES.md +147 -147
- package/ez-agents/templates/research-project/PITFALLS.md +200 -200
- package/ez-agents/templates/research-project/STACK.md +120 -120
- package/ez-agents/templates/research-project/SUMMARY.md +170 -170
- package/ez-agents/templates/retrospective.md +54 -54
- package/ez-agents/templates/roadmap.md +202 -202
- package/ez-agents/templates/rollback-plan.md +201 -0
- package/ez-agents/templates/summary-minimal.md +41 -41
- package/ez-agents/templates/summary-standard.md +48 -48
- package/ez-agents/templates/summary.md +248 -248
- package/ez-agents/templates/user-setup.md +311 -311
- package/ez-agents/templates/verification-report.md +322 -322
- package/ez-agents/workflows/add-phase.md +112 -112
- package/ez-agents/workflows/add-tests.md +351 -351
- package/ez-agents/workflows/add-todo.md +158 -158
- package/ez-agents/workflows/arch-review.md +54 -0
- package/ez-agents/workflows/audit-milestone.md +332 -332
- package/ez-agents/workflows/autonomous.md +131 -30
- package/ez-agents/workflows/check-todos.md +177 -177
- package/ez-agents/workflows/cleanup.md +152 -152
- package/ez-agents/workflows/complete-milestone.md +766 -766
- package/ez-agents/workflows/diagnose-issues.md +219 -219
- package/ez-agents/workflows/discovery-phase.md +289 -289
- package/ez-agents/workflows/discuss-phase.md +762 -762
- package/ez-agents/workflows/execute-phase.md +513 -468
- package/ez-agents/workflows/execute-plan.md +483 -483
- package/ez-agents/workflows/export-session.md +255 -0
- package/ez-agents/workflows/gather-requirements.md +206 -0
- package/ez-agents/workflows/health.md +159 -159
- package/ez-agents/workflows/help.md +584 -492
- package/ez-agents/workflows/hotfix.md +291 -0
- package/ez-agents/workflows/import-session.md +303 -0
- package/ez-agents/workflows/insert-phase.md +130 -130
- package/ez-agents/workflows/list-phase-assumptions.md +178 -178
- package/ez-agents/workflows/map-codebase.md +316 -316
- package/ez-agents/workflows/new-milestone.md +339 -10
- package/ez-agents/workflows/new-project.md +293 -299
- package/ez-agents/workflows/node-repair.md +92 -92
- package/ez-agents/workflows/pause-work.md +122 -122
- package/ez-agents/workflows/plan-milestone-gaps.md +274 -274
- package/ez-agents/workflows/plan-phase.md +673 -651
- package/ez-agents/workflows/progress.md +372 -382
- package/ez-agents/workflows/quick.md +610 -610
- package/ez-agents/workflows/release.md +253 -0
- package/ez-agents/workflows/remove-phase.md +155 -155
- package/ez-agents/workflows/research-phase.md +74 -74
- package/ez-agents/workflows/resume-project.md +307 -307
- package/ez-agents/workflows/resume-session.md +215 -0
- package/ez-agents/workflows/set-profile.md +81 -81
- package/ez-agents/workflows/settings.md +242 -242
- package/ez-agents/workflows/standup.md +64 -0
- package/ez-agents/workflows/stats.md +57 -57
- package/ez-agents/workflows/transition.md +544 -544
- package/ez-agents/workflows/ui-phase.md +290 -290
- package/ez-agents/workflows/ui-review.md +157 -157
- package/ez-agents/workflows/update.md +320 -320
- package/ez-agents/workflows/validate-phase.md +167 -167
- package/ez-agents/workflows/verify-phase.md +243 -243
- package/ez-agents/workflows/verify-work.md +584 -584
- package/package.json +10 -4
- package/scripts/build-hooks.js +43 -43
- package/scripts/run-tests.cjs +29 -29
|
@@ -1,241 +1,241 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* EZ Model Provider — Unified API for multiple AI providers
|
|
5
|
-
*
|
|
6
|
-
* Supports: Anthropic, Moonshot (Kimi), Alibaba (Qwen), OpenAI
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const https = require('https');
|
|
10
|
-
const { URL } = require('url');
|
|
11
|
-
const Logger = require('./logger.cjs');
|
|
12
|
-
const logger = new Logger();
|
|
13
|
-
|
|
14
|
-
class ModelProvider {
|
|
15
|
-
/**
|
|
16
|
-
* Create model provider
|
|
17
|
-
* @param {Object} config - Configuration
|
|
18
|
-
*/
|
|
19
|
-
constructor(config) {
|
|
20
|
-
this.provider = config.provider || 'anthropic';
|
|
21
|
-
this.model = config.model || 'sonnet';
|
|
22
|
-
this.apiKey = config.apiKey || process.env[`${this.provider.toUpperCase()}_API_KEY`];
|
|
23
|
-
|
|
24
|
-
if (!this.apiKey && this.provider !== 'anthropic') {
|
|
25
|
-
logger.warn('API key not configured', { provider: this.provider });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Helper for HTTP requests
|
|
31
|
-
*/
|
|
32
|
-
_httpRequest(options, data) {
|
|
33
|
-
return new Promise((resolve, reject) => {
|
|
34
|
-
const req = https.request(options, (res) => {
|
|
35
|
-
let body = '';
|
|
36
|
-
res.on('data', (chunk) => body += chunk);
|
|
37
|
-
res.on('end', () => {
|
|
38
|
-
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
39
|
-
try {
|
|
40
|
-
resolve(JSON.parse(body));
|
|
41
|
-
} catch (e) {
|
|
42
|
-
resolve(body);
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
reject(new Error(`HTTP ${res.statusCode}: ${body}`));
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
req.on('error', reject);
|
|
50
|
-
if (data) req.write(JSON.stringify(data));
|
|
51
|
-
req.end();
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Send chat message
|
|
57
|
-
* @param {Object[]} messages - Chat messages
|
|
58
|
-
* @param {Object} options - Chat options
|
|
59
|
-
* @returns {Promise<Object>} - Response
|
|
60
|
-
*/
|
|
61
|
-
async chat(messages, options = {}) {
|
|
62
|
-
logger.info('Chat request', {
|
|
63
|
-
provider: this.provider,
|
|
64
|
-
model: this.model,
|
|
65
|
-
messageCount: messages.length
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
switch (this.provider) {
|
|
69
|
-
case 'anthropic':
|
|
70
|
-
return this._chatAnthropic(messages, options);
|
|
71
|
-
case 'moonshot':
|
|
72
|
-
return this._chatMoonshot(messages, options);
|
|
73
|
-
case 'alibaba':
|
|
74
|
-
case 'qwen':
|
|
75
|
-
return this._chatQwen(messages, options);
|
|
76
|
-
case 'openai':
|
|
77
|
-
return this._chatOpenAI(messages, options);
|
|
78
|
-
default:
|
|
79
|
-
throw new Error(`Unsupported provider: ${this.provider}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Anthropic Claude API
|
|
85
|
-
*/
|
|
86
|
-
async _chatAnthropic(messages, options) {
|
|
87
|
-
// Anthropic usually requires their SDK or complex headers
|
|
88
|
-
logger.debug('Anthropic chat', { model: this.model });
|
|
89
|
-
return {
|
|
90
|
-
content: '[Anthropic response placeholder - requires SDK]',
|
|
91
|
-
provider: 'anthropic',
|
|
92
|
-
model: this.model
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Moonshot (Kimi) API
|
|
98
|
-
*/
|
|
99
|
-
async _chatMoonshot(messages, options) {
|
|
100
|
-
const modelName = this.model === 'sonnet' ? 'moonshot-v1-8k' : this.model;
|
|
101
|
-
const data = {
|
|
102
|
-
model: modelName,
|
|
103
|
-
messages: messages,
|
|
104
|
-
temperature: options.temperature || 0.3
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const reqOptions = {
|
|
108
|
-
hostname: 'api.moonshot.cn',
|
|
109
|
-
path: '/v1/chat/completions',
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: {
|
|
112
|
-
'Content-Type': 'application/json',
|
|
113
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
const response = await this._httpRequest(reqOptions, data);
|
|
119
|
-
return {
|
|
120
|
-
content: response.choices[0].message.content,
|
|
121
|
-
provider: 'moonshot',
|
|
122
|
-
model: modelName
|
|
123
|
-
};
|
|
124
|
-
} catch (error) {
|
|
125
|
-
logger.error('Moonshot API error', { error: error.message });
|
|
126
|
-
throw error;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Alibaba Qwen API (DashScope)
|
|
132
|
-
*/
|
|
133
|
-
async _chatQwen(messages, options) {
|
|
134
|
-
// Map generic model names to Qwen specific ones
|
|
135
|
-
let modelName = this.model;
|
|
136
|
-
if (modelName === 'sonnet' || modelName === 'gpt-4') modelName = 'qwen-max';
|
|
137
|
-
if (modelName === 'haiku' || modelName === 'gpt-3.5-turbo') modelName = 'qwen-plus';
|
|
138
|
-
|
|
139
|
-
const data = {
|
|
140
|
-
model: modelName,
|
|
141
|
-
input: {
|
|
142
|
-
messages: messages
|
|
143
|
-
},
|
|
144
|
-
parameters: {
|
|
145
|
-
result_format: 'message',
|
|
146
|
-
temperature: options.temperature || 0.3
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const reqOptions = {
|
|
151
|
-
hostname: 'dashscope.aliyuncs.com',
|
|
152
|
-
path: '/api/v1/services/aigc/text-generation/generation',
|
|
153
|
-
method: 'POST',
|
|
154
|
-
headers: {
|
|
155
|
-
'Content-Type': 'application/json',
|
|
156
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
const response = await this._httpRequest(reqOptions, data);
|
|
162
|
-
return {
|
|
163
|
-
content: response.output.choices[0].message.content,
|
|
164
|
-
provider: 'alibaba',
|
|
165
|
-
model: modelName
|
|
166
|
-
};
|
|
167
|
-
} catch (error) {
|
|
168
|
-
logger.error('Qwen API error', { error: error.message });
|
|
169
|
-
throw error;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* OpenAI API
|
|
175
|
-
*/
|
|
176
|
-
async _chatOpenAI(messages, options) {
|
|
177
|
-
const modelName = this.model === 'sonnet' ? 'gpt-4-turbo' : this.model;
|
|
178
|
-
const data = {
|
|
179
|
-
model: modelName,
|
|
180
|
-
messages: messages,
|
|
181
|
-
temperature: options.temperature || 0.3
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const reqOptions = {
|
|
185
|
-
hostname: 'api.openai.com',
|
|
186
|
-
path: '/v1/chat/completions',
|
|
187
|
-
method: 'POST',
|
|
188
|
-
headers: {
|
|
189
|
-
'Content-Type': 'application/json',
|
|
190
|
-
'Authorization': `Bearer ${this.apiKey}`
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
const response = await this._httpRequest(reqOptions, data);
|
|
196
|
-
return {
|
|
197
|
-
content: response.choices[0].message.content,
|
|
198
|
-
provider: 'openai',
|
|
199
|
-
model: modelName
|
|
200
|
-
};
|
|
201
|
-
} catch (error) {
|
|
202
|
-
logger.error('OpenAI API error', { error: error.message });
|
|
203
|
-
throw error;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Count tokens (approximate)
|
|
209
|
-
* @param {string} text - Text to count
|
|
210
|
-
* @returns {number} - Approximate token count
|
|
211
|
-
*/
|
|
212
|
-
countTokens(text) {
|
|
213
|
-
return Math.ceil(text.length / 4);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Get provider info
|
|
218
|
-
* @returns {Object} - Provider information
|
|
219
|
-
*/
|
|
220
|
-
getInfo() {
|
|
221
|
-
return {
|
|
222
|
-
provider: this.provider,
|
|
223
|
-
model: this.model,
|
|
224
|
-
hasApiKey: !!this.apiKey
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Create provider from config
|
|
231
|
-
* @param {Object} config - Provider config
|
|
232
|
-
* @returns {ModelProvider} - Model provider instance
|
|
233
|
-
*/
|
|
234
|
-
function createProvider(config) {
|
|
235
|
-
return new ModelProvider(config);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
module.exports = {
|
|
239
|
-
ModelProvider,
|
|
240
|
-
createProvider
|
|
241
|
-
};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EZ Model Provider — Unified API for multiple AI providers
|
|
5
|
+
*
|
|
6
|
+
* Supports: Anthropic, Moonshot (Kimi), Alibaba (Qwen), OpenAI
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const https = require('https');
|
|
10
|
+
const { URL } = require('url');
|
|
11
|
+
const Logger = require('./logger.cjs');
|
|
12
|
+
const logger = new Logger();
|
|
13
|
+
|
|
14
|
+
class ModelProvider {
|
|
15
|
+
/**
|
|
16
|
+
* Create model provider
|
|
17
|
+
* @param {Object} config - Configuration
|
|
18
|
+
*/
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.provider = config.provider || 'anthropic';
|
|
21
|
+
this.model = config.model || 'sonnet';
|
|
22
|
+
this.apiKey = config.apiKey || process.env[`${this.provider.toUpperCase()}_API_KEY`];
|
|
23
|
+
|
|
24
|
+
if (!this.apiKey && this.provider !== 'anthropic') {
|
|
25
|
+
logger.warn('API key not configured', { provider: this.provider });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Helper for HTTP requests
|
|
31
|
+
*/
|
|
32
|
+
_httpRequest(options, data) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const req = https.request(options, (res) => {
|
|
35
|
+
let body = '';
|
|
36
|
+
res.on('data', (chunk) => body += chunk);
|
|
37
|
+
res.on('end', () => {
|
|
38
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
39
|
+
try {
|
|
40
|
+
resolve(JSON.parse(body));
|
|
41
|
+
} catch (e) {
|
|
42
|
+
resolve(body);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
reject(new Error(`HTTP ${res.statusCode}: ${body}`));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
req.on('error', reject);
|
|
50
|
+
if (data) req.write(JSON.stringify(data));
|
|
51
|
+
req.end();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Send chat message
|
|
57
|
+
* @param {Object[]} messages - Chat messages
|
|
58
|
+
* @param {Object} options - Chat options
|
|
59
|
+
* @returns {Promise<Object>} - Response
|
|
60
|
+
*/
|
|
61
|
+
async chat(messages, options = {}) {
|
|
62
|
+
logger.info('Chat request', {
|
|
63
|
+
provider: this.provider,
|
|
64
|
+
model: this.model,
|
|
65
|
+
messageCount: messages.length
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
switch (this.provider) {
|
|
69
|
+
case 'anthropic':
|
|
70
|
+
return this._chatAnthropic(messages, options);
|
|
71
|
+
case 'moonshot':
|
|
72
|
+
return this._chatMoonshot(messages, options);
|
|
73
|
+
case 'alibaba':
|
|
74
|
+
case 'qwen':
|
|
75
|
+
return this._chatQwen(messages, options);
|
|
76
|
+
case 'openai':
|
|
77
|
+
return this._chatOpenAI(messages, options);
|
|
78
|
+
default:
|
|
79
|
+
throw new Error(`Unsupported provider: ${this.provider}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Anthropic Claude API
|
|
85
|
+
*/
|
|
86
|
+
async _chatAnthropic(messages, options) {
|
|
87
|
+
// Anthropic usually requires their SDK or complex headers
|
|
88
|
+
logger.debug('Anthropic chat', { model: this.model });
|
|
89
|
+
return {
|
|
90
|
+
content: '[Anthropic response placeholder - requires SDK]',
|
|
91
|
+
provider: 'anthropic',
|
|
92
|
+
model: this.model
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Moonshot (Kimi) API
|
|
98
|
+
*/
|
|
99
|
+
async _chatMoonshot(messages, options) {
|
|
100
|
+
const modelName = this.model === 'sonnet' ? 'moonshot-v1-8k' : this.model;
|
|
101
|
+
const data = {
|
|
102
|
+
model: modelName,
|
|
103
|
+
messages: messages,
|
|
104
|
+
temperature: options.temperature || 0.3
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const reqOptions = {
|
|
108
|
+
hostname: 'api.moonshot.cn',
|
|
109
|
+
path: '/v1/chat/completions',
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: {
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const response = await this._httpRequest(reqOptions, data);
|
|
119
|
+
return {
|
|
120
|
+
content: response.choices[0].message.content,
|
|
121
|
+
provider: 'moonshot',
|
|
122
|
+
model: modelName
|
|
123
|
+
};
|
|
124
|
+
} catch (error) {
|
|
125
|
+
logger.error('Moonshot API error', { error: error.message });
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Alibaba Qwen API (DashScope)
|
|
132
|
+
*/
|
|
133
|
+
async _chatQwen(messages, options) {
|
|
134
|
+
// Map generic model names to Qwen specific ones
|
|
135
|
+
let modelName = this.model;
|
|
136
|
+
if (modelName === 'sonnet' || modelName === 'gpt-4') modelName = 'qwen-max';
|
|
137
|
+
if (modelName === 'haiku' || modelName === 'gpt-3.5-turbo') modelName = 'qwen-plus';
|
|
138
|
+
|
|
139
|
+
const data = {
|
|
140
|
+
model: modelName,
|
|
141
|
+
input: {
|
|
142
|
+
messages: messages
|
|
143
|
+
},
|
|
144
|
+
parameters: {
|
|
145
|
+
result_format: 'message',
|
|
146
|
+
temperature: options.temperature || 0.3
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const reqOptions = {
|
|
151
|
+
hostname: 'dashscope.aliyuncs.com',
|
|
152
|
+
path: '/api/v1/services/aigc/text-generation/generation',
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/json',
|
|
156
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const response = await this._httpRequest(reqOptions, data);
|
|
162
|
+
return {
|
|
163
|
+
content: response.output.choices[0].message.content,
|
|
164
|
+
provider: 'alibaba',
|
|
165
|
+
model: modelName
|
|
166
|
+
};
|
|
167
|
+
} catch (error) {
|
|
168
|
+
logger.error('Qwen API error', { error: error.message });
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* OpenAI API
|
|
175
|
+
*/
|
|
176
|
+
async _chatOpenAI(messages, options) {
|
|
177
|
+
const modelName = this.model === 'sonnet' ? 'gpt-4-turbo' : this.model;
|
|
178
|
+
const data = {
|
|
179
|
+
model: modelName,
|
|
180
|
+
messages: messages,
|
|
181
|
+
temperature: options.temperature || 0.3
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const reqOptions = {
|
|
185
|
+
hostname: 'api.openai.com',
|
|
186
|
+
path: '/v1/chat/completions',
|
|
187
|
+
method: 'POST',
|
|
188
|
+
headers: {
|
|
189
|
+
'Content-Type': 'application/json',
|
|
190
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const response = await this._httpRequest(reqOptions, data);
|
|
196
|
+
return {
|
|
197
|
+
content: response.choices[0].message.content,
|
|
198
|
+
provider: 'openai',
|
|
199
|
+
model: modelName
|
|
200
|
+
};
|
|
201
|
+
} catch (error) {
|
|
202
|
+
logger.error('OpenAI API error', { error: error.message });
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Count tokens (approximate)
|
|
209
|
+
* @param {string} text - Text to count
|
|
210
|
+
* @returns {number} - Approximate token count
|
|
211
|
+
*/
|
|
212
|
+
countTokens(text) {
|
|
213
|
+
return Math.ceil(text.length / 4);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get provider info
|
|
218
|
+
* @returns {Object} - Provider information
|
|
219
|
+
*/
|
|
220
|
+
getInfo() {
|
|
221
|
+
return {
|
|
222
|
+
provider: this.provider,
|
|
223
|
+
model: this.model,
|
|
224
|
+
hasApiKey: !!this.apiKey
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Create provider from config
|
|
231
|
+
* @param {Object} config - Provider config
|
|
232
|
+
* @returns {ModelProvider} - Model provider instance
|
|
233
|
+
*/
|
|
234
|
+
function createProvider(config) {
|
|
235
|
+
return new ModelProvider(config);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
ModelProvider,
|
|
240
|
+
createProvider
|
|
241
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Package Manager Detector — Auto-detect available package managers
|
|
5
|
+
*
|
|
6
|
+
* Detects package managers (npm, yarn, pnpm) using a multi-layer strategy:
|
|
7
|
+
* 1. Configuration override (.planning/config.json)
|
|
8
|
+
* 2. Lockfile presence (pnpm-lock.yaml, yarn.lock, package-lock.json)
|
|
9
|
+
* 3. System availability (which pnpm/yarn/npm)
|
|
10
|
+
* 4. Fallback to npm (always available with Node)
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const PackageManagerDetector = require('./package-manager-detector.cjs');
|
|
14
|
+
* const detector = new PackageManagerDetector(cwd);
|
|
15
|
+
* const result = detector.detect();
|
|
16
|
+
* // Returns: { manager, source, confidence, lockfilePath? }
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { execFileSync } = require('child_process');
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const Logger = require('./logger.cjs');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Package Manager Detector class
|
|
26
|
+
* Detects available package managers with priority-based strategy
|
|
27
|
+
*/
|
|
28
|
+
class PackageManagerDetector {
|
|
29
|
+
/**
|
|
30
|
+
* Create a PackageManagerDetector instance
|
|
31
|
+
* @param {string} cwd - Working directory (default: process.cwd())
|
|
32
|
+
*/
|
|
33
|
+
constructor(cwd = process.cwd()) {
|
|
34
|
+
this.cwd = cwd;
|
|
35
|
+
this.logger = new Logger();
|
|
36
|
+
this.config = this.loadConfig();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Detect package manager with priority-based strategy
|
|
41
|
+
* @returns {Object} Detection result { manager, source, confidence, lockfilePath? }
|
|
42
|
+
*/
|
|
43
|
+
detect() {
|
|
44
|
+
this.logger.info('Starting package manager detection', { cwd: this.cwd });
|
|
45
|
+
|
|
46
|
+
// Layer 1: Configuration override
|
|
47
|
+
const configManager = this._detectFromConfig();
|
|
48
|
+
if (configManager) {
|
|
49
|
+
this.logger.info('Package manager detected from config', { manager: configManager });
|
|
50
|
+
return {
|
|
51
|
+
manager: configManager,
|
|
52
|
+
source: 'config',
|
|
53
|
+
confidence: 'high',
|
|
54
|
+
configPath: '.planning/config.json'
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Layer 2: Lockfile detection
|
|
59
|
+
const lockfileManager = this.detectFromLockfile();
|
|
60
|
+
if (lockfileManager) {
|
|
61
|
+
const lockfilePath = this.getLockfilePath(lockfileManager);
|
|
62
|
+
this.logger.info('Package manager detected from lockfile', {
|
|
63
|
+
manager: lockfileManager,
|
|
64
|
+
lockfilePath
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
manager: lockfileManager,
|
|
68
|
+
source: 'lockfile',
|
|
69
|
+
confidence: 'high',
|
|
70
|
+
lockfilePath
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Layer 3: System availability
|
|
75
|
+
const availableManagers = this.getAvailableManagers();
|
|
76
|
+
if (availableManagers.length > 0) {
|
|
77
|
+
// Prefer pnpm > yarn > npm (performance order)
|
|
78
|
+
const preferred = availableManagers.find(m => m === 'pnpm') ||
|
|
79
|
+
availableManagers.find(m => m === 'yarn') ||
|
|
80
|
+
availableManagers[0];
|
|
81
|
+
this.logger.info('Package manager detected from system', {
|
|
82
|
+
manager: preferred,
|
|
83
|
+
available: availableManagers
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
manager: preferred,
|
|
87
|
+
source: 'system',
|
|
88
|
+
confidence: 'medium',
|
|
89
|
+
available: availableManagers
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Layer 4: Fallback to npm
|
|
94
|
+
this.logger.warn('No package manager detected, falling back to npm');
|
|
95
|
+
return {
|
|
96
|
+
manager: 'npm',
|
|
97
|
+
source: 'fallback',
|
|
98
|
+
confidence: 'low',
|
|
99
|
+
reason: 'No other package manager detected'
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Detect package manager from configuration
|
|
105
|
+
* @private
|
|
106
|
+
* @returns {string|null} Package manager name or null
|
|
107
|
+
*/
|
|
108
|
+
_detectFromConfig() {
|
|
109
|
+
const configManager = this.config?.packageManager?.default;
|
|
110
|
+
if (configManager && this.isPackageManagerInstalled(configManager)) {
|
|
111
|
+
return configManager;
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Detect package manager from lockfile presence
|
|
118
|
+
* @returns {string|null} Package manager name or null
|
|
119
|
+
*/
|
|
120
|
+
detectFromLockfile() {
|
|
121
|
+
const lockfiles = {
|
|
122
|
+
'pnpm-lock.yaml': 'pnpm',
|
|
123
|
+
'yarn.lock': 'yarn',
|
|
124
|
+
'package-lock.json': 'npm'
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
for (const [lockfile, manager] of Object.entries(lockfiles)) {
|
|
128
|
+
const lockfilePath = path.join(this.cwd, lockfile);
|
|
129
|
+
if (fs.existsSync(lockfilePath)) {
|
|
130
|
+
return manager;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get all available package managers installed on the system
|
|
138
|
+
* @returns {string[]} Array of available manager names
|
|
139
|
+
*/
|
|
140
|
+
getAvailableManagers() {
|
|
141
|
+
const managers = ['pnpm', 'yarn', 'npm'];
|
|
142
|
+
const available = [];
|
|
143
|
+
|
|
144
|
+
for (const manager of managers) {
|
|
145
|
+
if (this.isPackageManagerInstalled(manager)) {
|
|
146
|
+
available.push(manager);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return available;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if a specific package manager is installed
|
|
155
|
+
* @param {string} manager - Package manager name
|
|
156
|
+
* @returns {boolean} True if installed
|
|
157
|
+
*/
|
|
158
|
+
isPackageManagerInstalled(manager) {
|
|
159
|
+
try {
|
|
160
|
+
execFileSync(manager, ['--version'], {
|
|
161
|
+
stdio: 'pipe',
|
|
162
|
+
shell: false
|
|
163
|
+
});
|
|
164
|
+
return true;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
this.logger.debug('Package manager not installed', { manager, error: err.message });
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get the lockfile path for a specific package manager
|
|
173
|
+
* @param {string} manager - Package manager name
|
|
174
|
+
* @returns {string} Full path to lockfile
|
|
175
|
+
*/
|
|
176
|
+
getLockfilePath(manager) {
|
|
177
|
+
const lockfiles = {
|
|
178
|
+
'pnpm': 'pnpm-lock.yaml',
|
|
179
|
+
'yarn': 'yarn.lock',
|
|
180
|
+
'npm': 'package-lock.json'
|
|
181
|
+
};
|
|
182
|
+
return path.join(this.cwd, lockfiles[manager] || 'package-lock.json');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Load configuration from .planning/config.json
|
|
187
|
+
* @returns {Object} Configuration object
|
|
188
|
+
*/
|
|
189
|
+
loadConfig() {
|
|
190
|
+
const configPath = path.join(this.cwd, '.planning', 'config.json');
|
|
191
|
+
try {
|
|
192
|
+
if (fs.existsSync(configPath)) {
|
|
193
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
194
|
+
return JSON.parse(content);
|
|
195
|
+
}
|
|
196
|
+
} catch (err) {
|
|
197
|
+
this.logger.warn('Failed to load config', { path: configPath, error: err.message });
|
|
198
|
+
}
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = PackageManagerDetector;
|