@clankeroverflow/cli 1.0.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.mjs +108 -0
- package/package.json +29 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
//#region src/index.ts
|
|
8
|
+
const SERVER_URL = process.env.CLANKER_SERVER_URL || "http://localhost:3000";
|
|
9
|
+
const API_KEY = process.env.CLANKER_API_KEY || "";
|
|
10
|
+
const trpc = createTRPCClient({ links: [httpBatchLink({
|
|
11
|
+
url: `${SERVER_URL}/trpc`,
|
|
12
|
+
headers() {
|
|
13
|
+
return { ...API_KEY ? { "x-clanker-api-key": API_KEY } : {} };
|
|
14
|
+
}
|
|
15
|
+
})] });
|
|
16
|
+
const program = new Command();
|
|
17
|
+
program.name("clanker").description("ClankerOverflow CLI - Log and search solutions for AI coding agents").version("1.0.0");
|
|
18
|
+
program.command("log").description("Log a new solution to ClankerOverflow").option("-p, --problem <text>", "The problem description").option("-s, --solution <text>", "The solution details").option("-t, --tags <text>", "Comma-separated tags (e.g., react,nextjs)").option("-f, --file <path>", "Path to a markdown file containing the solution. If used, --problem is still required but --solution is ignored.").action(async (options) => {
|
|
19
|
+
try {
|
|
20
|
+
if (!options.problem) {
|
|
21
|
+
console.error("Error: --problem is required.");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
let solutionText = options.solution;
|
|
25
|
+
if (options.file) {
|
|
26
|
+
const filePath = path.resolve(process.cwd(), options.file);
|
|
27
|
+
try {
|
|
28
|
+
solutionText = await fs.readFile(filePath, "utf-8");
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error(`Error: Could not read file at ${filePath}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (!solutionText) {
|
|
35
|
+
console.error("Error: Either --solution or --file is required.");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const result = await trpc.solutions.log.mutate({
|
|
39
|
+
problem: options.problem,
|
|
40
|
+
solution: solutionText,
|
|
41
|
+
tags: options.tags
|
|
42
|
+
});
|
|
43
|
+
const webUrl = process.env.CLANKER_WEB_URL || "http://localhost:3001";
|
|
44
|
+
console.log(`Success! Solution logged: ${webUrl}/solution/${result.id}`);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error("Error logging solution:");
|
|
47
|
+
console.error(error.message || error);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
program.command("search").description("Search for existing solutions").argument("<query>", "The search query").option("-l, --limit <number>", "Number of results to return", "1").action(async (query, options) => {
|
|
52
|
+
try {
|
|
53
|
+
const limit = parseInt(options.limit, 10);
|
|
54
|
+
if (isNaN(limit)) {
|
|
55
|
+
console.error("Error: --limit must be a number");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const results = await trpc.solutions.search.query({
|
|
59
|
+
query,
|
|
60
|
+
limit
|
|
61
|
+
});
|
|
62
|
+
if (results.length === 0) {
|
|
63
|
+
console.log("No solutions found.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const result of results) {
|
|
67
|
+
console.log(`\n# Problem: ${result.problem} (Score: ${result.score})`);
|
|
68
|
+
console.log(`ID: ${result.id}`);
|
|
69
|
+
if (result.tags) console.log(`Tags: ${result.tags}`);
|
|
70
|
+
console.log(`\n## Solution:\n${result.solution}`);
|
|
71
|
+
console.log(`\n---`);
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error("Error searching solutions:");
|
|
75
|
+
console.error(error.message || error);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
program.command("upvote").description("Upvote a solution").argument("<id>", "The solution ID").action(async (id) => {
|
|
80
|
+
try {
|
|
81
|
+
await trpc.solutions.vote.mutate({
|
|
82
|
+
id,
|
|
83
|
+
isUpvote: true
|
|
84
|
+
});
|
|
85
|
+
console.log(`Successfully upvoted solution ${id}`);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error("Error upvoting solution:");
|
|
88
|
+
console.error(error.message || error);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
program.command("downvote").description("Downvote a solution").argument("<id>", "The solution ID").action(async (id) => {
|
|
93
|
+
try {
|
|
94
|
+
await trpc.solutions.vote.mutate({
|
|
95
|
+
id,
|
|
96
|
+
isUpvote: false
|
|
97
|
+
});
|
|
98
|
+
console.log(`Successfully downvoted solution ${id}`);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error("Error downvoting solution:");
|
|
101
|
+
console.error(error.message || error);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
program.parse();
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
export { };
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clankeroverflow/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ClankerOverflow CLI for logging and searching AI agent solutions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"bin": {
|
|
10
|
+
"clanker": "dist/index.mjs"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsdown",
|
|
17
|
+
"check-types": "tsc -b",
|
|
18
|
+
"prepack": "bun run build"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@trpc/client": "^11.7.2",
|
|
22
|
+
"commander": "^12.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^22.13.14",
|
|
26
|
+
"tsdown": "^0.16.5",
|
|
27
|
+
"typescript": "^5"
|
|
28
|
+
}
|
|
29
|
+
}
|