@aida-dev/cli 0.2.0 → 0.3.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/dist/index.js +214 -9
- package/dist/index.js.map +1 -1
- package/package.json +28 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command5 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/collect.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -20,10 +20,37 @@ var CLIConfig = z.object({
|
|
|
20
20
|
aiTrailerDomains: z.array(z.string()).default([]),
|
|
21
21
|
defaultBranch: z.string().optional(),
|
|
22
22
|
outDir: z.string().default("./aida-output"),
|
|
23
|
-
format: z.enum(["json", "md", "both"]).default("both").optional(),
|
|
24
23
|
verbose: z.boolean().default(false)
|
|
25
24
|
});
|
|
26
25
|
|
|
26
|
+
// src/providers/pr-base.ts
|
|
27
|
+
function detectPRBaseRef() {
|
|
28
|
+
if (process.env.GITHUB_ACTIONS === "true" && process.env.GITHUB_BASE_REF) {
|
|
29
|
+
return `origin/${process.env.GITHUB_BASE_REF}`;
|
|
30
|
+
}
|
|
31
|
+
if (process.env.GITLAB_CI === "true") {
|
|
32
|
+
if (process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA) {
|
|
33
|
+
return process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA;
|
|
34
|
+
}
|
|
35
|
+
if (process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME) {
|
|
36
|
+
return `origin/${process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME}`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (process.env.BITBUCKET_PIPELINE_UUID) {
|
|
40
|
+
if (process.env.BITBUCKET_PR_DESTINATION_BRANCH) {
|
|
41
|
+
return `origin/${process.env.BITBUCKET_PR_DESTINATION_BRANCH}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (process.env.TF_BUILD === "True") {
|
|
45
|
+
const targetBranch = process.env.SYSTEM_PULLREQUEST_TARGETBRANCH;
|
|
46
|
+
if (targetBranch) {
|
|
47
|
+
const branchName = targetBranch.replace("refs/heads/", "");
|
|
48
|
+
return `origin/${branchName}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
27
54
|
// src/commands/collect.ts
|
|
28
55
|
async function loadAidaConfig(repoPath) {
|
|
29
56
|
try {
|
|
@@ -37,7 +64,7 @@ function collectRepeatable(value, previous) {
|
|
|
37
64
|
return previous ? [...previous, value] : [value];
|
|
38
65
|
}
|
|
39
66
|
function createCollectCommand() {
|
|
40
|
-
return new Command("collect").description("Collect commits and generate commit-stream.json").option("--repo <path>", "Repository path", process.cwd()).option("--since <date>", "Start date (ISO or relative like 90d)").option("--until <date>", "End date (ISO or relative)").option("--ai-pattern <pattern>", "AI detection regex (repeatable)", collectRepeatable, []).option("--ai-tool <name>", "Additional AI tool name (repeatable)", collectRepeatable, []).option("--ai-trailer-domain <domain>", "Additional Co-authored-by domain (repeatable)", collectRepeatable, []).option("--default-branch <name>", "Default branch name").option("--out-dir <path>", "Output directory", "./aida-output").option("--verbose", "Verbose logging", false).action(async (options) => {
|
|
67
|
+
return new Command("collect").description("Collect commits and generate commit-stream.json").option("--repo <path>", "Repository path", process.cwd()).option("--since <date>", "Start date (ISO or relative like 90d)").option("--until <date>", "End date (ISO or relative)").option("--pr", "PR-scoped analysis (auto-detect base ref from CI env vars)").option("--diff-base <ref>", "Explicit base ref for PR-scoped analysis (e.g., origin/main)").option("--ai-pattern <pattern>", "AI detection regex (repeatable)", collectRepeatable, []).option("--ai-tool <name>", "Additional AI tool name (repeatable)", collectRepeatable, []).option("--ai-trailer-domain <domain>", "Additional Co-authored-by domain (repeatable)", collectRepeatable, []).option("--default-branch <name>", "Default branch name").option("--out-dir <path>", "Output directory", "./aida-output").option("--verbose", "Verbose logging", false).action(async (options) => {
|
|
41
68
|
const mapped = {
|
|
42
69
|
...options,
|
|
43
70
|
aiPatterns: options.aiPattern || [],
|
|
@@ -53,11 +80,21 @@ function createCollectCommand() {
|
|
|
53
80
|
const aiTrailerDomains = [...fileConfig.trailerDomains || [], ...config.aiTrailerDomains];
|
|
54
81
|
if (aiTools.length > 0) logger.info(`Custom AI tools: ${aiTools.join(", ")}`);
|
|
55
82
|
if (aiTrailerDomains.length > 0) logger.info(`Custom trailer domains: ${aiTrailerDomains.join(", ")}`);
|
|
83
|
+
let diffBase = options.diffBase;
|
|
84
|
+
if (options.pr && !diffBase) {
|
|
85
|
+
diffBase = detectPRBaseRef() ?? void 0;
|
|
86
|
+
if (diffBase) {
|
|
87
|
+
logger.info(`PR mode: detected base ref ${diffBase}`);
|
|
88
|
+
} else {
|
|
89
|
+
logger.warn("--pr flag used but no PR context detected. Falling back to --since mode.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
56
92
|
logger.info("Starting commit collection...");
|
|
57
93
|
const commitStream = await collectCommits({
|
|
58
94
|
repoPath: config.repo,
|
|
59
|
-
since: config.since,
|
|
60
|
-
until: config.until,
|
|
95
|
+
since: diffBase ? void 0 : config.since,
|
|
96
|
+
until: diffBase ? void 0 : config.until,
|
|
97
|
+
diffBase,
|
|
61
98
|
aiPatterns,
|
|
62
99
|
aiTools,
|
|
63
100
|
aiTrailerDomains,
|
|
@@ -80,7 +117,7 @@ function createCollectCommand() {
|
|
|
80
117
|
|
|
81
118
|
// src/commands/analyze.ts
|
|
82
119
|
import { Command as Command2 } from "commander";
|
|
83
|
-
import { readJSON, writeJSON as writeJSON2, createLogger as createLogger2 } from "@aida-dev/core";
|
|
120
|
+
import { readJSON, writeJSON as writeJSON2, createLogger as createLogger2, CommitStream } from "@aida-dev/core";
|
|
84
121
|
import { calculateMetrics } from "@aida-dev/metrics";
|
|
85
122
|
import { join as join2 } from "path";
|
|
86
123
|
function createAnalyzeCommand() {
|
|
@@ -90,7 +127,7 @@ function createAnalyzeCommand() {
|
|
|
90
127
|
try {
|
|
91
128
|
logger.info("Starting metrics analysis...");
|
|
92
129
|
const inputPath = join2(config.outDir, "commit-stream.json");
|
|
93
|
-
const commitStream = await readJSON(inputPath);
|
|
130
|
+
const commitStream = await readJSON(inputPath, CommitStream);
|
|
94
131
|
logger.info(`Analyzing ${commitStream.commits.length} commits`);
|
|
95
132
|
const metrics = calculateMetrics(commitStream);
|
|
96
133
|
const outputPath = join2(config.outDir, "metrics.json");
|
|
@@ -108,6 +145,7 @@ function createAnalyzeCommand() {
|
|
|
108
145
|
// src/commands/report.ts
|
|
109
146
|
import { Command as Command3 } from "commander";
|
|
110
147
|
import { readJSON as readJSON2, createLogger as createLogger3 } from "@aida-dev/core";
|
|
148
|
+
import { Metrics } from "@aida-dev/metrics";
|
|
111
149
|
import { join as join3 } from "path";
|
|
112
150
|
import { promises as fs } from "fs";
|
|
113
151
|
function generateMarkdownReport(metrics) {
|
|
@@ -144,7 +182,7 @@ function createReportCommand() {
|
|
|
144
182
|
try {
|
|
145
183
|
logger.info("Generating report...");
|
|
146
184
|
const inputPath = join3(config.outDir, "metrics.json");
|
|
147
|
-
const metrics = await readJSON2(inputPath);
|
|
185
|
+
const metrics = await readJSON2(inputPath, Metrics);
|
|
148
186
|
const markdown = generateMarkdownReport(metrics);
|
|
149
187
|
const mdPath = join3(config.outDir, "report.md");
|
|
150
188
|
await fs.writeFile(mdPath, markdown, "utf-8");
|
|
@@ -159,11 +197,178 @@ function createReportCommand() {
|
|
|
159
197
|
});
|
|
160
198
|
}
|
|
161
199
|
|
|
200
|
+
// src/commands/comment.ts
|
|
201
|
+
import { Command as Command4 } from "commander";
|
|
202
|
+
import { createLogger as createLogger4 } from "@aida-dev/core";
|
|
203
|
+
import { join as join4 } from "path";
|
|
204
|
+
import { promises as fs2 } from "fs";
|
|
205
|
+
|
|
206
|
+
// src/providers/github.ts
|
|
207
|
+
import { readFileSync } from "fs";
|
|
208
|
+
var MARKER = "<!-- aida-metrics-report -->";
|
|
209
|
+
function sanitizeErrorBody(text, maxLength = 200) {
|
|
210
|
+
let sanitized = text.replace(/ghp_[A-Za-z0-9_]+/g, "[REDACTED]").replace(/gho_[A-Za-z0-9_]+/g, "[REDACTED]").replace(/github_pat_[A-Za-z0-9_]+/g, "[REDACTED]").replace(/Bearer\s+[A-Za-z0-9._-]+/gi, "Bearer [REDACTED]").replace(/token\s+[A-Za-z0-9._-]+/gi, "token [REDACTED]");
|
|
211
|
+
if (sanitized.length > maxLength) {
|
|
212
|
+
sanitized = sanitized.slice(0, maxLength) + "...(truncated)";
|
|
213
|
+
}
|
|
214
|
+
return sanitized;
|
|
215
|
+
}
|
|
216
|
+
var GitHubProvider = class {
|
|
217
|
+
name = "github";
|
|
218
|
+
get token() {
|
|
219
|
+
const token = process.env.GITHUB_TOKEN;
|
|
220
|
+
if (!token) {
|
|
221
|
+
throw new Error("GITHUB_TOKEN is required for GitHub PR comments. Pass it via env.");
|
|
222
|
+
}
|
|
223
|
+
return token;
|
|
224
|
+
}
|
|
225
|
+
get repo() {
|
|
226
|
+
const repo = process.env.GITHUB_REPOSITORY;
|
|
227
|
+
if (!repo) {
|
|
228
|
+
throw new Error("GITHUB_REPOSITORY env var not found. Are you running in GitHub Actions?");
|
|
229
|
+
}
|
|
230
|
+
return repo;
|
|
231
|
+
}
|
|
232
|
+
get apiUrl() {
|
|
233
|
+
return process.env.GITHUB_API_URL || "https://api.github.com";
|
|
234
|
+
}
|
|
235
|
+
getPRIdentifier() {
|
|
236
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
237
|
+
if (!eventPath) return null;
|
|
238
|
+
try {
|
|
239
|
+
const event = JSON.parse(readFileSync(eventPath, "utf-8"));
|
|
240
|
+
const prNumber = event.pull_request?.number || event.number;
|
|
241
|
+
if (!prNumber) return null;
|
|
242
|
+
return {
|
|
243
|
+
provider: "github",
|
|
244
|
+
prNumber: String(prNumber),
|
|
245
|
+
repo: this.repo
|
|
246
|
+
};
|
|
247
|
+
} catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async postComment(content, marker = MARKER) {
|
|
252
|
+
const pr = this.getPRIdentifier();
|
|
253
|
+
if (!pr) {
|
|
254
|
+
throw new Error("Could not determine PR number. Is this running on a pull_request event?");
|
|
255
|
+
}
|
|
256
|
+
const markedContent = `${marker}
|
|
257
|
+
${content}`;
|
|
258
|
+
const existingId = await this.findExistingComment(pr, marker);
|
|
259
|
+
if (existingId) {
|
|
260
|
+
await this.updateComment(existingId, markedContent);
|
|
261
|
+
} else {
|
|
262
|
+
await this.createComment(pr, markedContent);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async findExistingComment(pr, marker) {
|
|
266
|
+
const url = `${this.apiUrl}/repos/${pr.repo}/issues/${pr.prNumber}/comments?per_page=100`;
|
|
267
|
+
const response = await fetch(url, {
|
|
268
|
+
headers: {
|
|
269
|
+
Authorization: `Bearer ${this.token}`,
|
|
270
|
+
Accept: "application/vnd.github.v3+json"
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
if (!response.ok) return null;
|
|
274
|
+
const comments = await response.json();
|
|
275
|
+
const existing = comments.find((c) => c.body.startsWith(marker));
|
|
276
|
+
return existing?.id || null;
|
|
277
|
+
}
|
|
278
|
+
async createComment(pr, body) {
|
|
279
|
+
const url = `${this.apiUrl}/repos/${pr.repo}/issues/${pr.prNumber}/comments`;
|
|
280
|
+
const response = await fetch(url, {
|
|
281
|
+
method: "POST",
|
|
282
|
+
headers: {
|
|
283
|
+
Authorization: `Bearer ${this.token}`,
|
|
284
|
+
Accept: "application/vnd.github.v3+json",
|
|
285
|
+
"Content-Type": "application/json"
|
|
286
|
+
},
|
|
287
|
+
body: JSON.stringify({ body })
|
|
288
|
+
});
|
|
289
|
+
if (!response.ok) {
|
|
290
|
+
const error = await response.text();
|
|
291
|
+
throw new Error(`Failed to create GitHub comment: ${response.status} ${sanitizeErrorBody(error)}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async updateComment(commentId, body) {
|
|
295
|
+
const url = `${this.apiUrl}/repos/${this.repo}/issues/comments/${commentId}`;
|
|
296
|
+
const response = await fetch(url, {
|
|
297
|
+
method: "PATCH",
|
|
298
|
+
headers: {
|
|
299
|
+
Authorization: `Bearer ${this.token}`,
|
|
300
|
+
Accept: "application/vnd.github.v3+json",
|
|
301
|
+
"Content-Type": "application/json"
|
|
302
|
+
},
|
|
303
|
+
body: JSON.stringify({ body })
|
|
304
|
+
});
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
const error = await response.text();
|
|
307
|
+
throw new Error(`Failed to update GitHub comment: ${response.status} ${sanitizeErrorBody(error)}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/providers/detect.ts
|
|
313
|
+
function detectProvider() {
|
|
314
|
+
if (process.env.GITHUB_ACTIONS === "true") {
|
|
315
|
+
return new GitHubProvider();
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/commands/comment.ts
|
|
321
|
+
function createCommentCommand() {
|
|
322
|
+
return new Command4("comment").description("Post AIDA report as a PR/MR comment (auto-detects CI provider)").option("--out-dir <path>", "Output directory", "./aida-output").option("--verbose", "Verbose logging", false).option("--dry-run", "Print comment to stdout instead of posting", false).action(async (options) => {
|
|
323
|
+
const config = CLIConfig.parse(options);
|
|
324
|
+
const logger = createLogger4(config.verbose);
|
|
325
|
+
try {
|
|
326
|
+
const reportPath = join4(config.outDir, "report.md");
|
|
327
|
+
let content;
|
|
328
|
+
try {
|
|
329
|
+
content = await fs2.readFile(reportPath, "utf-8");
|
|
330
|
+
} catch {
|
|
331
|
+
logger.error(`Report not found at ${reportPath}. Run 'aida report' first.`);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
if (options.dryRun) {
|
|
335
|
+
logger.info("Dry run \u2014 printing report to stdout:");
|
|
336
|
+
console.log(content);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const provider = detectProvider();
|
|
340
|
+
if (!provider) {
|
|
341
|
+
logger.error(
|
|
342
|
+
"Could not detect CI provider. Supported: GitHub Actions.\nUse --dry-run to print the report to stdout instead."
|
|
343
|
+
);
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
logger.info(`Detected CI provider: ${provider.name}`);
|
|
347
|
+
const pr = provider.getPRIdentifier();
|
|
348
|
+
if (!pr) {
|
|
349
|
+
logger.error(
|
|
350
|
+
"Could not determine PR/MR number. Is this running on a pull_request event?"
|
|
351
|
+
);
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
logger.info(`Posting comment to ${provider.name} PR #${pr.prNumber}...`);
|
|
355
|
+
await provider.postComment(content);
|
|
356
|
+
logger.info("AIDA report posted as PR comment");
|
|
357
|
+
} catch (error) {
|
|
358
|
+
logger.error(
|
|
359
|
+
`Comment failed: ${error instanceof Error ? error.message : String(error)}`
|
|
360
|
+
);
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
162
366
|
// src/index.ts
|
|
163
|
-
var program = new
|
|
367
|
+
var program = new Command5();
|
|
164
368
|
program.name("aida").description("AIDA (AI Development Accounting) - Metrics for AI-assisted development").version("0.0.0");
|
|
165
369
|
program.addCommand(createCollectCommand());
|
|
166
370
|
program.addCommand(createAnalyzeCommand());
|
|
167
371
|
program.addCommand(createReportCommand());
|
|
372
|
+
program.addCommand(createCommentCommand());
|
|
168
373
|
program.parse();
|
|
169
374
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/collect.ts","../src/schema/config.ts","../src/commands/analyze.ts","../src/commands/report.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { createCollectCommand } from './commands/collect.js';\nimport { createAnalyzeCommand } from './commands/analyze.js';\nimport { createReportCommand } from './commands/report.js';\n\nconst program = new Command();\n\nprogram\n .name('aida')\n .description('AIDA (AI Development Accounting) - Metrics for AI-assisted development')\n .version('0.0.0');\n\n// Add commands\nprogram.addCommand(createCollectCommand());\nprogram.addCommand(createAnalyzeCommand());\nprogram.addCommand(createReportCommand());\n\nprogram.parse();\n","import { Command } from 'commander';\nimport { collectCommits, writeJSON, createLogger, AidaConfig } from '@aida-dev/core';\nimport { join } from 'path';\nimport { readFile } from 'fs/promises';\nimport { CLIConfig } from '../schema/config.js';\n\nasync function loadAidaConfig(repoPath: string): Promise<Partial<AidaConfig>> {\n try {\n const raw = await readFile(join(repoPath, '.aida.json'), 'utf-8');\n return AidaConfig.parse(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction collectRepeatable(value: string, previous: string[]): string[] {\n return previous ? [...previous, value] : [value];\n}\n\nexport function createCollectCommand(): Command {\n return new Command('collect')\n .description('Collect commits and generate commit-stream.json')\n .option('--repo <path>', 'Repository path', process.cwd())\n .option('--since <date>', 'Start date (ISO or relative like 90d)')\n .option('--until <date>', 'End date (ISO or relative)')\n .option('--ai-pattern <pattern>', 'AI detection regex (repeatable)', collectRepeatable, [])\n .option('--ai-tool <name>', 'Additional AI tool name (repeatable)', collectRepeatable, [])\n .option('--ai-trailer-domain <domain>', 'Additional Co-authored-by domain (repeatable)', collectRepeatable, [])\n .option('--default-branch <name>', 'Default branch name')\n .option('--out-dir <path>', 'Output directory', './aida-output')\n .option('--verbose', 'Verbose logging', false)\n .action(async (options) => {\n // Commander uses singular camelCase (--ai-tool → aiTool), schema uses plural\n const mapped = {\n ...options,\n aiPatterns: options.aiPattern || [],\n aiTools: options.aiTool || [],\n aiTrailerDomains: options.aiTrailerDomain || [],\n };\n const config = CLIConfig.parse(mapped);\n const logger = createLogger(config.verbose);\n\n try {\n // Load .aida.json config (merge with CLI flags)\n const fileConfig = await loadAidaConfig(config.repo);\n const aiPatterns = [...(fileConfig.patterns || []), ...config.aiPatterns];\n const aiTools = [...(fileConfig.tools || []), ...config.aiTools];\n const aiTrailerDomains = [...(fileConfig.trailerDomains || []), ...config.aiTrailerDomains];\n\n if (aiTools.length > 0) logger.info(`Custom AI tools: ${aiTools.join(', ')}`);\n if (aiTrailerDomains.length > 0) logger.info(`Custom trailer domains: ${aiTrailerDomains.join(', ')}`);\n\n logger.info('Starting commit collection...');\n\n const commitStream = await collectCommits({\n repoPath: config.repo,\n since: config.since,\n until: config.until,\n aiPatterns,\n aiTools,\n aiTrailerDomains,\n defaultBranch: config.defaultBranch,\n logger,\n });\n\n const outputPath = join(config.outDir, 'commit-stream.json');\n await writeJSON(outputPath, commitStream);\n\n logger.info(`Collected ${commitStream.commits.length} commits`);\n logger.info(`AI-tagged commits: ${commitStream.commits.filter((c) => c.tags.ai).length}`);\n logger.info(`Output written to: ${outputPath}`);\n } catch (error) {\n logger.error(\n `Collection failed: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n });\n}\n","import { z } from 'zod';\n\nexport const CLIConfig = z.object({\n repo: z.string().default(process.cwd()),\n since: z.string().optional(),\n until: z.string().optional(),\n aiPatterns: z.array(z.string()).default([]),\n aiTools: z.array(z.string()).default([]),\n aiTrailerDomains: z.array(z.string()).default([]),\n defaultBranch: z.string().optional(),\n outDir: z.string().default('./aida-output'),\n format: z.enum(['json', 'md', 'both']).default('both').optional(),\n verbose: z.boolean().default(false),\n});\n\nexport type CLIConfig = z.infer<typeof CLIConfig>;\n","import { Command } from 'commander';\nimport { readJSON, writeJSON, createLogger } from '@aida-dev/core';\nimport { calculateMetrics } from '@aida-dev/metrics';\nimport { join } from 'path';\nimport { CLIConfig } from '../schema/config.js';\n\nexport function createAnalyzeCommand(): Command {\n return new Command('analyze')\n .description('Analyze commit stream and generate metrics.json')\n .option('--out-dir <path>', 'Output directory', './aida-output')\n .option('--verbose', 'Verbose logging', false)\n .action(async (options) => {\n const config = CLIConfig.parse(options);\n const logger = createLogger(config.verbose);\n\n try {\n logger.info('Starting metrics analysis...');\n\n const inputPath = join(config.outDir, 'commit-stream.json');\n const commitStream = await readJSON(inputPath);\n\n logger.info(`Analyzing ${commitStream.commits.length} commits`);\n\n const metrics = calculateMetrics(commitStream);\n\n const outputPath = join(config.outDir, 'metrics.json');\n await writeJSON(outputPath, metrics);\n\n logger.info(`Merge ratio: ${(metrics.mergeRatio.mergeRatio * 100).toFixed(1)}%`);\n logger.info(`Average persistence: ${metrics.persistence.avgDays} days`);\n logger.info(`Output written to: ${outputPath}`);\n } catch (error) {\n logger.error(`Analysis failed: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n}\n","import { Command } from 'commander';\nimport { readJSON, createLogger } from '@aida-dev/core';\nimport { Metrics } from '@aida-dev/metrics';\nimport { join } from 'path';\nimport { promises as fs } from 'fs';\nimport { CLIConfig } from '../schema/config.js';\n\nfunction generateMarkdownReport(metrics: Metrics): string {\n const mergeRatioPct = (metrics.mergeRatio.mergeRatio * 100).toFixed(1);\n\n return `# AIDA Report\n\n**Repo:** ${metrics.repoPath} \n**Default branch:** ${metrics.defaultBranch} \n**Window:** ${metrics.window.since || 'beginning'} → ${metrics.window.until || 'now'} \n**Generated:** ${metrics.generatedAt}\n\n## Merge Ratio\n- AI-tagged commits (total): ${metrics.mergeRatio.aiCommitsTotal}\n- AI-tagged commits merged: ${metrics.mergeRatio.aiCommitsMerged}\n- **Merge Ratio:** ${mergeRatioPct}%\n\n## Persistence (file-level proxy)\n- Commits considered: ${metrics.persistence.commitsConsidered}\n- Average days: ${metrics.persistence.avgDays}\n- Median days: ${metrics.persistence.medianDays}\n\n| 0–1d | 2–7d | 8–30d | 31–90d | 90d+ |\n|---:|---:|---:|---:|---:|\n| ${metrics.persistence.buckets.d0_1} | ${metrics.persistence.buckets.d2_7} | ${metrics.persistence.buckets.d8_30} | ${metrics.persistence.buckets.d31_90} | ${metrics.persistence.buckets.d90_plus} |\n\n### Caveats\n${metrics.caveats.map((caveat) => `- ${caveat}`).join('\\n')}\n`;\n}\n\nexport function createReportCommand(): Command {\n return new Command('report')\n .description('Generate report from metrics.json')\n .option('--out-dir <path>', 'Output directory', './aida-output')\n .option('--verbose', 'Verbose logging', false)\n .action(async (options) => {\n const config = CLIConfig.parse(options);\n const logger = createLogger(config.verbose);\n\n try {\n logger.info('Generating report...');\n\n const inputPath = join(config.outDir, 'metrics.json');\n const metrics: Metrics = await readJSON(inputPath);\n\n const markdown = generateMarkdownReport(metrics);\n const mdPath = join(config.outDir, 'report.md');\n await fs.writeFile(mdPath, markdown, 'utf-8');\n logger.info(`Markdown report written to: ${mdPath}`);\n\n logger.info('Report generation completed');\n } catch (error) {\n logger.error(\n `Report generation failed: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n });\n}\n"],"mappings":"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","names":["Command","Command","writeJSON","createLogger","join","Command","createLogger","join","writeJSON","Command","readJSON","createLogger","join","Command","createLogger","join","readJSON","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/collect.ts","../src/schema/config.ts","../src/providers/pr-base.ts","../src/commands/analyze.ts","../src/commands/report.ts","../src/commands/comment.ts","../src/providers/github.ts","../src/providers/detect.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { createCollectCommand } from './commands/collect.js';\nimport { createAnalyzeCommand } from './commands/analyze.js';\nimport { createReportCommand } from './commands/report.js';\nimport { createCommentCommand } from './commands/comment.js';\n\nconst program = new Command();\n\nprogram\n .name('aida')\n .description('AIDA (AI Development Accounting) - Metrics for AI-assisted development')\n .version('0.0.0');\n\n// Add commands\nprogram.addCommand(createCollectCommand());\nprogram.addCommand(createAnalyzeCommand());\nprogram.addCommand(createReportCommand());\nprogram.addCommand(createCommentCommand());\n\nprogram.parse();\n","import { Command } from 'commander';\nimport { collectCommits, writeJSON, createLogger, AidaConfig } from '@aida-dev/core';\nimport { join } from 'path';\nimport { readFile } from 'fs/promises';\nimport { CLIConfig } from '../schema/config.js';\nimport { detectPRBaseRef } from '../providers/pr-base.js';\n\nasync function loadAidaConfig(repoPath: string): Promise<Partial<AidaConfig>> {\n try {\n const raw = await readFile(join(repoPath, '.aida.json'), 'utf-8');\n return AidaConfig.parse(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction collectRepeatable(value: string, previous: string[]): string[] {\n return previous ? [...previous, value] : [value];\n}\n\nexport function createCollectCommand(): Command {\n return new Command('collect')\n .description('Collect commits and generate commit-stream.json')\n .option('--repo <path>', 'Repository path', process.cwd())\n .option('--since <date>', 'Start date (ISO or relative like 90d)')\n .option('--until <date>', 'End date (ISO or relative)')\n .option('--pr', 'PR-scoped analysis (auto-detect base ref from CI env vars)')\n .option('--diff-base <ref>', 'Explicit base ref for PR-scoped analysis (e.g., origin/main)')\n .option('--ai-pattern <pattern>', 'AI detection regex (repeatable)', collectRepeatable, [])\n .option('--ai-tool <name>', 'Additional AI tool name (repeatable)', collectRepeatable, [])\n .option('--ai-trailer-domain <domain>', 'Additional Co-authored-by domain (repeatable)', collectRepeatable, [])\n .option('--default-branch <name>', 'Default branch name')\n .option('--out-dir <path>', 'Output directory', './aida-output')\n .option('--verbose', 'Verbose logging', false)\n .action(async (options) => {\n // Commander uses singular camelCase (--ai-tool → aiTool), schema uses plural\n const mapped = {\n ...options,\n aiPatterns: options.aiPattern || [],\n aiTools: options.aiTool || [],\n aiTrailerDomains: options.aiTrailerDomain || [],\n };\n const config = CLIConfig.parse(mapped);\n const logger = createLogger(config.verbose);\n\n try {\n // Load .aida.json config (merge with CLI flags)\n const fileConfig = await loadAidaConfig(config.repo);\n const aiPatterns = [...(fileConfig.patterns || []), ...config.aiPatterns];\n const aiTools = [...(fileConfig.tools || []), ...config.aiTools];\n const aiTrailerDomains = [...(fileConfig.trailerDomains || []), ...config.aiTrailerDomains];\n\n if (aiTools.length > 0) logger.info(`Custom AI tools: ${aiTools.join(', ')}`);\n if (aiTrailerDomains.length > 0) logger.info(`Custom trailer domains: ${aiTrailerDomains.join(', ')}`);\n\n // Determine diffBase for PR-scoped analysis\n let diffBase: string | undefined = options.diffBase;\n if (options.pr && !diffBase) {\n diffBase = detectPRBaseRef() ?? undefined;\n if (diffBase) {\n logger.info(`PR mode: detected base ref ${diffBase}`);\n } else {\n logger.warn('--pr flag used but no PR context detected. Falling back to --since mode.');\n }\n }\n\n logger.info('Starting commit collection...');\n\n const commitStream = await collectCommits({\n repoPath: config.repo,\n since: diffBase ? undefined : config.since,\n until: diffBase ? undefined : config.until,\n diffBase,\n aiPatterns,\n aiTools,\n aiTrailerDomains,\n defaultBranch: config.defaultBranch,\n logger,\n });\n\n const outputPath = join(config.outDir, 'commit-stream.json');\n await writeJSON(outputPath, commitStream);\n\n logger.info(`Collected ${commitStream.commits.length} commits`);\n logger.info(`AI-tagged commits: ${commitStream.commits.filter((c) => c.tags.ai).length}`);\n logger.info(`Output written to: ${outputPath}`);\n } catch (error) {\n logger.error(\n `Collection failed: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n });\n}\n","import { z } from 'zod';\n\nexport const CLIConfig = z.object({\n repo: z.string().default(process.cwd()),\n since: z.string().optional(),\n until: z.string().optional(),\n aiPatterns: z.array(z.string()).default([]),\n aiTools: z.array(z.string()).default([]),\n aiTrailerDomains: z.array(z.string()).default([]),\n defaultBranch: z.string().optional(),\n outDir: z.string().default('./aida-output'),\n verbose: z.boolean().default(false),\n});\n\nexport type CLIConfig = z.infer<typeof CLIConfig>;\n","/**\n * Detect the PR base ref from CI environment variables.\n * Returns the base branch/ref that the PR is targeting, or null if not in a PR context.\n */\nexport function detectPRBaseRef(): string | null {\n // GitHub Actions\n // GITHUB_BASE_REF is set on pull_request events\n if (process.env.GITHUB_ACTIONS === 'true' && process.env.GITHUB_BASE_REF) {\n return `origin/${process.env.GITHUB_BASE_REF}`;\n }\n\n // GitLab CI\n // CI_MERGE_REQUEST_DIFF_BASE_SHA is the commit SHA of the base branch\n // CI_MERGE_REQUEST_TARGET_BRANCH_NAME is the branch name\n if (process.env.GITLAB_CI === 'true') {\n if (process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA) {\n return process.env.CI_MERGE_REQUEST_DIFF_BASE_SHA;\n }\n if (process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME) {\n return `origin/${process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME}`;\n }\n }\n\n // Bitbucket Pipelines\n if (process.env.BITBUCKET_PIPELINE_UUID) {\n if (process.env.BITBUCKET_PR_DESTINATION_BRANCH) {\n return `origin/${process.env.BITBUCKET_PR_DESTINATION_BRANCH}`;\n }\n }\n\n // Azure DevOps\n if (process.env.TF_BUILD === 'True') {\n // SYSTEM_PULLREQUEST_TARGETBRANCH contains refs/heads/main format\n const targetBranch = process.env.SYSTEM_PULLREQUEST_TARGETBRANCH;\n if (targetBranch) {\n const branchName = targetBranch.replace('refs/heads/', '');\n return `origin/${branchName}`;\n }\n }\n\n return null;\n}\n\n/**\n * Check if we're currently running in a PR/MR context.\n */\nexport function isInPRContext(): boolean {\n return detectPRBaseRef() !== null;\n}\n","import { Command } from 'commander';\nimport { readJSON, writeJSON, createLogger, CommitStream } from '@aida-dev/core';\nimport { calculateMetrics } from '@aida-dev/metrics';\nimport { join } from 'path';\nimport { CLIConfig } from '../schema/config.js';\n\nexport function createAnalyzeCommand(): Command {\n return new Command('analyze')\n .description('Analyze commit stream and generate metrics.json')\n .option('--out-dir <path>', 'Output directory', './aida-output')\n .option('--verbose', 'Verbose logging', false)\n .action(async (options) => {\n const config = CLIConfig.parse(options);\n const logger = createLogger(config.verbose);\n\n try {\n logger.info('Starting metrics analysis...');\n\n const inputPath = join(config.outDir, 'commit-stream.json');\n const commitStream = await readJSON(inputPath, CommitStream);\n\n logger.info(`Analyzing ${commitStream.commits.length} commits`);\n\n const metrics = calculateMetrics(commitStream);\n\n const outputPath = join(config.outDir, 'metrics.json');\n await writeJSON(outputPath, metrics);\n\n logger.info(`Merge ratio: ${(metrics.mergeRatio.mergeRatio * 100).toFixed(1)}%`);\n logger.info(`Average persistence: ${metrics.persistence.avgDays} days`);\n logger.info(`Output written to: ${outputPath}`);\n } catch (error) {\n logger.error(`Analysis failed: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n }\n });\n}\n","import { Command } from 'commander';\nimport { readJSON, createLogger } from '@aida-dev/core';\nimport { Metrics } from '@aida-dev/metrics';\nimport { join } from 'path';\nimport { promises as fs } from 'fs';\nimport { CLIConfig } from '../schema/config.js';\n\nfunction generateMarkdownReport(metrics: Metrics): string {\n const mergeRatioPct = (metrics.mergeRatio.mergeRatio * 100).toFixed(1);\n\n return `# AIDA Report\n\n**Repo:** ${metrics.repoPath} \n**Default branch:** ${metrics.defaultBranch} \n**Window:** ${metrics.window.since || 'beginning'} → ${metrics.window.until || 'now'} \n**Generated:** ${metrics.generatedAt}\n\n## Merge Ratio\n- AI-tagged commits (total): ${metrics.mergeRatio.aiCommitsTotal}\n- AI-tagged commits merged: ${metrics.mergeRatio.aiCommitsMerged}\n- **Merge Ratio:** ${mergeRatioPct}%\n\n## Persistence (file-level proxy)\n- Commits considered: ${metrics.persistence.commitsConsidered}\n- Average days: ${metrics.persistence.avgDays}\n- Median days: ${metrics.persistence.medianDays}\n\n| 0–1d | 2–7d | 8–30d | 31–90d | 90d+ |\n|---:|---:|---:|---:|---:|\n| ${metrics.persistence.buckets.d0_1} | ${metrics.persistence.buckets.d2_7} | ${metrics.persistence.buckets.d8_30} | ${metrics.persistence.buckets.d31_90} | ${metrics.persistence.buckets.d90_plus} |\n\n### Caveats\n${metrics.caveats.map((caveat) => `- ${caveat}`).join('\\n')}\n`;\n}\n\nexport function createReportCommand(): Command {\n return new Command('report')\n .description('Generate report from metrics.json')\n .option('--out-dir <path>', 'Output directory', './aida-output')\n .option('--verbose', 'Verbose logging', false)\n .action(async (options) => {\n const config = CLIConfig.parse(options);\n const logger = createLogger(config.verbose);\n\n try {\n logger.info('Generating report...');\n\n const inputPath = join(config.outDir, 'metrics.json');\n const metrics = await readJSON(inputPath, Metrics);\n\n const markdown = generateMarkdownReport(metrics);\n const mdPath = join(config.outDir, 'report.md');\n await fs.writeFile(mdPath, markdown, 'utf-8');\n logger.info(`Markdown report written to: ${mdPath}`);\n\n logger.info('Report generation completed');\n } catch (error) {\n logger.error(\n `Report generation failed: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n });\n}\n","import { Command } from 'commander';\nimport { createLogger } from '@aida-dev/core';\nimport { join } from 'path';\nimport { promises as fs } from 'fs';\nimport { CLIConfig } from '../schema/config.js';\nimport { detectProvider } from '../providers/detect.js';\n\nexport function createCommentCommand(): Command {\n return new Command('comment')\n .description('Post AIDA report as a PR/MR comment (auto-detects CI provider)')\n .option('--out-dir <path>', 'Output directory', './aida-output')\n .option('--verbose', 'Verbose logging', false)\n .option('--dry-run', 'Print comment to stdout instead of posting', false)\n .action(async (options) => {\n const config = CLIConfig.parse(options);\n const logger = createLogger(config.verbose);\n\n try {\n const reportPath = join(config.outDir, 'report.md');\n let content: string;\n\n try {\n content = await fs.readFile(reportPath, 'utf-8');\n } catch {\n logger.error(`Report not found at ${reportPath}. Run 'aida report' first.`);\n process.exit(1);\n }\n\n // Dry run — just print to stdout\n if (options.dryRun) {\n logger.info('Dry run — printing report to stdout:');\n console.log(content);\n return;\n }\n\n // Detect CI provider\n const provider = detectProvider();\n if (!provider) {\n logger.error(\n 'Could not detect CI provider. Supported: GitHub Actions.\\n' +\n 'Use --dry-run to print the report to stdout instead.'\n );\n process.exit(1);\n }\n\n logger.info(`Detected CI provider: ${provider.name}`);\n\n const pr = provider.getPRIdentifier();\n if (!pr) {\n logger.error(\n 'Could not determine PR/MR number. Is this running on a pull_request event?'\n );\n process.exit(1);\n }\n\n logger.info(`Posting comment to ${provider.name} PR #${pr.prNumber}...`);\n await provider.postComment(content);\n logger.info('AIDA report posted as PR comment');\n } catch (error) {\n logger.error(\n `Comment failed: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n });\n}\n","import type { CIProvider, PRIdentifier } from './types.js';\nimport { readFileSync } from 'fs';\n\nconst MARKER = '<!-- aida-metrics-report -->';\n\nfunction sanitizeErrorBody(text: string, maxLength = 200): string {\n // Strip potential tokens, auth headers, and URLs with credentials\n let sanitized = text\n .replace(/ghp_[A-Za-z0-9_]+/g, '[REDACTED]')\n .replace(/gho_[A-Za-z0-9_]+/g, '[REDACTED]')\n .replace(/github_pat_[A-Za-z0-9_]+/g, '[REDACTED]')\n .replace(/Bearer\\s+[A-Za-z0-9._-]+/gi, 'Bearer [REDACTED]')\n .replace(/token\\s+[A-Za-z0-9._-]+/gi, 'token [REDACTED]');\n if (sanitized.length > maxLength) {\n sanitized = sanitized.slice(0, maxLength) + '...(truncated)';\n }\n return sanitized;\n}\n\ninterface GitHubEvent {\n pull_request?: {\n number: number;\n };\n number?: number;\n}\n\nexport class GitHubProvider implements CIProvider {\n name = 'github';\n\n private get token(): string {\n const token = process.env.GITHUB_TOKEN;\n if (!token) {\n throw new Error('GITHUB_TOKEN is required for GitHub PR comments. Pass it via env.');\n }\n return token;\n }\n\n private get repo(): string {\n const repo = process.env.GITHUB_REPOSITORY;\n if (!repo) {\n throw new Error('GITHUB_REPOSITORY env var not found. Are you running in GitHub Actions?');\n }\n return repo;\n }\n\n private get apiUrl(): string {\n return process.env.GITHUB_API_URL || 'https://api.github.com';\n }\n\n getPRIdentifier(): PRIdentifier | null {\n const eventPath = process.env.GITHUB_EVENT_PATH;\n if (!eventPath) return null;\n\n try {\n const event: GitHubEvent = JSON.parse(readFileSync(eventPath, 'utf-8'));\n const prNumber = event.pull_request?.number || event.number;\n if (!prNumber) return null;\n\n return {\n provider: 'github',\n prNumber: String(prNumber),\n repo: this.repo,\n };\n } catch {\n return null;\n }\n }\n\n async postComment(content: string, marker: string = MARKER): Promise<void> {\n const pr = this.getPRIdentifier();\n if (!pr) {\n throw new Error('Could not determine PR number. Is this running on a pull_request event?');\n }\n\n const markedContent = `${marker}\\n${content}`;\n\n // Try to find and update existing comment\n const existingId = await this.findExistingComment(pr, marker);\n\n if (existingId) {\n await this.updateComment(existingId, markedContent);\n } else {\n await this.createComment(pr, markedContent);\n }\n }\n\n private async findExistingComment(pr: PRIdentifier, marker: string): Promise<number | null> {\n const url = `${this.apiUrl}/repos/${pr.repo}/issues/${pr.prNumber}/comments?per_page=100`;\n\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n Accept: 'application/vnd.github.v3+json',\n },\n });\n\n if (!response.ok) return null;\n\n const comments = (await response.json()) as Array<{ id: number; body: string }>;\n const existing = comments.find((c) => c.body.startsWith(marker));\n return existing?.id || null;\n }\n\n private async createComment(pr: PRIdentifier, body: string): Promise<void> {\n const url = `${this.apiUrl}/repos/${pr.repo}/issues/${pr.prNumber}/comments`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.token}`,\n Accept: 'application/vnd.github.v3+json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ body }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to create GitHub comment: ${response.status} ${sanitizeErrorBody(error)}`);\n }\n }\n\n private async updateComment(commentId: number, body: string): Promise<void> {\n const url = `${this.apiUrl}/repos/${this.repo}/issues/comments/${commentId}`;\n\n const response = await fetch(url, {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${this.token}`,\n Accept: 'application/vnd.github.v3+json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ body }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to update GitHub comment: ${response.status} ${sanitizeErrorBody(error)}`);\n }\n }\n}\n","import type { CIProvider } from './types.js';\nimport { GitHubProvider } from './github.js';\n\nexport function detectProvider(): CIProvider | null {\n // GitHub Actions\n if (process.env.GITHUB_ACTIONS === 'true') {\n return new GitHubProvider();\n }\n\n // GitLab CI — not yet implemented\n // if (process.env.GITLAB_CI === 'true') { ... }\n\n // Bitbucket Pipelines — not yet implemented\n // if (process.env.BITBUCKET_PIPELINE_UUID) { ... }\n\n // Azure DevOps — not yet implemented\n // if (process.env.TF_BUILD === 'True') { ... }\n\n return null;\n}\n"],"mappings":"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","names":["Command","Command","writeJSON","createLogger","join","Command","createLogger","join","writeJSON","Command","readJSON","createLogger","join","Command","createLogger","join","readJSON","Command","createLogger","join","fs","Command","createLogger","join","fs","Command"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aida-dev/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "CLI for AIDA (AI Development Accounting) - Track and measure AI-assisted development",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"module": "dist/index.js",
|
|
@@ -11,14 +12,38 @@
|
|
|
11
12
|
"bin": {
|
|
12
13
|
"aida": "dist/index.js"
|
|
13
14
|
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"development",
|
|
18
|
+
"metrics",
|
|
19
|
+
"git",
|
|
20
|
+
"cli",
|
|
21
|
+
"copilot",
|
|
22
|
+
"cursor",
|
|
23
|
+
"claude"
|
|
24
|
+
],
|
|
25
|
+
"author": "Francesco Falanga <falanga.fra@gmail.com>",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"homepage": "https://github.com/ceccode/aida-metrics#readme",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/ceccode/aida-metrics.git",
|
|
31
|
+
"directory": "packages/cli"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/ceccode/aida-metrics/issues"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
14
39
|
"publishConfig": {
|
|
15
40
|
"access": "public"
|
|
16
41
|
},
|
|
17
42
|
"dependencies": {
|
|
18
43
|
"commander": "^11.1.0",
|
|
19
44
|
"zod": "^3.22.4",
|
|
20
|
-
"@aida-dev/core": "0.
|
|
21
|
-
"@aida-dev/metrics": "0.
|
|
45
|
+
"@aida-dev/core": "0.7.0",
|
|
46
|
+
"@aida-dev/metrics": "0.2.0"
|
|
22
47
|
},
|
|
23
48
|
"devDependencies": {
|
|
24
49
|
"tsup": "^8.0.0",
|