@growthub/cli 0.10.0 → 0.12.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/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/apply/route.js +307 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/query/route.js +372 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/receipts/route.js +47 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +664 -82
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +1371 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +1383 -24
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +7 -21
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/ownership/ownership-panel.jsx +222 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/ownership/page.jsx +19 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/settings-shell.jsx +2 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +116 -24
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +497 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/growthub.config.json +20 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-intelligence.js +19 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +23 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper-apply.js +473 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +583 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package-lock.json +34 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +3 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/export-training-traces.mjs +144 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/grade-raw-pairs.mjs +279 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/harvest-cursor-traces.mjs +288 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/upload-graded-traces.mjs +128 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +19 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/alignment-loop.config.json +264 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/workers/custom-workspace-operator/CLAUDE.md +38 -0
- package/dist/index.js +1416 -2627
- package/package.json +1 -1
package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/harvest-cursor-traces.mjs
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* helpers/harvest-cursor-traces.mjs — Distillation Pipeline V1, Phase 1
|
|
4
|
+
*
|
|
5
|
+
* Reads Cursor agent transcript JSONL files (one folder per session, one
|
|
6
|
+
* `<uuid>.jsonl` inside it), pairs each user query with the assistant turn(s)
|
|
7
|
+
* that follow it, filters to pairs where the assistant actually executed work
|
|
8
|
+
* (≥1 `tool_use` block), and emits a single newline-delimited JSON file ready
|
|
9
|
+
* for Phase 2 (critic-grader scoring) and Phase 3 (Unsloth QLoRA export).
|
|
10
|
+
*
|
|
11
|
+
* Quality signals captured per pair (used by the grader, not graded here):
|
|
12
|
+
* - `executedWork` true ⇔ assistant turn produced ≥1 tool_use
|
|
13
|
+
* - `toolUseCount` how many tool calls were issued
|
|
14
|
+
* - `toolNames` deduped list of tool names invoked
|
|
15
|
+
* - `branchesTouched` branch names parsed out of git/gh shell commands
|
|
16
|
+
* - `mergedToMain` true if any branch matches a squash-merged PR on main
|
|
17
|
+
* - `mergedPrNumbers` PR numbers whose `headRefName` matched
|
|
18
|
+
*
|
|
19
|
+
* Squash-to-main is the highest-signal heuristic: it means the work was
|
|
20
|
+
* accepted by the maintainer and shipped. The grader can boost those rows.
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* node helpers/harvest-cursor-traces.mjs \
|
|
24
|
+
* --in /Users/antonio/.cursor/projects/Users-antonio-growthub-local/agent-transcripts \
|
|
25
|
+
* --out ./antonio/distillation/raw-pairs.jsonl \
|
|
26
|
+
* --repo /Users/antonio/growthub-local # optional: enables gh PR enrichment
|
|
27
|
+
* --pr-limit 200 # optional: how many merged PRs to fetch
|
|
28
|
+
* --min-prompt-chars 12 # optional: drop trivial prompts
|
|
29
|
+
*
|
|
30
|
+
* No network calls beyond `gh pr list` (only when --repo is provided).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { spawnSync } from "node:child_process";
|
|
34
|
+
import fs from "node:fs";
|
|
35
|
+
import path from "node:path";
|
|
36
|
+
|
|
37
|
+
// ---------- arg parsing ----------
|
|
38
|
+
function parseArgs(argv) {
|
|
39
|
+
const a = { in: "", out: "", repo: "", prLimit: 200, minPromptChars: 12 };
|
|
40
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
41
|
+
const t = argv[i];
|
|
42
|
+
const next = () => String(argv[++i] || "").trim();
|
|
43
|
+
if (t === "--in") a.in = next();
|
|
44
|
+
else if (t === "--out") a.out = next();
|
|
45
|
+
else if (t === "--repo") a.repo = next();
|
|
46
|
+
else if (t === "--pr-limit") a.prLimit = Number(next()) || 200;
|
|
47
|
+
else if (t === "--min-prompt-chars") a.minPromptChars = Number(next()) || 0;
|
|
48
|
+
else if (t === "--help" || t === "-h") {
|
|
49
|
+
process.stdout.write(
|
|
50
|
+
"Usage: harvest-cursor-traces.mjs --in <transcripts-dir> --out <raw-pairs.jsonl> [--repo <git-repo>] [--pr-limit N] [--min-prompt-chars N]\n",
|
|
51
|
+
);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!a.in || !a.out) {
|
|
56
|
+
process.stderr.write("error: --in and --out are required\n");
|
|
57
|
+
process.exit(2);
|
|
58
|
+
}
|
|
59
|
+
return a;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const args = parseArgs(process.argv.slice(2));
|
|
63
|
+
const transcriptsDir = path.resolve(args.in);
|
|
64
|
+
const outPath = path.resolve(args.out);
|
|
65
|
+
const repoDir = args.repo ? path.resolve(args.repo) : "";
|
|
66
|
+
|
|
67
|
+
// ---------- gh squash-merge index ----------
|
|
68
|
+
/** Map<branchName, { number, mergeSha, mergedAt, title }> */
|
|
69
|
+
const mergedByBranch = new Map();
|
|
70
|
+
if (repoDir) {
|
|
71
|
+
if (!fs.existsSync(path.join(repoDir, ".git"))) {
|
|
72
|
+
process.stderr.write(`warn: --repo ${repoDir} has no .git dir; skipping gh enrichment\n`);
|
|
73
|
+
} else {
|
|
74
|
+
const r = spawnSync(
|
|
75
|
+
"gh",
|
|
76
|
+
[
|
|
77
|
+
"pr",
|
|
78
|
+
"list",
|
|
79
|
+
"--state",
|
|
80
|
+
"merged",
|
|
81
|
+
"--limit",
|
|
82
|
+
String(args.prLimit),
|
|
83
|
+
"--json",
|
|
84
|
+
"number,title,headRefName,mergeCommit,mergedAt",
|
|
85
|
+
],
|
|
86
|
+
{ cwd: repoDir, encoding: "utf8" },
|
|
87
|
+
);
|
|
88
|
+
if (r.status === 0) {
|
|
89
|
+
try {
|
|
90
|
+
const list = JSON.parse(r.stdout || "[]");
|
|
91
|
+
for (const pr of list) {
|
|
92
|
+
if (typeof pr.headRefName === "string" && pr.headRefName.trim()) {
|
|
93
|
+
mergedByBranch.set(pr.headRefName.trim(), {
|
|
94
|
+
number: pr.number,
|
|
95
|
+
mergeSha: pr.mergeCommit?.oid || null,
|
|
96
|
+
mergedAt: pr.mergedAt || null,
|
|
97
|
+
title: pr.title || "",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (e) {
|
|
102
|
+
process.stderr.write(`warn: gh JSON parse failed: ${e.message}\n`);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
process.stderr.write(`warn: gh pr list failed (${r.status}); continuing without merge index\n`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ---------- transcript walker ----------
|
|
111
|
+
function listTranscriptFiles(rootDir) {
|
|
112
|
+
if (!fs.existsSync(rootDir)) {
|
|
113
|
+
process.stderr.write(`error: transcripts dir not found: ${rootDir}\n`);
|
|
114
|
+
process.exit(2);
|
|
115
|
+
}
|
|
116
|
+
const out = [];
|
|
117
|
+
for (const entry of fs.readdirSync(rootDir, { withFileTypes: true })) {
|
|
118
|
+
const sessionDir = path.join(rootDir, entry.name);
|
|
119
|
+
if (!entry.isDirectory()) continue;
|
|
120
|
+
for (const child of fs.readdirSync(sessionDir, { withFileTypes: true })) {
|
|
121
|
+
if (child.isFile() && child.name.endsWith(".jsonl")) {
|
|
122
|
+
out.push({ sessionId: entry.name, file: path.join(sessionDir, child.name) });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return out.sort((a, b) => a.sessionId.localeCompare(b.sessionId));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------- helpers ----------
|
|
130
|
+
function flattenAssistantText(blocks) {
|
|
131
|
+
if (!Array.isArray(blocks)) return "";
|
|
132
|
+
return blocks
|
|
133
|
+
.filter((b) => b && b.type === "text" && typeof b.text === "string")
|
|
134
|
+
.map((b) => b.text)
|
|
135
|
+
.join("\n\n")
|
|
136
|
+
.trim();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function extractToolNames(blocks) {
|
|
140
|
+
if (!Array.isArray(blocks)) return [];
|
|
141
|
+
return blocks
|
|
142
|
+
.filter((b) => b && b.type === "tool_use" && typeof b.name === "string")
|
|
143
|
+
.map((b) => b.name);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function extractToolInputs(blocks) {
|
|
147
|
+
if (!Array.isArray(blocks)) return [];
|
|
148
|
+
return blocks
|
|
149
|
+
.filter((b) => b && b.type === "tool_use")
|
|
150
|
+
.map((b) => ({ name: b.name, input: b.input ?? {} }));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const BRANCH_FROM_GH_PR = /(?:gh\s+pr\s+(?:create|merge|view|checkout)[^\n]*?)(?:--head|head\s*[:=])\s+([\w./-]+)/g;
|
|
154
|
+
const BRANCH_FROM_GIT_PUSH = /git\s+push\s+(?:-u\s+)?(?:origin)\s+([\w./-]+)/g;
|
|
155
|
+
const BRANCH_FROM_GIT_CHECKOUT = /git\s+checkout\s+(?:-b\s+)?([\w./-]+)/g;
|
|
156
|
+
const BRANCH_FROM_BRANCH_FLAG = /(?:^|\s)(?:--branch|--head|--base)\s+([\w./-]+)/g;
|
|
157
|
+
|
|
158
|
+
function extractBranchesFromShell(toolUses) {
|
|
159
|
+
const out = new Set();
|
|
160
|
+
for (const tu of toolUses) {
|
|
161
|
+
if (tu.name !== "Shell") continue;
|
|
162
|
+
const cmd = String(tu.input?.command || "");
|
|
163
|
+
if (!cmd) continue;
|
|
164
|
+
for (const re of [BRANCH_FROM_GH_PR, BRANCH_FROM_GIT_PUSH, BRANCH_FROM_GIT_CHECKOUT, BRANCH_FROM_BRANCH_FLAG]) {
|
|
165
|
+
let m;
|
|
166
|
+
re.lastIndex = 0;
|
|
167
|
+
while ((m = re.exec(cmd))) {
|
|
168
|
+
const name = (m[1] || "").trim();
|
|
169
|
+
if (name && name !== "main" && name !== "HEAD") out.add(name);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return [...out];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ---------- main pairing pass ----------
|
|
177
|
+
const files = listTranscriptFiles(transcriptsDir);
|
|
178
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
179
|
+
const outStream = fs.createWriteStream(outPath, { encoding: "utf8" });
|
|
180
|
+
|
|
181
|
+
const stats = {
|
|
182
|
+
sessions: 0,
|
|
183
|
+
pairsConsidered: 0,
|
|
184
|
+
pairsKept: 0,
|
|
185
|
+
pairsDroppedNoTool: 0,
|
|
186
|
+
pairsDroppedShortPrompt: 0,
|
|
187
|
+
pairsMergedToMain: 0,
|
|
188
|
+
toolNameTotals: {},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
for (const { sessionId, file } of files) {
|
|
192
|
+
stats.sessions += 1;
|
|
193
|
+
const lines = fs.readFileSync(file, "utf8").split("\n").filter(Boolean);
|
|
194
|
+
|
|
195
|
+
/** @type {Array<{role:string,content:any}>} */
|
|
196
|
+
const turns = [];
|
|
197
|
+
for (const ln of lines) {
|
|
198
|
+
try {
|
|
199
|
+
const j = JSON.parse(ln);
|
|
200
|
+
if (j && typeof j.role === "string" && j.message?.content) {
|
|
201
|
+
turns.push({ role: j.role, content: j.message.content });
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
// skip malformed line
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let pairIndex = 0;
|
|
209
|
+
for (let i = 0; i < turns.length; i += 1) {
|
|
210
|
+
if (turns[i].role !== "user") continue;
|
|
211
|
+
const userBlocks = Array.isArray(turns[i].content) ? turns[i].content : [];
|
|
212
|
+
const userText = userBlocks
|
|
213
|
+
.filter((b) => b && b.type === "text" && typeof b.text === "string")
|
|
214
|
+
.map((b) => b.text)
|
|
215
|
+
.join("\n\n")
|
|
216
|
+
.trim();
|
|
217
|
+
if (!userText) continue;
|
|
218
|
+
|
|
219
|
+
// Collect every consecutive assistant turn until the next user turn.
|
|
220
|
+
const assistantBlocks = [];
|
|
221
|
+
let j = i + 1;
|
|
222
|
+
while (j < turns.length && turns[j].role !== "user") {
|
|
223
|
+
const c = turns[j].content;
|
|
224
|
+
if (Array.isArray(c)) assistantBlocks.push(...c);
|
|
225
|
+
j += 1;
|
|
226
|
+
}
|
|
227
|
+
if (assistantBlocks.length === 0) continue;
|
|
228
|
+
|
|
229
|
+
stats.pairsConsidered += 1;
|
|
230
|
+
|
|
231
|
+
if (userText.length < args.minPromptChars) {
|
|
232
|
+
stats.pairsDroppedShortPrompt += 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const toolUses = extractToolInputs(assistantBlocks);
|
|
237
|
+
if (toolUses.length === 0) {
|
|
238
|
+
stats.pairsDroppedNoTool += 1;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const assistantText = flattenAssistantText(assistantBlocks);
|
|
243
|
+
const toolNames = [...new Set(extractToolNames(assistantBlocks))];
|
|
244
|
+
for (const n of toolNames) stats.toolNameTotals[n] = (stats.toolNameTotals[n] || 0) + 1;
|
|
245
|
+
|
|
246
|
+
const branchesTouched = extractBranchesFromShell(toolUses);
|
|
247
|
+
const mergedPrNumbers = [];
|
|
248
|
+
for (const b of branchesTouched) {
|
|
249
|
+
const hit = mergedByBranch.get(b);
|
|
250
|
+
if (hit) mergedPrNumbers.push(hit.number);
|
|
251
|
+
}
|
|
252
|
+
const mergedToMain = mergedPrNumbers.length > 0;
|
|
253
|
+
if (mergedToMain) stats.pairsMergedToMain += 1;
|
|
254
|
+
|
|
255
|
+
const row = {
|
|
256
|
+
sessionId,
|
|
257
|
+
pairIndex,
|
|
258
|
+
inputPrompt: userText,
|
|
259
|
+
agentOutput: assistantText,
|
|
260
|
+
executedWork: true,
|
|
261
|
+
toolUseCount: toolUses.length,
|
|
262
|
+
toolNames,
|
|
263
|
+
branchesTouched,
|
|
264
|
+
mergedToMain,
|
|
265
|
+
mergedPrNumbers,
|
|
266
|
+
};
|
|
267
|
+
outStream.write(`${JSON.stringify(row)}\n`);
|
|
268
|
+
stats.pairsKept += 1;
|
|
269
|
+
pairIndex += 1;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
await new Promise((resolve) => outStream.end(resolve));
|
|
274
|
+
|
|
275
|
+
// ---------- summary ----------
|
|
276
|
+
process.stdout.write(
|
|
277
|
+
JSON.stringify(
|
|
278
|
+
{
|
|
279
|
+
ok: true,
|
|
280
|
+
out: outPath,
|
|
281
|
+
transcriptsDir,
|
|
282
|
+
mergeIndexEntries: mergedByBranch.size,
|
|
283
|
+
...stats,
|
|
284
|
+
},
|
|
285
|
+
null,
|
|
286
|
+
2,
|
|
287
|
+
) + "\n",
|
|
288
|
+
);
|
package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/upload-graded-traces.mjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* helpers/upload-graded-traces.mjs — Distillation Pipeline V1, Phase 2.5
|
|
4
|
+
*
|
|
5
|
+
* Reads graded pairs JSONL (Phase 2 output) and appends rows whose
|
|
6
|
+
* qualityScore >= --min-score into the live workspace's `training-traces`
|
|
7
|
+
* dataModel object via PATCH /api/workspace.
|
|
8
|
+
*
|
|
9
|
+
* - Append-only. Existing rows are preserved.
|
|
10
|
+
* - Marks every uploaded row with `exported: "false"` so the export script
|
|
11
|
+
* can pick them up later.
|
|
12
|
+
* - Truncates `agentOutput` to keep the workspace config file lean.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* node helpers/upload-graded-traces.mjs \
|
|
16
|
+
* --in ./antonio/distillation/graded-batch-001.jsonl \
|
|
17
|
+
* --workspace http://localhost:3000 \
|
|
18
|
+
* --traces-object training-traces \
|
|
19
|
+
* --min-score 4 \
|
|
20
|
+
* --max-output-chars 4000
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fs from "node:fs";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
const a = {
|
|
28
|
+
in: "",
|
|
29
|
+
workspace: "http://localhost:3000",
|
|
30
|
+
tracesObject: "training-traces",
|
|
31
|
+
minScore: 4,
|
|
32
|
+
maxOutputChars: 4000,
|
|
33
|
+
};
|
|
34
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
35
|
+
const t = argv[i];
|
|
36
|
+
const next = () => String(argv[++i] || "").trim();
|
|
37
|
+
if (t === "--in") a.in = next();
|
|
38
|
+
else if (t === "--workspace") a.workspace = next().replace(/\/+$/, "");
|
|
39
|
+
else if (t === "--traces-object") a.tracesObject = next();
|
|
40
|
+
else if (t === "--min-score") a.minScore = Number(next()) || 4;
|
|
41
|
+
else if (t === "--max-output-chars") a.maxOutputChars = Number(next()) || 4000;
|
|
42
|
+
else if (t === "--help" || t === "-h") {
|
|
43
|
+
process.stdout.write(
|
|
44
|
+
"Usage: upload-graded-traces.mjs --in <graded.jsonl> [--workspace URL] [--traces-object id] [--min-score N] [--max-output-chars N]\n",
|
|
45
|
+
);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!a.in) {
|
|
50
|
+
process.stderr.write("error: --in is required\n");
|
|
51
|
+
process.exit(2);
|
|
52
|
+
}
|
|
53
|
+
return a;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const args = parseArgs(process.argv.slice(2));
|
|
57
|
+
const inAbs = path.resolve(args.in);
|
|
58
|
+
if (!fs.existsSync(inAbs)) {
|
|
59
|
+
process.stderr.write(`error: input not found: ${inAbs}\n`);
|
|
60
|
+
process.exit(2);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function getObjects() {
|
|
64
|
+
const r = await fetch(`${args.workspace}/api/workspace`, { cache: "no-store" });
|
|
65
|
+
if (!r.ok) throw new Error(`GET /api/workspace ${r.status}`);
|
|
66
|
+
return (await r.json()).workspaceConfig.dataModel.objects;
|
|
67
|
+
}
|
|
68
|
+
async function patchObjects(objects) {
|
|
69
|
+
const r = await fetch(`${args.workspace}/api/workspace`, {
|
|
70
|
+
method: "PATCH",
|
|
71
|
+
headers: { "content-type": "application/json" },
|
|
72
|
+
body: JSON.stringify({ dataModel: { objects } }),
|
|
73
|
+
});
|
|
74
|
+
if (!r.ok) throw new Error(`PATCH ${r.status}: ${(await r.text()).slice(0, 300)}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const lines = fs.readFileSync(inAbs, "utf8").split("\n").filter(Boolean);
|
|
78
|
+
const candidates = [];
|
|
79
|
+
for (const ln of lines) {
|
|
80
|
+
try {
|
|
81
|
+
const j = JSON.parse(ln);
|
|
82
|
+
if (Number(j.qualityScore) >= args.minScore) candidates.push(j);
|
|
83
|
+
} catch {
|
|
84
|
+
/* skip */
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (candidates.length === 0) {
|
|
89
|
+
process.stdout.write(JSON.stringify({ ok: true, uploaded: 0, reason: "no rows >= --min-score" }) + "\n");
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const objects = await getObjects();
|
|
94
|
+
const tracesIdx = objects.findIndex((o) => o.id === args.tracesObject);
|
|
95
|
+
if (tracesIdx < 0) {
|
|
96
|
+
process.stderr.write(`error: object ${args.tracesObject} not found in workspace\n`);
|
|
97
|
+
process.exit(3);
|
|
98
|
+
}
|
|
99
|
+
const tracesObj = objects[tracesIdx];
|
|
100
|
+
const newRows = candidates.map((p) => ({
|
|
101
|
+
sessionDate: p.gradedAt || new Date().toISOString(),
|
|
102
|
+
inputPrompt: String(p.inputPrompt || "").slice(0, args.maxOutputChars),
|
|
103
|
+
agentOutput: String(p.agentOutput || "").slice(0, args.maxOutputChars),
|
|
104
|
+
qualityScore: String(p.qualityScore),
|
|
105
|
+
reason: String(p.qualityReason || ""),
|
|
106
|
+
exported: "false",
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
const nextObjects = objects.map((o, i) =>
|
|
110
|
+
i !== tracesIdx ? o : { ...o, rows: [...(o.rows || []), ...newRows] },
|
|
111
|
+
);
|
|
112
|
+
await patchObjects(nextObjects);
|
|
113
|
+
|
|
114
|
+
const after = await getObjects();
|
|
115
|
+
const finalCount = after.find((o) => o.id === args.tracesObject)?.rows?.length || 0;
|
|
116
|
+
process.stdout.write(
|
|
117
|
+
JSON.stringify(
|
|
118
|
+
{
|
|
119
|
+
ok: true,
|
|
120
|
+
candidates: candidates.length,
|
|
121
|
+
uploaded: newRows.length,
|
|
122
|
+
tracesTotalRows: finalCount,
|
|
123
|
+
sourceFile: path.basename(inAbs),
|
|
124
|
+
},
|
|
125
|
+
null,
|
|
126
|
+
2,
|
|
127
|
+
) + "\n",
|
|
128
|
+
);
|
|
@@ -43,10 +43,15 @@
|
|
|
43
43
|
"templates/deployment-plan.md",
|
|
44
44
|
"templates/project.md",
|
|
45
45
|
"templates/self-eval.md",
|
|
46
|
+
"templates/seeded-configs/alignment-loop.config.json",
|
|
46
47
|
"helpers/README.md",
|
|
47
48
|
"helpers/propose-capability.mjs",
|
|
48
49
|
"helpers/promote-capability.mjs",
|
|
49
50
|
"helpers/check-self-improving-health.sh",
|
|
51
|
+
"helpers/harvest-cursor-traces.mjs",
|
|
52
|
+
"helpers/grade-raw-pairs.mjs",
|
|
53
|
+
"helpers/upload-graded-traces.mjs",
|
|
54
|
+
"helpers/export-training-traces.mjs",
|
|
50
55
|
"skills/README.md",
|
|
51
56
|
"examples/workspace-sample.md",
|
|
52
57
|
"docs/starter-kit-overview.md",
|
|
@@ -98,9 +103,18 @@
|
|
|
98
103
|
"apps/workspace/lib/adapters/sandboxes/adapter-loader.js",
|
|
99
104
|
"apps/workspace/lib/adapters/sandboxes/adapters/README.md",
|
|
100
105
|
"apps/workspace/lib/adapters/sandboxes/default-local-agent-host.js",
|
|
106
|
+
"apps/workspace/lib/adapters/sandboxes/default-local-intelligence.js",
|
|
101
107
|
"apps/workspace/lib/adapters/sandboxes/default-local-process.js",
|
|
102
108
|
"apps/workspace/lib/adapters/sandboxes/index.js",
|
|
103
109
|
"apps/workspace/lib/adapters/sandboxes/sandbox-adapter-registry.js",
|
|
110
|
+
"apps/workspace/lib/workspace-helper.js",
|
|
111
|
+
"apps/workspace/lib/workspace-helper-apply.js",
|
|
112
|
+
"apps/workspace/app/api/workspace/helper/query/route.js",
|
|
113
|
+
"apps/workspace/app/api/workspace/helper/apply/route.js",
|
|
114
|
+
"apps/workspace/app/api/workspace/helper/receipts/route.js",
|
|
115
|
+
"apps/workspace/app/data-model/page.jsx",
|
|
116
|
+
"apps/workspace/app/data-model/components/DataModelShell.jsx",
|
|
117
|
+
"apps/workspace/app/data-model/components/HelperSidecar.jsx",
|
|
104
118
|
"apps/workspace/lib/adapters/payments/index.js",
|
|
105
119
|
"apps/workspace/lib/adapters/persistence/index.js",
|
|
106
120
|
"apps/workspace/lib/adapters/persistence/postgres.js",
|
|
@@ -139,11 +153,16 @@
|
|
|
139
153
|
"templates/self-eval.md",
|
|
140
154
|
"templates/deployment-handoff.md",
|
|
141
155
|
"templates/supabase-setup-plan.md",
|
|
156
|
+
"templates/seeded-configs/alignment-loop.config.json",
|
|
142
157
|
"helpers",
|
|
143
158
|
"helpers/README.md",
|
|
144
159
|
"helpers/propose-capability.mjs",
|
|
145
160
|
"helpers/promote-capability.mjs",
|
|
146
161
|
"helpers/check-self-improving-health.sh",
|
|
162
|
+
"helpers/harvest-cursor-traces.mjs",
|
|
163
|
+
"helpers/grade-raw-pairs.mjs",
|
|
164
|
+
"helpers/upload-graded-traces.mjs",
|
|
165
|
+
"helpers/export-training-traces.mjs",
|
|
147
166
|
"skills",
|
|
148
167
|
"skills/README.md",
|
|
149
168
|
"docs",
|