yapout 0.3.0 → 0.4.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 +241 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -245,23 +245,75 @@ def main():
|
|
|
245
245
|
)
|
|
246
246
|
elif action == "enrich":
|
|
247
247
|
prompt = (
|
|
248
|
-
f'
|
|
249
|
-
f'
|
|
250
|
-
f'
|
|
251
|
-
f'
|
|
252
|
-
f'
|
|
253
|
-
f
|
|
254
|
-
f'
|
|
255
|
-
f'5. Scope assessment (is this too large for one PR?)\\n\\n'
|
|
256
|
-
f'Call yapout_save_enrichment with your analysis. '
|
|
257
|
-
f'If no questions were generated, also call yapout_sync_to_linear to create the Linear ticket.'
|
|
248
|
+
f'Use yapout to enrich a finding.\\n'
|
|
249
|
+
f'\\n'
|
|
250
|
+
f'Start with filter: {{ findingIds: ["{ticket_id}"] }}\\n'
|
|
251
|
+
f'\\n'
|
|
252
|
+
f'Call yapout_start_enrichment, then yapout_enrich_next to claim it.\\n'
|
|
253
|
+
f'Read the codebase, ask me questions if needed, then call yapout_save_enrichment\\n'
|
|
254
|
+
f'with a clean description, acceptance criteria, and implementation brief.'
|
|
258
255
|
)
|
|
256
|
+
elif action == "enrich-bulk":
|
|
257
|
+
tags = qs.get("tags", [""])[0]
|
|
258
|
+
finding_ids = qs.get("findingIds", [""])[0]
|
|
259
|
+
lines = [
|
|
260
|
+
"Use yapout to enrich findings for this project. Start a bulk enrichment session and work through each finding one by one.",
|
|
261
|
+
"",
|
|
262
|
+
"For each finding:",
|
|
263
|
+
"1. Call yapout_start_enrichment to begin" + (" with the filter below" if (tags or finding_ids) else ""),
|
|
264
|
+
"2. Call yapout_enrich_next to get the next finding",
|
|
265
|
+
"3. Read relevant code in the repository to understand the finding's context",
|
|
266
|
+
"4. Ask me any clarifying questions (only if genuinely needed)",
|
|
267
|
+
"5. Call yapout_save_enrichment with:",
|
|
268
|
+
" - A clean, specific title",
|
|
269
|
+
" - A description a senior engineer would write",
|
|
270
|
+
" - Concrete acceptance criteria",
|
|
271
|
+
" - An implementation brief (which files, approach, edge cases)",
|
|
272
|
+
'6. If I say "skip", call yapout_enrich_next with skip=true',
|
|
273
|
+
"7. After saving, if compactionHint is true or every 5 findings, run /compact",
|
|
274
|
+
"8. Repeat until done",
|
|
275
|
+
"",
|
|
276
|
+
"Keep the pace steady. Don't over-ask \u2014 use your judgment from the code.",
|
|
277
|
+
]
|
|
278
|
+
if finding_ids:
|
|
279
|
+
ids = [fid.strip() for fid in finding_ids.split(",") if fid.strip()]
|
|
280
|
+
lines.append("")
|
|
281
|
+
lines.append("These findings are bundled \u2014 enrich them as one cohesive problem.")
|
|
282
|
+
lines.append(f'Start with filter: {{ findingIds: {json.dumps(ids)} }}')
|
|
283
|
+
elif tags:
|
|
284
|
+
tag_list = [t.strip() for t in tags.split(",") if t.strip()]
|
|
285
|
+
lines.append("")
|
|
286
|
+
lines.append(f'Start with filter: {{ tags: {json.dumps(tag_list)} }}')
|
|
287
|
+
prompt = "\\n".join(lines)
|
|
259
288
|
elif action == "yap":
|
|
260
289
|
prompt = "Let's have a yap session"
|
|
261
290
|
if topic:
|
|
262
291
|
prompt += f" about {topic}"
|
|
263
292
|
if persona and persona != "tech lead":
|
|
264
293
|
prompt += f" \u2014 be a {persona}"
|
|
294
|
+
elif action == "compact":
|
|
295
|
+
elif action == "enrich-bundle":
|
|
296
|
+
bundle_id = ticket_id
|
|
297
|
+
prompt = (
|
|
298
|
+
f'Use yapout to enrich a bundle as a single cohesive unit.\\n'
|
|
299
|
+
f'\\n'
|
|
300
|
+
f'1. Call yapout_enrich_bundle with bundleId "{bundle_id}" to claim it and get all findings\\n'
|
|
301
|
+
f'2. Read the codebase to understand the full scope of the bundle\\n'
|
|
302
|
+
f'3. Ask me any clarifying questions about the bundle as a whole (not individual findings)\\n'
|
|
303
|
+
f'4. For each finding, formulate:\\n'
|
|
304
|
+
f' - A refined title\\n'
|
|
305
|
+
f' - A specific description\\n'
|
|
306
|
+
f' - Concrete acceptance criteria\\n'
|
|
307
|
+
f' - An implementation brief\\n'
|
|
308
|
+
f'5. Also formulate bundle-level:\\n'
|
|
309
|
+
f' - An overall description of what the bundle delivers\\n'
|
|
310
|
+
f' - Combined acceptance criteria\\n'
|
|
311
|
+
f' - An architecture/approach brief that covers the full scope\\n'
|
|
312
|
+
f'6. Call yapout_save_bundle_enrichment with ALL enrichment data at once\\n'
|
|
313
|
+
f'\\n'
|
|
314
|
+
f'Treat this as ONE problem, not separate findings. Understand how they relate,\\n'
|
|
315
|
+
f'identify dependencies, and write briefs that reference each other.'
|
|
316
|
+
)
|
|
265
317
|
elif action == "compact":
|
|
266
318
|
prompt = "Run yapout_compact to update the project context summary."
|
|
267
319
|
else:
|
|
@@ -2026,7 +2078,7 @@ This tool saves the enrichment, then automatically creates the Linear issue with
|
|
|
2026
2078
|
- Clarification Q&A as a branded comment (if any)
|
|
2027
2079
|
- Implementation brief as attachment metadata
|
|
2028
2080
|
|
|
2029
|
-
The finding transitions: enriching \u2192 enriched \u2192
|
|
2081
|
+
The finding transitions: enriching \u2192 enriched \u2192 ready.`,
|
|
2030
2082
|
{
|
|
2031
2083
|
findingId: z11.string().describe("The finding ID to enrich (from yapout_get_unenriched_finding)"),
|
|
2032
2084
|
title: z11.string().describe("Refined finding title \u2014 improve it if the original was vague"),
|
|
@@ -2086,7 +2138,7 @@ The finding transitions: enriching \u2192 enriched \u2192 synced.`,
|
|
|
2086
2138
|
linearIssueId: finding?.linearIssueId ?? null,
|
|
2087
2139
|
linearIssueUrl: finding?.linearIssueUrl ?? null,
|
|
2088
2140
|
compactionHint,
|
|
2089
|
-
message: finding?.linearIssueUrl ? `Finding enriched and
|
|
2141
|
+
message: finding?.linearIssueUrl ? `Finding enriched and ready in Linear: ${finding.linearIssueUrl}` : "Finding enriched and ready in Linear."
|
|
2090
2142
|
};
|
|
2091
2143
|
if (args.isOversized && args.suggestedSplit?.length) {
|
|
2092
2144
|
response.warning = `This finding is oversized. Suggested split: ${args.suggestedSplit.join(", ")}`;
|
|
@@ -2967,6 +3019,121 @@ When done=true, all findings have been processed.`,
|
|
|
2967
3019
|
);
|
|
2968
3020
|
}
|
|
2969
3021
|
|
|
3022
|
+
// src/mcp/tools/enrich-bundle.ts
|
|
3023
|
+
import { z as z20 } from "zod";
|
|
3024
|
+
function registerEnrichBundleTool(server, ctx) {
|
|
3025
|
+
server.tool(
|
|
3026
|
+
"yapout_enrich_bundle",
|
|
3027
|
+
`Claim an entire bundle for enrichment. Returns ALL findings in the bundle at once
|
|
3028
|
+
with their relationships, source quotes, and project context.
|
|
3029
|
+
|
|
3030
|
+
This is for enriching a bundle as a single cohesive unit \u2014 NOT individual findings.
|
|
3031
|
+
The agent should understand the full scope, ask questions about the bundle as a whole,
|
|
3032
|
+
then call yapout_save_bundle_enrichment with enrichment data for every finding.
|
|
3033
|
+
|
|
3034
|
+
The bundle and all its findings transition to "enriching" status.`,
|
|
3035
|
+
{
|
|
3036
|
+
bundleId: z20.string().describe("The bundle ID to enrich")
|
|
3037
|
+
},
|
|
3038
|
+
async (args) => {
|
|
3039
|
+
try {
|
|
3040
|
+
const result = await ctx.client.mutation(
|
|
3041
|
+
anyApi2.functions.bundles.claimBundleForEnrichment,
|
|
3042
|
+
{ bundleId: args.bundleId }
|
|
3043
|
+
);
|
|
3044
|
+
if (!result) {
|
|
3045
|
+
return {
|
|
3046
|
+
content: [{ type: "text", text: "Failed to claim bundle for enrichment." }],
|
|
3047
|
+
isError: true
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
return {
|
|
3051
|
+
content: [
|
|
3052
|
+
{
|
|
3053
|
+
type: "text",
|
|
3054
|
+
text: JSON.stringify(result, null, 2)
|
|
3055
|
+
}
|
|
3056
|
+
]
|
|
3057
|
+
};
|
|
3058
|
+
} catch (err) {
|
|
3059
|
+
return {
|
|
3060
|
+
content: [{ type: "text", text: `Error claiming bundle: ${err.message}` }],
|
|
3061
|
+
isError: true
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
// src/mcp/tools/save-bundle-enrichment.ts
|
|
3069
|
+
import { z as z21 } from "zod";
|
|
3070
|
+
function registerSaveBundleEnrichmentTool(server, ctx) {
|
|
3071
|
+
server.tool(
|
|
3072
|
+
"yapout_save_bundle_enrichment",
|
|
3073
|
+
`Save enrichment for an entire bundle at once. Call this after you've analyzed all
|
|
3074
|
+
findings in the bundle as a cohesive unit, read the codebase, and asked any questions.
|
|
3075
|
+
|
|
3076
|
+
Provide:
|
|
3077
|
+
- Bundle-level: overall description, combined acceptance criteria, implementation brief
|
|
3078
|
+
- Per-finding: each finding gets its own refined title, description, acceptance criteria, and brief
|
|
3079
|
+
|
|
3080
|
+
The bundle and all findings transition from "enriching" to "enriched".
|
|
3081
|
+
Call yapout_sync_bundle_to_linear afterwards to create the Linear project.`,
|
|
3082
|
+
{
|
|
3083
|
+
bundleId: z21.string().describe("The bundle ID"),
|
|
3084
|
+
title: z21.string().optional().describe("Refined bundle title (optional, keeps existing if omitted)"),
|
|
3085
|
+
enrichedDescription: z21.string().describe("Bundle-level description \u2014 the cohesive story of what this bundle delivers"),
|
|
3086
|
+
acceptanceCriteria: z21.array(z21.string()).describe("Bundle-level acceptance criteria"),
|
|
3087
|
+
implementationBrief: z21.string().describe("Bundle-level implementation brief \u2014 overall approach, architecture decisions, key files"),
|
|
3088
|
+
findings: z21.array(z21.object({
|
|
3089
|
+
findingId: z21.string().describe("Finding ID"),
|
|
3090
|
+
title: z21.string().describe("Refined finding title"),
|
|
3091
|
+
enrichedDescription: z21.string().describe("Finding-specific description"),
|
|
3092
|
+
acceptanceCriteria: z21.array(z21.string()).describe("Finding-specific acceptance criteria"),
|
|
3093
|
+
implementationBrief: z21.string().describe("Finding-specific implementation brief")
|
|
3094
|
+
})).describe("Per-finding enrichment data \u2014 one entry per finding in the bundle")
|
|
3095
|
+
},
|
|
3096
|
+
async (args) => {
|
|
3097
|
+
try {
|
|
3098
|
+
await ctx.client.mutation(
|
|
3099
|
+
anyApi2.functions.bundles.saveBundleEnrichment,
|
|
3100
|
+
{
|
|
3101
|
+
bundleId: args.bundleId,
|
|
3102
|
+
title: args.title,
|
|
3103
|
+
enrichedDescription: args.enrichedDescription,
|
|
3104
|
+
acceptanceCriteria: args.acceptanceCriteria,
|
|
3105
|
+
implementationBrief: args.implementationBrief,
|
|
3106
|
+
findings: args.findings.map((f) => ({
|
|
3107
|
+
findingId: f.findingId,
|
|
3108
|
+
title: f.title,
|
|
3109
|
+
enrichedDescription: f.enrichedDescription,
|
|
3110
|
+
acceptanceCriteria: f.acceptanceCriteria,
|
|
3111
|
+
implementationBrief: f.implementationBrief
|
|
3112
|
+
}))
|
|
3113
|
+
}
|
|
3114
|
+
);
|
|
3115
|
+
return {
|
|
3116
|
+
content: [
|
|
3117
|
+
{
|
|
3118
|
+
type: "text",
|
|
3119
|
+
text: JSON.stringify({
|
|
3120
|
+
bundleId: args.bundleId,
|
|
3121
|
+
findingsEnriched: args.findings.length,
|
|
3122
|
+
message: `Bundle enriched successfully (${args.findings.length} findings). Call yapout_sync_bundle_to_linear to create the Linear project.`
|
|
3123
|
+
}, null, 2)
|
|
3124
|
+
}
|
|
3125
|
+
]
|
|
3126
|
+
};
|
|
3127
|
+
} catch (err) {
|
|
3128
|
+
return {
|
|
3129
|
+
content: [{ type: "text", text: `Error saving bundle enrichment: ${err.message}` }],
|
|
3130
|
+
isError: true
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
);
|
|
3135
|
+
}
|
|
3136
|
+
|
|
2970
3137
|
// src/mcp/server.ts
|
|
2971
3138
|
async function startMcpServer() {
|
|
2972
3139
|
const cwd = process.cwd();
|
|
@@ -3021,6 +3188,8 @@ async function startMcpServer() {
|
|
|
3021
3188
|
registerGetLinearProjectsTool(server, ctx);
|
|
3022
3189
|
registerStartEnrichmentTool(server, ctx);
|
|
3023
3190
|
registerEnrichNextTool(server, ctx);
|
|
3191
|
+
registerEnrichBundleTool(server, ctx);
|
|
3192
|
+
registerSaveBundleEnrichmentTool(server, ctx);
|
|
3024
3193
|
const transport = new StdioServerTransport();
|
|
3025
3194
|
await server.connect(transport);
|
|
3026
3195
|
}
|
|
@@ -4112,7 +4281,7 @@ import { Command as Command15 } from "commander";
|
|
|
4112
4281
|
import { spawn as spawn2 } from "child_process";
|
|
4113
4282
|
import { platform as platform2 } from "os";
|
|
4114
4283
|
import chalk16 from "chalk";
|
|
4115
|
-
var VALID_ACTIONS = ["claim", "enrich", "yap", "compact"];
|
|
4284
|
+
var VALID_ACTIONS = ["claim", "enrich", "enrich-bulk", "enrich-bundle", "yap", "compact"];
|
|
4116
4285
|
function parseYapoutUri(raw) {
|
|
4117
4286
|
const url = new URL(raw);
|
|
4118
4287
|
const action = url.hostname;
|
|
@@ -4122,14 +4291,16 @@ function parseYapoutUri(raw) {
|
|
|
4122
4291
|
);
|
|
4123
4292
|
}
|
|
4124
4293
|
const ticketId = url.pathname.replace(/^\//, "") || void 0;
|
|
4125
|
-
if ((action === "claim" || action === "enrich") && !ticketId) {
|
|
4126
|
-
throw new Error(`Missing
|
|
4294
|
+
if ((action === "claim" || action === "enrich" || action === "enrich-bundle") && !ticketId) {
|
|
4295
|
+
throw new Error(`Missing ID in URI: ${raw}`);
|
|
4127
4296
|
}
|
|
4128
4297
|
return {
|
|
4129
4298
|
action,
|
|
4130
4299
|
ticketId,
|
|
4131
4300
|
topic: url.searchParams.get("topic") || void 0,
|
|
4132
|
-
persona: url.searchParams.get("persona") || void 0
|
|
4301
|
+
persona: url.searchParams.get("persona") || void 0,
|
|
4302
|
+
tags: url.searchParams.get("tags")?.split(",").filter(Boolean) || void 0,
|
|
4303
|
+
findingIds: url.searchParams.get("findingIds")?.split(",").filter(Boolean) || void 0
|
|
4133
4304
|
};
|
|
4134
4305
|
}
|
|
4135
4306
|
function buildPrompt(parsed) {
|
|
@@ -4142,18 +4313,62 @@ function buildPrompt(parsed) {
|
|
|
4142
4313
|
].join(" ");
|
|
4143
4314
|
case "enrich":
|
|
4144
4315
|
return [
|
|
4145
|
-
`
|
|
4146
|
-
|
|
4316
|
+
`Use yapout to enrich a finding.`,
|
|
4317
|
+
``,
|
|
4318
|
+
`Start with filter: { findingIds: ["${parsed.ticketId}"] }`,
|
|
4319
|
+
``,
|
|
4320
|
+
`Call yapout_start_enrichment, then yapout_enrich_next to claim it.`,
|
|
4321
|
+
`Read the codebase, ask me questions if needed, then call yapout_save_enrichment`,
|
|
4322
|
+
`with a clean description, acceptance criteria, and implementation brief.`
|
|
4323
|
+
].join("\n");
|
|
4324
|
+
case "enrich-bulk": {
|
|
4325
|
+
const lines = [
|
|
4326
|
+
"Use yapout to enrich findings for this project. Start a bulk enrichment session and work through each finding one by one.",
|
|
4327
|
+
"",
|
|
4328
|
+
"For each finding:",
|
|
4329
|
+
"1. Call yapout_start_enrichment to begin" + (parsed.tags || parsed.findingIds ? " with the filter below" : ""),
|
|
4330
|
+
"2. Call yapout_enrich_next to get the next finding",
|
|
4331
|
+
"3. Read relevant code in the repository to understand the finding's context",
|
|
4332
|
+
"4. Ask me any clarifying questions (only if genuinely needed)",
|
|
4333
|
+
"5. Call yapout_save_enrichment with:",
|
|
4334
|
+
" - A clean, specific title",
|
|
4335
|
+
" - A description a senior engineer would write",
|
|
4336
|
+
" - Concrete acceptance criteria",
|
|
4337
|
+
" - An implementation brief (which files, approach, edge cases)",
|
|
4338
|
+
'6. If I say "skip", call yapout_enrich_next with skip=true',
|
|
4339
|
+
"7. After saving, if compactionHint is true or every 5 findings, run /compact",
|
|
4340
|
+
"8. Repeat until done",
|
|
4341
|
+
"",
|
|
4342
|
+
"Keep the pace steady. Don't over-ask \u2014 use your judgment from the code."
|
|
4343
|
+
];
|
|
4344
|
+
if (parsed.findingIds?.length) {
|
|
4345
|
+
lines.push("", `These findings are bundled \u2014 enrich them as one cohesive problem.`);
|
|
4346
|
+
lines.push(`Start with filter: { findingIds: ${JSON.stringify(parsed.findingIds)} }`);
|
|
4347
|
+
} else if (parsed.tags?.length) {
|
|
4348
|
+
lines.push("", `Start with filter: { tags: ${JSON.stringify(parsed.tags)} }`);
|
|
4349
|
+
}
|
|
4350
|
+
return lines.join("\n");
|
|
4351
|
+
}
|
|
4352
|
+
case "enrich-bundle":
|
|
4353
|
+
return [
|
|
4354
|
+
`Use yapout to enrich a bundle as a single cohesive unit.`,
|
|
4147
4355
|
``,
|
|
4148
|
-
`
|
|
4149
|
-
`
|
|
4150
|
-
`
|
|
4151
|
-
`
|
|
4152
|
-
`
|
|
4153
|
-
`
|
|
4356
|
+
`1. Call yapout_enrich_bundle with bundleId "${parsed.ticketId}" to claim it and get all findings`,
|
|
4357
|
+
`2. Read the codebase to understand the full scope of the bundle`,
|
|
4358
|
+
`3. Ask me any clarifying questions about the bundle as a whole (not individual findings)`,
|
|
4359
|
+
`4. For each finding, formulate:`,
|
|
4360
|
+
` - A refined title`,
|
|
4361
|
+
` - A specific description`,
|
|
4362
|
+
` - Concrete acceptance criteria`,
|
|
4363
|
+
` - An implementation brief`,
|
|
4364
|
+
`5. Also formulate bundle-level:`,
|
|
4365
|
+
` - An overall description of what the bundle delivers`,
|
|
4366
|
+
` - Combined acceptance criteria`,
|
|
4367
|
+
` - An architecture/approach brief that covers the full scope`,
|
|
4368
|
+
`6. Call yapout_save_bundle_enrichment with ALL enrichment data at once`,
|
|
4154
4369
|
``,
|
|
4155
|
-
`
|
|
4156
|
-
`
|
|
4370
|
+
`Treat this as ONE problem, not separate findings. Understand how they relate,`,
|
|
4371
|
+
`identify dependencies, and write briefs that reference each other.`
|
|
4157
4372
|
].join("\n");
|
|
4158
4373
|
case "yap": {
|
|
4159
4374
|
const parts = ["Let's have a yap session"];
|