@papi-ai/server 0.7.58 → 0.7.60
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 +230 -26
- package/dist/prompts.js +116 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7527,6 +7527,9 @@ ${footer}`);
|
|
|
7527
7527
|
function newTaskJoinKey(task, index) {
|
|
7528
7528
|
return task.tempId ?? `new-${index + 1}`;
|
|
7529
7529
|
}
|
|
7530
|
+
function isUnresolvedPlaceholder(taskId, map) {
|
|
7531
|
+
return !map.has(taskId) && /^new-\d+$/i.test(taskId);
|
|
7532
|
+
}
|
|
7530
7533
|
var NONE_PATTERN2 = /^none\b/i;
|
|
7531
7534
|
function normalizeText2(text) {
|
|
7532
7535
|
return text.trim().toLowerCase().replace(/[.,;:!]+$/, "").replace(/\s+/g, " ");
|
|
@@ -9010,7 +9013,7 @@ var PLAN_FRAGMENT_DESIGN_BRIEF = `
|
|
|
9010
9013
|
**Design brief task detection:** When a task's task type is "design-brief", generate a DESIGN BRIEF handoff. Replace the standard SCOPE (DO THIS) section with these type-specific sections:
|
|
9011
9014
|
- AUDIENCE: Who this design is for \u2014 persona and context of use (e.g. "non-technical Owner, first dashboard visit")
|
|
9012
9015
|
- BRAND CONSTRAINTS: Palette, typography, tone \u2014 pull from \`.impeccable.md\` (dev patterns, anti-patterns, component rules) AND \`docs/branding/brand-book.html\` (brand identity, positioning, voice canon) if present. If neither exists, state "No brand doc \u2014 Owner should define constraints before starting."
|
|
9013
|
-
- DELIVERABLE FORMAT: What the output looks like \u2014
|
|
9016
|
+
- DELIVERABLE FORMAT: What the output looks like \u2014 design handoff package / annotated mockup / style spec. Be specific so the person doing the work knows what "done" means.
|
|
9014
9017
|
- REVIEW POINTS: What the Owner must approve before the design is considered done (e.g. layout, copy, colour, imagery).
|
|
9015
9018
|
Keep SCOPE BOUNDARY, ACCEPTANCE CRITERIA, SECURITY CONSIDERATIONS, and PRE-BUILD VERIFICATION sections as normal.
|
|
9016
9019
|
Add to ACCEPTANCE CRITERIA: "[ ] Deliverable format confirmed with Owner before starting" and "[ ] Design output is self-contained \u2014 includes enough context for a developer to implement without further clarification."`;
|
|
@@ -9209,6 +9212,21 @@ function buildPlanFullInstructionsConditional(flags, ctx) {
|
|
|
9209
9212
|
if (!flags || !ctx) return PLAN_FULL_INSTRUCTIONS;
|
|
9210
9213
|
return composeFullModeInstructions(flags, ctx);
|
|
9211
9214
|
}
|
|
9215
|
+
var CYCLE_DENSITY_TARGETS = {
|
|
9216
|
+
light: { label: "Light", range: "2-3" },
|
|
9217
|
+
standard: { label: "Standard", range: "3-5" },
|
|
9218
|
+
deep: { label: "Deep", range: "6-8" }
|
|
9219
|
+
};
|
|
9220
|
+
function buildCycleDensityDirective(density) {
|
|
9221
|
+
if (!density) return "";
|
|
9222
|
+
const target = CYCLE_DENSITY_TARGETS[density];
|
|
9223
|
+
return [
|
|
9224
|
+
`## CYCLE DENSITY: ${target.label}`,
|
|
9225
|
+
"",
|
|
9226
|
+
`The user set this cycle's density to **${density}** \u2014 aim for roughly **${target.range} tasks** this cycle.`,
|
|
9227
|
+
`This is a TARGET to size toward, not a hard floor or cap: still honour explicit user direction, any pre-assigned tasks, and impact-based sizing. If fewer genuinely-valuable tasks exist, plan fewer \u2014 do NOT pad the cycle to hit the number.`
|
|
9228
|
+
].join("\n");
|
|
9229
|
+
}
|
|
9212
9230
|
function buildPlanUserMessage(ctx) {
|
|
9213
9231
|
const modeLabel = ctx.mode.toUpperCase();
|
|
9214
9232
|
const parts = [
|
|
@@ -9226,6 +9244,10 @@ function buildPlanUserMessage(ctx) {
|
|
|
9226
9244
|
""
|
|
9227
9245
|
);
|
|
9228
9246
|
}
|
|
9247
|
+
const densityDirective = buildCycleDensityDirective(ctx.density);
|
|
9248
|
+
if (densityDirective) {
|
|
9249
|
+
parts.push(densityDirective, "");
|
|
9250
|
+
}
|
|
9229
9251
|
if (ctx.mode === "bootstrap") {
|
|
9230
9252
|
parts.push(PLAN_BOOTSTRAP_INSTRUCTIONS);
|
|
9231
9253
|
} else {
|
|
@@ -10101,15 +10123,49 @@ phases:
|
|
|
10101
10123
|
|
|
10102
10124
|
*No decisions locked yet. These are added as planning cycles confirm strategic choices.*`;
|
|
10103
10125
|
}
|
|
10126
|
+
function buildPreScanInstruction(opts) {
|
|
10127
|
+
const docRouting = opts.hosted ? "paste the relevant content (decision records, roadmap, spec excerpts) directly into `description` \u2014 over a remote connector PAPI cannot read local file paths." : "pass local doc file paths as `sources` (comma-separated), or paste content into `description`.";
|
|
10128
|
+
return [
|
|
10129
|
+
`**\u{1F50E} Before you generate anything \u2014 scan the project and gather real context.**`,
|
|
10130
|
+
`A brief and Active Decisions built from the real code and docs are far sharper than ones guessed from a project name. You (the agent running this) have local access that PAPI does not \u2014 do the gathering, then pass it back.`,
|
|
10131
|
+
``,
|
|
10132
|
+
`1. **Confirm the environment.** Check that you are running from the root of this project's dev environment \u2014 the folder that holds its source, git history, and docs. If you are not there, or you are connected over a remote connector with no access to the user's files, say so and ask the user where the project lives before continuing.`,
|
|
10133
|
+
`2. **Scan the code.** List the top-level directories and files, read the package/build manifest and the README (first ~3000 chars), and collect recent commit subjects. Pass this back as \`codebase_scan\` on the next \`setup\` call.`,
|
|
10134
|
+
`3. **Scan the docs.** Look through \`docs/\`, design notes, ADRs/decision records, and any roadmap or planning files \u2014 not just code.`,
|
|
10135
|
+
`4. **Check for sibling / separate repos.** Many projects span more than one repo (a separate frontend, backend, infra, or mobile repo, or other packages in a monorepo). Ask the user whether any related repos or directories exist, and scan those too.`,
|
|
10136
|
+
`5. **Ask the user to point you at extra context.** Prompt them: "Is there anything else I should read before setting this up \u2014 a PRD or spec, decision records, a roadmap, or links to related repos or docs?" Fold whatever they share into the scan.`,
|
|
10137
|
+
`6. **Re-run \`setup\` with what you gathered:** pass the code scan as \`codebase_scan\`; for docs, decision records, and roadmap, ${docRouting} Then answer the prompts below.`,
|
|
10138
|
+
``,
|
|
10139
|
+
`If there is genuinely no code and no docs yet, skip this and generate from what the user tells you \u2014 the zero-context path is fully supported.`
|
|
10140
|
+
].join("\n");
|
|
10141
|
+
}
|
|
10142
|
+
var PAPI_DOCS_QUICKSTART_URL = "https://getpapi.ai/docs/guide/quick-start";
|
|
10143
|
+
var PAPI_PUBLIC_REPO_URL = "https://github.com/getpapi/papi";
|
|
10144
|
+
function buildNewProjectSetupInstruction(opts) {
|
|
10145
|
+
const visionRouting = opts.hosted ? "paste anything they share (notes, a PRD, links) into `description` \u2014 over a remote connector PAPI cannot read local file paths." : "fold anything they share (notes, a PRD, a sketch, links) into `description`, or pass local doc paths as `sources`.";
|
|
10146
|
+
return [
|
|
10147
|
+
`**\u{1F331} New project \u2014 start from the vision.**`,
|
|
10148
|
+
`There's no existing code to mine, so setup builds from what the user wants to create. Do two things before answering the prompts below.`,
|
|
10149
|
+
``,
|
|
10150
|
+
`1. **Get oriented on PAPI.** So you can guide the user well \u2014 whatever assistant you are \u2014 skim the quick-start docs at ${PAPI_DOCS_QUICKSTART_URL} and the public repo at ${PAPI_PUBLIC_REPO_URL}. They explain the plan \u2192 build \u2192 review cycle the user will run after setup.`,
|
|
10151
|
+
`2. **Draw out the vision with the user.** Ask what they're building and for whom, the core problem it solves, and what success looks like \u2014 enough to write a real brief, a North Star, and a starter backlog rather than a generic scaffold. If they already have a PRD, sketch, notes, or links, ${visionRouting}`,
|
|
10152
|
+
``,
|
|
10153
|
+
`Then answer the prompts below to generate the brief, decisions, North Star, and a vision-first starter backlog.`
|
|
10154
|
+
].join("\n");
|
|
10155
|
+
}
|
|
10104
10156
|
var AD_SEED_SYSTEM = `You are a technical architect seeding initial Active Decisions for a new software project managed by PAPI.
|
|
10105
10157
|
|
|
10106
10158
|
Active Decisions (ADs) are documented architectural choices with confidence levels. They guide the planner and builder agents \u2014 without ADs, planning output is generic and unhelpful.
|
|
10107
10159
|
|
|
10108
10160
|
IMPORTANT: You are running as a non-interactive API call. Do NOT ask questions. Produce decisions directly.
|
|
10109
10161
|
|
|
10162
|
+
## SOURCE OF TRUTH \u2014 extract real decisions first
|
|
10163
|
+
|
|
10164
|
+
If the context includes decision records, ADRs, a roadmap, a README, or codebase analysis, your PRIMARY job is to EXTRACT the real, already-made decisions from that material \u2014 not to invent generic ones. Read the provided context and capture each load-bearing choice the project has actually made (its stack, data model, architecture, positioning, deployment posture). Only fall back to informed defaults for a project of this type when the context contains no evidenced decisions.
|
|
10165
|
+
|
|
10110
10166
|
## OUTPUT FORMAT
|
|
10111
10167
|
|
|
10112
|
-
Return a JSON array of
|
|
10168
|
+
Return a JSON array of Active Decisions \u2014 ONE per real decision you find. Each AD must have:
|
|
10113
10169
|
- "id": "AD-1", "AD-2", etc.
|
|
10114
10170
|
- "body": Full markdown block including ### heading, confidence tag, and body text
|
|
10115
10171
|
|
|
@@ -10123,17 +10179,20 @@ The body format for each AD:
|
|
|
10123
10179
|
|
|
10124
10180
|
## GUIDELINES
|
|
10125
10181
|
|
|
10126
|
-
-
|
|
10127
|
-
-
|
|
10128
|
-
-
|
|
10182
|
+
- **Count follows the evidence \u2014 there is NO fixed target.** Seed as many ADs as there are real, distinct decisions in the material. Do NOT pad to a number, and do NOT fabricate decisions to hit a count. When the context has no evidenced decisions, return a small set (2-4) of informed defaults for this project type.
|
|
10183
|
+
- **Confidence reflects evidence:** a decision explicitly documented as settled in the source may be HIGH; an informed default you inferred stays MEDIUM.
|
|
10184
|
+
- Never mint an AD that is not a genuine stance-with-alternatives \u2014 a preference, a fact, or a config value is NOT an AD.
|
|
10185
|
+
- Focus on decisions that genuinely differ by project \u2014 avoid generic truisms.
|
|
10186
|
+
- Each AD should be actionable and falsifiable (something the team could decide differently).
|
|
10129
10187
|
- Cover different concerns: architecture, data, deployment, testing strategy, API design, etc.
|
|
10130
|
-
- Keep each AD body to 4-6 lines \u2014 concise and scannable
|
|
10188
|
+
- Keep each AD body to 4-6 lines \u2014 concise and scannable.
|
|
10189
|
+
- **Do NOT duplicate** any decision already listed as an existing Active Decision in the context \u2014 skip it entirely.
|
|
10131
10190
|
- **Quality bar:** ADs are for product and architecture choices that constrain future work \u2014 technology selections, data model designs, UX principles, strategic positioning. They are NOT for process preferences, configuration choices, or temporary workarounds.
|
|
10132
10191
|
|
|
10133
10192
|
Return ONLY valid JSON \u2014 no preamble, no code fences, no explanation.`;
|
|
10134
10193
|
function buildAdSeedPrompt(ctx) {
|
|
10135
10194
|
const parts = [
|
|
10136
|
-
`
|
|
10195
|
+
ctx.codebaseContext ? `Extract the real Active Decisions for this project from the context below. Seed one AD per genuine decision \u2014 do not cap the count, and do not fabricate.` : `Seed the informed default Active Decisions for this project (no decision docs were supplied \u2014 infer sensible MEDIUM-confidence defaults for this project type).`,
|
|
10137
10196
|
"",
|
|
10138
10197
|
`**Project:** ${ctx.projectName}`,
|
|
10139
10198
|
`**Type:** ${ctx.projectType}`,
|
|
@@ -10146,12 +10205,57 @@ function buildAdSeedPrompt(ctx) {
|
|
|
10146
10205
|
if (ctx.constraints) {
|
|
10147
10206
|
parts.push(`**Constraints:** ${ctx.constraints}`);
|
|
10148
10207
|
}
|
|
10208
|
+
if (ctx.codebaseContext) {
|
|
10209
|
+
parts.push(
|
|
10210
|
+
"",
|
|
10211
|
+
"## Project context \u2014 extract the real decisions from here",
|
|
10212
|
+
ctx.codebaseContext
|
|
10213
|
+
);
|
|
10214
|
+
}
|
|
10215
|
+
if (ctx.existingDecisions && ctx.existingDecisions.length > 0) {
|
|
10216
|
+
parts.push(
|
|
10217
|
+
"",
|
|
10218
|
+
"## Existing Active Decisions \u2014 do NOT duplicate these",
|
|
10219
|
+
...ctx.existingDecisions.map((d) => `- ${d}`)
|
|
10220
|
+
);
|
|
10221
|
+
}
|
|
10149
10222
|
parts.push(
|
|
10150
10223
|
"",
|
|
10151
10224
|
'Return a JSON array of AD objects with "id" and "body" fields. No other text.'
|
|
10152
10225
|
);
|
|
10153
10226
|
return parts.join("\n");
|
|
10154
10227
|
}
|
|
10228
|
+
var NORTH_STAR_SYSTEM = `You are helping a builder define the North Star for a software project set up with PAPI.
|
|
10229
|
+
|
|
10230
|
+
A North Star is the ONE outcome that best captures whether the project is succeeding \u2014 a single, measurable, user-centred statement the team can steer by. It is not a feature list and not a vision paragraph.
|
|
10231
|
+
|
|
10232
|
+
IMPORTANT: You are running as a non-interactive API call. Do NOT ask questions in your output.
|
|
10233
|
+
|
|
10234
|
+
## HOW TO PRODUCE IT
|
|
10235
|
+
- If the provided context (brief, docs, decision records) ALREADY states a North Star, goal metric, or primary success measure, EXTRACT and restate it \u2014 do not invent a competing one.
|
|
10236
|
+
- Otherwise, PROPOSE the most fitting North Star from the project's purpose and users. (The calling agent will confirm it with the user before it is saved.)
|
|
10237
|
+
|
|
10238
|
+
## OUTPUT FORMAT
|
|
10239
|
+
Return ONLY the North Star statement \u2014 one or two sentences, concrete and measurable where possible. No heading, no preamble, no quotes, no code fences.`;
|
|
10240
|
+
function buildNorthStarPrompt(inputs) {
|
|
10241
|
+
const parts = [
|
|
10242
|
+
`Define the North Star for this project.`,
|
|
10243
|
+
"",
|
|
10244
|
+
`**Project:** ${inputs.projectName}`,
|
|
10245
|
+
`**Description:** ${inputs.description?.trim() || "(not provided \u2014 infer from context below)"}`,
|
|
10246
|
+
`**Target users:** ${inputs.targetUsers?.trim() || "(not provided \u2014 infer from context below)"}`,
|
|
10247
|
+
`**Problems solved:** ${inputs.problems}`
|
|
10248
|
+
];
|
|
10249
|
+
if (inputs.codebaseContext) {
|
|
10250
|
+
parts.push(
|
|
10251
|
+
"",
|
|
10252
|
+
"## Project context \u2014 extract an existing North Star from here if one is stated",
|
|
10253
|
+
inputs.codebaseContext
|
|
10254
|
+
);
|
|
10255
|
+
}
|
|
10256
|
+
parts.push("", "Return only the North Star statement.");
|
|
10257
|
+
return parts.join("\n");
|
|
10258
|
+
}
|
|
10155
10259
|
var CONVENTIONS_SYSTEM = `You are a senior software engineer generating CLAUDE.md coding conventions for a new project.
|
|
10156
10260
|
|
|
10157
10261
|
IMPORTANT: You are running as a non-interactive API call. Do NOT ask questions. Produce conventions directly.
|
|
@@ -12327,10 +12431,10 @@ ${cleanContent}`;
|
|
|
12327
12431
|
}
|
|
12328
12432
|
for (const handoff of data.cycleHandoffs) {
|
|
12329
12433
|
const resolvedId = newTaskIdMap.get(handoff.taskId) ?? handoff.taskId;
|
|
12330
|
-
if (
|
|
12434
|
+
if (isUnresolvedPlaceholder(handoff.taskId, newTaskIdMap)) {
|
|
12331
12435
|
const titleLine = handoff.buildHandoff.match(/^Task:\s*(.+)/m)?.[1] ?? "(unknown)";
|
|
12332
|
-
|
|
12333
|
-
`
|
|
12436
|
+
warnings.push(
|
|
12437
|
+
`UNRESOLVED handoff "${handoff.taskId}" (task: ${titleLine.slice(0, 80)}) \u2014 no newTask carries this tempId/index; handoff NOT written and the task is missing from the cycle. Ensure each newTasks entry has a tempId matching its cycleHandoffs taskId (task-2242).`
|
|
12334
12438
|
);
|
|
12335
12439
|
continue;
|
|
12336
12440
|
}
|
|
@@ -12596,7 +12700,7 @@ async function processLlmOutput(adapter2, config2, rawOutput, mode, cycleNumber,
|
|
|
12596
12700
|
skippedCancellations
|
|
12597
12701
|
};
|
|
12598
12702
|
}
|
|
12599
|
-
async function preparePlan(adapter2, config2, filters, focus, force, handoffsOnly, skipHandoffs, tracker) {
|
|
12703
|
+
async function preparePlan(adapter2, config2, filters, focus, force, handoffsOnly, skipHandoffs, tracker, density) {
|
|
12600
12704
|
const prepareTimer = startTimer();
|
|
12601
12705
|
tracker?.mark("validate_and_prepare");
|
|
12602
12706
|
let t = startTimer();
|
|
@@ -12657,6 +12761,7 @@ async function preparePlan(adapter2, config2, filters, focus, force, handoffsOnl
|
|
|
12657
12761
|
throw new Error("TEMPLATE_BRIEF");
|
|
12658
12762
|
}
|
|
12659
12763
|
if (skipHandoffs) context.skipHandoffs = true;
|
|
12764
|
+
if (density) context.density = density;
|
|
12660
12765
|
tracker?.mark("codebase_scan");
|
|
12661
12766
|
t = startTimer();
|
|
12662
12767
|
try {
|
|
@@ -13297,11 +13402,6 @@ var planTool = {
|
|
|
13297
13402
|
type: "string",
|
|
13298
13403
|
description: 'Absolute path to a file containing the plan output (mode "apply" only). Use this when the response is too large to pass as a string parameter (some hosts cap inputs around 50KB). The file must be absolute, exist, and be \u2264500KB. Mutually exclusive with llm_response.'
|
|
13299
13404
|
},
|
|
13300
|
-
plan_mode: {
|
|
13301
|
-
type: "string",
|
|
13302
|
-
enum: ["bootstrap", "full"],
|
|
13303
|
-
description: 'The plan mode returned from prepare phase (mode "apply" only).'
|
|
13304
|
-
},
|
|
13305
13405
|
cycle_number: {
|
|
13306
13406
|
type: "number",
|
|
13307
13407
|
description: 'The cycle number returned from prepare phase (mode "apply" only).'
|
|
@@ -13330,6 +13430,11 @@ var planTool = {
|
|
|
13330
13430
|
type: "string",
|
|
13331
13431
|
description: 'User direction for this cycle \u2014 what to focus on, which phase/tasks to prioritise, or constraints to respect. Overrides the autonomous priority tier system. Example: "Focus on Schema Model phase 9 tasks this cycle".'
|
|
13332
13432
|
},
|
|
13433
|
+
density: {
|
|
13434
|
+
type: "string",
|
|
13435
|
+
enum: ["light", "standard", "deep"],
|
|
13436
|
+
description: 'Target cycle density \u2014 how many tasks the planner should aim to include. "light" \u2248 2-3 tasks, "standard" \u2248 3-5 (the default when omitted), "deep" \u2248 6-8. A TARGET, not a floor or cap: the planner still honours user direction, pre-assigned tasks, and impact sizing, and plans fewer if fewer genuinely-valuable tasks exist.'
|
|
13437
|
+
},
|
|
13333
13438
|
force: {
|
|
13334
13439
|
type: "boolean",
|
|
13335
13440
|
description: "Bypass planning guards (unreleased cycle block, strategy review hard-block). Only use when explicitly requested by the user."
|
|
@@ -13428,6 +13533,7 @@ async function handlePlan(adapter2, config2, args) {
|
|
|
13428
13533
|
const focus = typeof args.focus === "string" ? args.focus : void 0;
|
|
13429
13534
|
const force = args.force === true;
|
|
13430
13535
|
const handoffsOnly = args.handoffs_only === true;
|
|
13536
|
+
const density = args.density === "light" || args.density === "standard" || args.density === "deep" ? args.density : void 0;
|
|
13431
13537
|
const tracker = new ProgressTracker(toolMode === "apply" ? "apply_validate" : "prepare_validate").bindStream(adapter2, { stage: "plan" });
|
|
13432
13538
|
try {
|
|
13433
13539
|
if (toolMode === "apply") {
|
|
@@ -13439,7 +13545,6 @@ async function handlePlan(adapter2, config2, args) {
|
|
|
13439
13545
|
return errorResponse(resolved.error);
|
|
13440
13546
|
}
|
|
13441
13547
|
const llmResponse = resolved.llmResponse;
|
|
13442
|
-
const planMode = args.plan_mode || "full";
|
|
13443
13548
|
const rawCycleNumber = args.cycle_number != null ? Number(args.cycle_number) : NaN;
|
|
13444
13549
|
const strategyReviewWarning = args.strategy_review_warning || "";
|
|
13445
13550
|
const prep = planPrepareCache.peek(callerKey) ?? loadPrepareSpill(adapter2.getProjectId?.(), callerKey);
|
|
@@ -13469,6 +13574,7 @@ async function handlePlan(adapter2, config2, args) {
|
|
|
13469
13574
|
);
|
|
13470
13575
|
}
|
|
13471
13576
|
const cycleNumber = newCycleNumber - 1;
|
|
13577
|
+
const planMode = determineMode(cycleNumber);
|
|
13472
13578
|
planPrepareCache.clear(callerKey);
|
|
13473
13579
|
clearPrepareSpill(adapter2.getProjectId?.(), callerKey);
|
|
13474
13580
|
let utilisation;
|
|
@@ -13500,7 +13606,7 @@ async function handlePlan(adapter2, config2, args) {
|
|
|
13500
13606
|
} catch {
|
|
13501
13607
|
}
|
|
13502
13608
|
const skipHandoffs = args.skip_handoffs === true;
|
|
13503
|
-
const result = await preparePlan(adapter2, config2, filters, focus, force, handoffsOnly, skipHandoffs, tracker);
|
|
13609
|
+
const result = await preparePlan(adapter2, config2, filters, focus, force, handoffsOnly, skipHandoffs, tracker, density);
|
|
13504
13610
|
const prepareState = {
|
|
13505
13611
|
contextHashes: result.contextHashes,
|
|
13506
13612
|
userMessage: result.userMessage,
|
|
@@ -13550,7 +13656,6 @@ Follow the system prompt and context below to generate a complete cycle plan.
|
|
|
13550
13656
|
When done, call \`plan\` again with:
|
|
13551
13657
|
- \`mode\`: "apply"
|
|
13552
13658
|
- \`llm_response\`: your complete output (both parts)
|
|
13553
|
-
- \`plan_mode\`: "${result.mode}"
|
|
13554
13659
|
- \`cycle_number\`: ${result.cycleNumber + 1}
|
|
13555
13660
|
- \`strategy_review_warning\`: "${result.strategyReviewWarning.replace(/"/g, '\\"')}"
|
|
13556
13661
|
|
|
@@ -13575,7 +13680,9 @@ ${result.userMessage}
|
|
|
13575
13680
|
} catch (err) {
|
|
13576
13681
|
const message = err instanceof Error ? err.message : String(err);
|
|
13577
13682
|
if (message === "TEMPLATE_BRIEF") {
|
|
13578
|
-
return textResponse(
|
|
13683
|
+
return textResponse(
|
|
13684
|
+
"Can't plan a full cycle yet \u2014 your Product Brief is still the placeholder template.\n\nPlan mode is derived automatically from your cycle count: your first-ever cycle runs in bootstrap mode (which tolerates a thin brief), and every cycle after that runs in full mode (which needs a real brief to scope work against). This project is past its first cycle, so full mode expected a real brief but found the template text.\n\nFix: run `setup` to generate a real Product Brief for your project, then run `plan` again."
|
|
13685
|
+
);
|
|
13579
13686
|
}
|
|
13580
13687
|
const isKnownFriendly = /^(Cannot run plan|Strategy Review gate|No tasks assigned|llm_response is required|cycle_number|Merge conflicts|applyPlan timed out)/i.test(message);
|
|
13581
13688
|
if (isKnownFriendly) {
|
|
@@ -18008,6 +18115,7 @@ async function prepareSetup(adapter2, config2, input) {
|
|
|
18008
18115
|
codebaseContext: codebaseSummary
|
|
18009
18116
|
})
|
|
18010
18117
|
};
|
|
18118
|
+
const existingDecisions = adapter2.getActiveDecisions ? (await adapter2.getActiveDecisions({ includeRetired: true }).catch(() => [])).filter((a) => !a.superseded).map((a) => `${a.displayId}: ${a.title}`) : [];
|
|
18011
18119
|
const adSeedPrompt = input.projectType ? {
|
|
18012
18120
|
system: AD_SEED_SYSTEM,
|
|
18013
18121
|
user: buildAdSeedPrompt({
|
|
@@ -18018,7 +18126,11 @@ async function prepareSetup(adapter2, config2, input) {
|
|
|
18018
18126
|
problems: input.problems,
|
|
18019
18127
|
teamSize: input.teamSize,
|
|
18020
18128
|
deploymentTarget: input.deploymentTarget,
|
|
18021
|
-
constraints: input.constraints
|
|
18129
|
+
constraints: input.constraints,
|
|
18130
|
+
// task-2811: feed the pre-scanned codebase/decision-doc context so ADs are
|
|
18131
|
+
// extracted from the project's real decisions, not invented generically.
|
|
18132
|
+
codebaseContext: codebaseSummary,
|
|
18133
|
+
existingDecisions: existingDecisions.length > 0 ? existingDecisions : void 0
|
|
18022
18134
|
})
|
|
18023
18135
|
} : void 0;
|
|
18024
18136
|
const conventionsPrompt = input.projectType ? {
|
|
@@ -18052,6 +18164,21 @@ async function prepareSetup(adapter2, config2, input) {
|
|
|
18052
18164
|
projectType: input.projectType
|
|
18053
18165
|
})
|
|
18054
18166
|
} : void 0;
|
|
18167
|
+
const existingNorthStar = adapter2.getCurrentNorthStar ? await adapter2.getCurrentNorthStar().catch(() => null) : null;
|
|
18168
|
+
const northStarAlreadyExists = Boolean(existingNorthStar && existingNorthStar.trim());
|
|
18169
|
+
const northStarPrompt = northStarAlreadyExists ? void 0 : {
|
|
18170
|
+
system: NORTH_STAR_SYSTEM,
|
|
18171
|
+
user: buildNorthStarPrompt({
|
|
18172
|
+
projectName: input.projectName,
|
|
18173
|
+
description: input.description,
|
|
18174
|
+
targetUsers: input.targetUsers,
|
|
18175
|
+
problems: input.problems,
|
|
18176
|
+
codebaseContext: codebaseSummary
|
|
18177
|
+
})
|
|
18178
|
+
};
|
|
18179
|
+
const willGenerateBrief = !effectiveBriefAlreadyExists;
|
|
18180
|
+
const preScanInstruction = willGenerateBrief && isExistingProject && !input.codebaseScan ? buildPreScanInstruction({ hosted: !canScanFilesystem, existingProject: true }) : void 0;
|
|
18181
|
+
const newProjectInstruction = willGenerateBrief && !isExistingProject ? buildNewProjectSetupInstruction({ hosted: !canScanFilesystem }) : void 0;
|
|
18055
18182
|
return {
|
|
18056
18183
|
createdProject,
|
|
18057
18184
|
projectName: input.projectName,
|
|
@@ -18059,17 +18186,21 @@ async function prepareSetup(adapter2, config2, input) {
|
|
|
18059
18186
|
adSeedPrompt,
|
|
18060
18187
|
conventionsPrompt,
|
|
18061
18188
|
initialTasksPrompt,
|
|
18189
|
+
northStarPrompt,
|
|
18190
|
+
northStarAlreadyExists,
|
|
18062
18191
|
codebaseSummary,
|
|
18063
18192
|
detectedCodebaseType,
|
|
18064
18193
|
autoDetected: autoDetected && detectedCodebaseType !== "new_project",
|
|
18065
18194
|
briefAlreadyExists: effectiveBriefAlreadyExists,
|
|
18066
18195
|
briefWillRegenerate,
|
|
18067
18196
|
briefRegenReason,
|
|
18197
|
+
preScanInstruction,
|
|
18198
|
+
newProjectInstruction,
|
|
18068
18199
|
warnings: warnings.length > 0 ? warnings : void 0,
|
|
18069
18200
|
filesToWrite: prepareCollector.isEmpty() ? void 0 : prepareCollector
|
|
18070
18201
|
};
|
|
18071
18202
|
}
|
|
18072
|
-
async function applySetup(adapter2, config2, input, briefText, adSeedText, conventionsText, initialTasksText) {
|
|
18203
|
+
async function applySetup(adapter2, config2, input, briefText, adSeedText, conventionsText, initialTasksText, northStarText) {
|
|
18073
18204
|
const collector = new FileWriteCollector();
|
|
18074
18205
|
const createdProject = await scaffoldPapiDir(adapter2, config2, input, collector);
|
|
18075
18206
|
let effectiveBriefText = briefText;
|
|
@@ -18096,6 +18227,21 @@ async function applySetup(adapter2, config2, input, briefText, adSeedText, conve
|
|
|
18096
18227
|
}
|
|
18097
18228
|
}
|
|
18098
18229
|
const { seededAds, warnings } = await applySetupOutputs(adapter2, config2, input, collector, effectiveBriefText, adSeedText, conventionsText);
|
|
18230
|
+
let northStarSet = false;
|
|
18231
|
+
const northStarStatement = northStarText?.trim();
|
|
18232
|
+
if (northStarStatement && adapter2.upsertNorthStar) {
|
|
18233
|
+
try {
|
|
18234
|
+
const existing = adapter2.getCurrentNorthStar ? await adapter2.getCurrentNorthStar().catch(() => null) : null;
|
|
18235
|
+
if (!(existing && existing.trim()) || input.force) {
|
|
18236
|
+
await adapter2.upsertNorthStar(northStarStatement, 0);
|
|
18237
|
+
northStarSet = true;
|
|
18238
|
+
}
|
|
18239
|
+
} catch (err) {
|
|
18240
|
+
warnings.push(
|
|
18241
|
+
`North Star not saved \u2014 ${err instanceof Error ? err.message : String(err)}. Set one later during your first \`plan\`.`
|
|
18242
|
+
);
|
|
18243
|
+
}
|
|
18244
|
+
}
|
|
18099
18245
|
let createdTasks = 0;
|
|
18100
18246
|
let tasksSkipped = 0;
|
|
18101
18247
|
if (initialTasksText?.trim()) {
|
|
@@ -18241,6 +18387,7 @@ async function applySetup(adapter2, config2, input, briefText, adSeedText, conve
|
|
|
18241
18387
|
seededAds,
|
|
18242
18388
|
createdTasks,
|
|
18243
18389
|
tasksSkipped: tasksSkipped > 0 ? tasksSkipped : void 0,
|
|
18390
|
+
northStarSet: northStarSet || void 0,
|
|
18244
18391
|
briefRegenerated: briefRegenerated || void 0,
|
|
18245
18392
|
cursorScaffolded,
|
|
18246
18393
|
gitignoreNote,
|
|
@@ -18299,6 +18446,10 @@ var setupTool = {
|
|
|
18299
18446
|
type: "string",
|
|
18300
18447
|
description: 'Your generated conventions markdown to append to CLAUDE.md (mode "apply" only). Optional.'
|
|
18301
18448
|
},
|
|
18449
|
+
north_star_response: {
|
|
18450
|
+
type: "string",
|
|
18451
|
+
description: `The project's North Star statement \u2014 one or two sentences (mode "apply" only). Provide the statement you extracted from the docs or agreed with the user in response to the prepare-phase North Star prompt. Optional; omit when the project already has one.`
|
|
18452
|
+
},
|
|
18302
18453
|
project_name: {
|
|
18303
18454
|
type: "string",
|
|
18304
18455
|
description: "Name of the project."
|
|
@@ -18408,6 +18559,9 @@ function formatSuccessResponse(result, constraints, writesClaudeMd = true) {
|
|
|
18408
18559
|
const adNote = result.seededAds > 0 ? `
|
|
18409
18560
|
|
|
18410
18561
|
${result.seededAds} Active Decision${result.seededAds > 1 ? "s" : ""} seeded based on project type \u2014 review them with \`strategy_review\` after your first cycle.` : "";
|
|
18562
|
+
const northStarNote = result.northStarSet ? `
|
|
18563
|
+
|
|
18564
|
+
\u2B50 North Star set \u2014 your project starts steered (no "define a North Star" warning on \`orient\`).` : "";
|
|
18411
18565
|
const taskNote = result.createdTasks > 0 || (result.tasksSkipped ?? 0) > 0 ? (() => {
|
|
18412
18566
|
const created = result.createdTasks > 0 ? `${result.createdTasks} initial backlog task${result.createdTasks > 1 ? "s" : ""} created` : "";
|
|
18413
18567
|
const skipped = (result.tasksSkipped ?? 0) > 0 ? `${result.tasksSkipped} duplicate${(result.tasksSkipped ?? 0) > 1 ? "s" : ""} skipped` : "";
|
|
@@ -18427,7 +18581,7 @@ ${[created, skipped].filter(Boolean).join(", ")}.${idea}`;
|
|
|
18427
18581
|
${result.warnings.map((w) => `- ${w}`).join("\n")}` : "";
|
|
18428
18582
|
const filesToWriteSection = result.filesToWrite ? formatFilesToWriteSection(result.filesToWrite) : "";
|
|
18429
18583
|
return textResponse(
|
|
18430
|
-
`${prefix}Product Brief generated and saved.${briefRegenNote}${adNote}${taskNote}${constraintsHint}${editorNote}${gitignoreNote}${warningsNote}
|
|
18584
|
+
`${prefix}Product Brief generated and saved.${briefRegenNote}${adNote}${northStarNote}${taskNote}${constraintsHint}${editorNote}${gitignoreNote}${warningsNote}
|
|
18431
18585
|
|
|
18432
18586
|
**Important:** Setup created/modified files (${harnessFiles}, .claude/settings.json, docs/). Commit these changes before running \`build_execute\` \u2014 it requires a clean working directory.
|
|
18433
18587
|
|
|
@@ -18460,8 +18614,9 @@ PAPI needs the project name. Description and target users are optional \u2014 th
|
|
|
18460
18614
|
const adSeedResponse = args.ad_seed_response;
|
|
18461
18615
|
const conventionsResponse = args.conventions_response;
|
|
18462
18616
|
const initialTasksResponse = args.initial_tasks_response;
|
|
18617
|
+
const northStarResponse = args.north_star_response;
|
|
18463
18618
|
tracker.mark("apply_setup_writeback");
|
|
18464
|
-
const result = await applySetup(adapter2, config2, input, briefResponse, adSeedResponse, conventionsResponse, initialTasksResponse);
|
|
18619
|
+
const result = await applySetup(adapter2, config2, input, briefResponse, adSeedResponse, conventionsResponse, initialTasksResponse, northStarResponse);
|
|
18465
18620
|
tracker.mark("apply_format_response");
|
|
18466
18621
|
return formatSuccessResponse(result, args.constraints, writesClaudeMd);
|
|
18467
18622
|
}
|
|
@@ -18517,7 +18672,13 @@ PAPI needs the project name. Description and target users are optional \u2014 th
|
|
|
18517
18672
|
""
|
|
18518
18673
|
);
|
|
18519
18674
|
}
|
|
18520
|
-
if (result.
|
|
18675
|
+
if (result.preScanInstruction) {
|
|
18676
|
+
sections.push(result.preScanInstruction, "");
|
|
18677
|
+
}
|
|
18678
|
+
if (result.newProjectInstruction) {
|
|
18679
|
+
sections.push(result.newProjectInstruction, "");
|
|
18680
|
+
}
|
|
18681
|
+
if (!result.preScanInstruction && !result.newProjectInstruction && result.briefPrompt && !result.briefAlreadyExists) {
|
|
18521
18682
|
sections.push(
|
|
18522
18683
|
`**\u{1F4C4} Before you write the brief \u2014 does the user already have a PRD, brief, spec, or design doc?**`,
|
|
18523
18684
|
`A brief generated from the user's real spec produces a far better first plan than a generic scaffold.`,
|
|
@@ -18534,6 +18695,7 @@ PAPI needs the project name. Description and target users are optional \u2014 th
|
|
|
18534
18695
|
result.briefPrompt ? `- \`brief_response\`: your Product Brief markdown` : "",
|
|
18535
18696
|
result.adSeedPrompt ? `- \`ad_seed_response\`: your AD seed JSON array` : "",
|
|
18536
18697
|
result.conventionsPrompt ? `- \`conventions_response\`: your conventions markdown` : "",
|
|
18698
|
+
result.northStarPrompt ? `- \`north_star_response\`: the North Star statement (confirm it with the user first)` : "",
|
|
18537
18699
|
result.initialTasksPrompt ? `- \`initial_tasks_response\`: your initial tasks JSON array` : "",
|
|
18538
18700
|
`- Plus all the original setup fields (project_name, description, target_users${isExisting ? ", existing_project: true" : ""})`,
|
|
18539
18701
|
"",
|
|
@@ -18586,6 +18748,25 @@ ${result.conventionsPrompt.system}
|
|
|
18586
18748
|
"",
|
|
18587
18749
|
`<context>
|
|
18588
18750
|
${result.conventionsPrompt.user}
|
|
18751
|
+
</context>`
|
|
18752
|
+
);
|
|
18753
|
+
}
|
|
18754
|
+
if (result.northStarPrompt) {
|
|
18755
|
+
sectionNum++;
|
|
18756
|
+
sections.push(
|
|
18757
|
+
"",
|
|
18758
|
+
`---`,
|
|
18759
|
+
"",
|
|
18760
|
+
`### ${sectionNum}. North Star`,
|
|
18761
|
+
"",
|
|
18762
|
+
`This project has no North Star yet \u2014 define one now so it starts steered (otherwise \`orient\` will warn that none is set). Extract it from the docs if one is already stated there; otherwise propose one and **confirm it with the user** before applying. Return it as \`north_star_response\`.`,
|
|
18763
|
+
"",
|
|
18764
|
+
`<system_prompt>
|
|
18765
|
+
${result.northStarPrompt.system}
|
|
18766
|
+
</system_prompt>`,
|
|
18767
|
+
"",
|
|
18768
|
+
`<context>
|
|
18769
|
+
${result.northStarPrompt.user}
|
|
18589
18770
|
</context>`
|
|
18590
18771
|
);
|
|
18591
18772
|
}
|
|
@@ -21863,6 +22044,11 @@ var buildExecuteTool = {
|
|
|
21863
22044
|
description: 'IDs of cycle_learnings this build directly resolves or acts on. Use when the BUILD HANDOFF references a prior learning entry (e.g. "addresses learning abc-123"). Links the learning to this task as action_taken=task_created.',
|
|
21864
22045
|
items: { type: "string" }
|
|
21865
22046
|
},
|
|
22047
|
+
fixed_issues: {
|
|
22048
|
+
type: "array",
|
|
22049
|
+
description: `cycle_learnings UUIDs of discovered issues this build FIXED. Stamps resolved_at (via the existing discovered_issue_resolve path) so the hub's "What PAPI caught" surface counts them as fixed on the caught\u2192fixed ledger \u2014 triage-and-fix at build time, no separate tool call. Distinct from resolves_learnings, which only LINKS a learning to this task without closing it. Best-effort and idempotent.`,
|
|
22050
|
+
items: { type: "string" }
|
|
22051
|
+
},
|
|
21866
22052
|
production_verification: {
|
|
21867
22053
|
type: "object",
|
|
21868
22054
|
description: "Required when the branch diff touches a trigger surface (install snippets, MCP transport, auth/middleware, OAuth well-known, vercel.json, supabase functions, Dockerfile, Procfile, env declarations). Record exactly what you verified against the live deploy. http_status MUST be 2xx \u2014 non-2xx rejects the build. Server inspects the diff and enforces this, so passing it on non-trigger tasks is harmless but unnecessary.",
|
|
@@ -22063,6 +22249,10 @@ async function handleBuildExecute(adapter2, config2, args, clientName) {
|
|
|
22063
22249
|
}
|
|
22064
22250
|
const tracker = new ProgressTracker("start_build").bindStream(adapter2, { stage: "build", taskId });
|
|
22065
22251
|
try {
|
|
22252
|
+
const scopeTask = await adapter2.getTask(taskId).catch(() => null);
|
|
22253
|
+
if (scopeTask) {
|
|
22254
|
+
tracker.setStreamScope({ taskId: scopeTask.displayId ?? scopeTask.id, cycle: scopeTask.cycle ?? null });
|
|
22255
|
+
}
|
|
22066
22256
|
await tracker.recordStep("started");
|
|
22067
22257
|
const result = await startBuild(adapter2, config2, taskId, { light }, clientName);
|
|
22068
22258
|
tracker.setStreamScope({ taskId: result.task.displayId ?? result.task.id, cycle: result.task.cycle ?? null });
|
|
@@ -22175,6 +22365,7 @@ async function handleExecuteComplete(adapter2, config2, taskId, args, light = fa
|
|
|
22175
22365
|
const deadEnds = args.dead_ends;
|
|
22176
22366
|
const rawBriefImplications = args.brief_implications;
|
|
22177
22367
|
const resolvesLearnings = Array.isArray(args.resolves_learnings) ? args.resolves_learnings : void 0;
|
|
22368
|
+
const fixedIssues = Array.isArray(args.fixed_issues) ? args.fixed_issues : void 0;
|
|
22178
22369
|
const rawPreview = args.preview;
|
|
22179
22370
|
const preview = rawPreview ? {
|
|
22180
22371
|
urls: Array.isArray(rawPreview.urls) ? rawPreview.urls.filter((u) => typeof u === "string") : void 0,
|
|
@@ -22261,6 +22452,16 @@ async function handleExecuteComplete(adapter2, config2, taskId, args, light = fa
|
|
|
22261
22452
|
metadata: { learningsLinkedCount: result.learningsLinkedCount }
|
|
22262
22453
|
});
|
|
22263
22454
|
}
|
|
22455
|
+
let fixedResolvedCount = 0;
|
|
22456
|
+
if (fixedIssues && fixedIssues.length > 0 && typeof adapter2.markCycleLearningResolved === "function") {
|
|
22457
|
+
for (const learningId of fixedIssues) {
|
|
22458
|
+
try {
|
|
22459
|
+
await adapter2.markCycleLearningResolved(learningId, clientName);
|
|
22460
|
+
fixedResolvedCount++;
|
|
22461
|
+
} catch {
|
|
22462
|
+
}
|
|
22463
|
+
}
|
|
22464
|
+
}
|
|
22264
22465
|
await tracker.recordStep("moving-to-review", {
|
|
22265
22466
|
metadata: { status: result.task.status }
|
|
22266
22467
|
});
|
|
@@ -22286,7 +22487,10 @@ async function handleExecuteComplete(adapter2, config2, taskId, args, light = fa
|
|
|
22286
22487
|
batchRollupNote = "";
|
|
22287
22488
|
}
|
|
22288
22489
|
}
|
|
22289
|
-
|
|
22490
|
+
const fixedNote = fixedResolvedCount > 0 ? `
|
|
22491
|
+
|
|
22492
|
+
\u2705 Marked ${fixedResolvedCount} discovered issue(s) FIXED \u2014 resolved_at stamped, now counted as fixed on the hub's caught\u2192fixed ledger.` : "";
|
|
22493
|
+
return textResponse(formatCompleteResult(result) + fixedNote + docsNote + batchRollupNote);
|
|
22290
22494
|
} catch (err) {
|
|
22291
22495
|
const message = err instanceof Error ? err.message : String(err);
|
|
22292
22496
|
if (isBuildPushError(err)) {
|
package/dist/prompts.js
CHANGED
|
@@ -236,7 +236,7 @@ var PLAN_FRAGMENT_DESIGN_BRIEF = `
|
|
|
236
236
|
**Design brief task detection:** When a task's task type is "design-brief", generate a DESIGN BRIEF handoff. Replace the standard SCOPE (DO THIS) section with these type-specific sections:
|
|
237
237
|
- AUDIENCE: Who this design is for \u2014 persona and context of use (e.g. "non-technical Owner, first dashboard visit")
|
|
238
238
|
- BRAND CONSTRAINTS: Palette, typography, tone \u2014 pull from \`.impeccable.md\` (dev patterns, anti-patterns, component rules) AND \`docs/branding/brand-book.html\` (brand identity, positioning, voice canon) if present. If neither exists, state "No brand doc \u2014 Owner should define constraints before starting."
|
|
239
|
-
- DELIVERABLE FORMAT: What the output looks like \u2014
|
|
239
|
+
- DELIVERABLE FORMAT: What the output looks like \u2014 design handoff package / annotated mockup / style spec. Be specific so the person doing the work knows what "done" means.
|
|
240
240
|
- REVIEW POINTS: What the Owner must approve before the design is considered done (e.g. layout, copy, colour, imagery).
|
|
241
241
|
Keep SCOPE BOUNDARY, ACCEPTANCE CRITERIA, SECURITY CONSIDERATIONS, and PRE-BUILD VERIFICATION sections as normal.
|
|
242
242
|
Add to ACCEPTANCE CRITERIA: "[ ] Deliverable format confirmed with Owner before starting" and "[ ] Design output is self-contained \u2014 includes enough context for a developer to implement without further clarification."`;
|
|
@@ -435,6 +435,21 @@ function buildPlanFullInstructionsConditional(flags, ctx) {
|
|
|
435
435
|
if (!flags || !ctx) return PLAN_FULL_INSTRUCTIONS;
|
|
436
436
|
return composeFullModeInstructions(flags, ctx);
|
|
437
437
|
}
|
|
438
|
+
var CYCLE_DENSITY_TARGETS = {
|
|
439
|
+
light: { label: "Light", range: "2-3" },
|
|
440
|
+
standard: { label: "Standard", range: "3-5" },
|
|
441
|
+
deep: { label: "Deep", range: "6-8" }
|
|
442
|
+
};
|
|
443
|
+
function buildCycleDensityDirective(density) {
|
|
444
|
+
if (!density) return "";
|
|
445
|
+
const target = CYCLE_DENSITY_TARGETS[density];
|
|
446
|
+
return [
|
|
447
|
+
`## CYCLE DENSITY: ${target.label}`,
|
|
448
|
+
"",
|
|
449
|
+
`The user set this cycle's density to **${density}** \u2014 aim for roughly **${target.range} tasks** this cycle.`,
|
|
450
|
+
`This is a TARGET to size toward, not a hard floor or cap: still honour explicit user direction, any pre-assigned tasks, and impact-based sizing. If fewer genuinely-valuable tasks exist, plan fewer \u2014 do NOT pad the cycle to hit the number.`
|
|
451
|
+
].join("\n");
|
|
452
|
+
}
|
|
438
453
|
function buildPlanUserMessage(ctx) {
|
|
439
454
|
const modeLabel = ctx.mode.toUpperCase();
|
|
440
455
|
const parts = [
|
|
@@ -452,6 +467,10 @@ function buildPlanUserMessage(ctx) {
|
|
|
452
467
|
""
|
|
453
468
|
);
|
|
454
469
|
}
|
|
470
|
+
const densityDirective = buildCycleDensityDirective(ctx.density);
|
|
471
|
+
if (densityDirective) {
|
|
472
|
+
parts.push(densityDirective, "");
|
|
473
|
+
}
|
|
455
474
|
if (ctx.mode === "bootstrap") {
|
|
456
475
|
parts.push(PLAN_BOOTSTRAP_INSTRUCTIONS);
|
|
457
476
|
} else {
|
|
@@ -1327,15 +1346,49 @@ phases:
|
|
|
1327
1346
|
|
|
1328
1347
|
*No decisions locked yet. These are added as planning cycles confirm strategic choices.*`;
|
|
1329
1348
|
}
|
|
1349
|
+
function buildPreScanInstruction(opts) {
|
|
1350
|
+
const docRouting = opts.hosted ? "paste the relevant content (decision records, roadmap, spec excerpts) directly into `description` \u2014 over a remote connector PAPI cannot read local file paths." : "pass local doc file paths as `sources` (comma-separated), or paste content into `description`.";
|
|
1351
|
+
return [
|
|
1352
|
+
`**\u{1F50E} Before you generate anything \u2014 scan the project and gather real context.**`,
|
|
1353
|
+
`A brief and Active Decisions built from the real code and docs are far sharper than ones guessed from a project name. You (the agent running this) have local access that PAPI does not \u2014 do the gathering, then pass it back.`,
|
|
1354
|
+
``,
|
|
1355
|
+
`1. **Confirm the environment.** Check that you are running from the root of this project's dev environment \u2014 the folder that holds its source, git history, and docs. If you are not there, or you are connected over a remote connector with no access to the user's files, say so and ask the user where the project lives before continuing.`,
|
|
1356
|
+
`2. **Scan the code.** List the top-level directories and files, read the package/build manifest and the README (first ~3000 chars), and collect recent commit subjects. Pass this back as \`codebase_scan\` on the next \`setup\` call.`,
|
|
1357
|
+
`3. **Scan the docs.** Look through \`docs/\`, design notes, ADRs/decision records, and any roadmap or planning files \u2014 not just code.`,
|
|
1358
|
+
`4. **Check for sibling / separate repos.** Many projects span more than one repo (a separate frontend, backend, infra, or mobile repo, or other packages in a monorepo). Ask the user whether any related repos or directories exist, and scan those too.`,
|
|
1359
|
+
`5. **Ask the user to point you at extra context.** Prompt them: "Is there anything else I should read before setting this up \u2014 a PRD or spec, decision records, a roadmap, or links to related repos or docs?" Fold whatever they share into the scan.`,
|
|
1360
|
+
`6. **Re-run \`setup\` with what you gathered:** pass the code scan as \`codebase_scan\`; for docs, decision records, and roadmap, ${docRouting} Then answer the prompts below.`,
|
|
1361
|
+
``,
|
|
1362
|
+
`If there is genuinely no code and no docs yet, skip this and generate from what the user tells you \u2014 the zero-context path is fully supported.`
|
|
1363
|
+
].join("\n");
|
|
1364
|
+
}
|
|
1365
|
+
var PAPI_DOCS_QUICKSTART_URL = "https://getpapi.ai/docs/guide/quick-start";
|
|
1366
|
+
var PAPI_PUBLIC_REPO_URL = "https://github.com/getpapi/papi";
|
|
1367
|
+
function buildNewProjectSetupInstruction(opts) {
|
|
1368
|
+
const visionRouting = opts.hosted ? "paste anything they share (notes, a PRD, links) into `description` \u2014 over a remote connector PAPI cannot read local file paths." : "fold anything they share (notes, a PRD, a sketch, links) into `description`, or pass local doc paths as `sources`.";
|
|
1369
|
+
return [
|
|
1370
|
+
`**\u{1F331} New project \u2014 start from the vision.**`,
|
|
1371
|
+
`There's no existing code to mine, so setup builds from what the user wants to create. Do two things before answering the prompts below.`,
|
|
1372
|
+
``,
|
|
1373
|
+
`1. **Get oriented on PAPI.** So you can guide the user well \u2014 whatever assistant you are \u2014 skim the quick-start docs at ${PAPI_DOCS_QUICKSTART_URL} and the public repo at ${PAPI_PUBLIC_REPO_URL}. They explain the plan \u2192 build \u2192 review cycle the user will run after setup.`,
|
|
1374
|
+
`2. **Draw out the vision with the user.** Ask what they're building and for whom, the core problem it solves, and what success looks like \u2014 enough to write a real brief, a North Star, and a starter backlog rather than a generic scaffold. If they already have a PRD, sketch, notes, or links, ${visionRouting}`,
|
|
1375
|
+
``,
|
|
1376
|
+
`Then answer the prompts below to generate the brief, decisions, North Star, and a vision-first starter backlog.`
|
|
1377
|
+
].join("\n");
|
|
1378
|
+
}
|
|
1330
1379
|
var AD_SEED_SYSTEM = `You are a technical architect seeding initial Active Decisions for a new software project managed by PAPI.
|
|
1331
1380
|
|
|
1332
1381
|
Active Decisions (ADs) are documented architectural choices with confidence levels. They guide the planner and builder agents \u2014 without ADs, planning output is generic and unhelpful.
|
|
1333
1382
|
|
|
1334
1383
|
IMPORTANT: You are running as a non-interactive API call. Do NOT ask questions. Produce decisions directly.
|
|
1335
1384
|
|
|
1385
|
+
## SOURCE OF TRUTH \u2014 extract real decisions first
|
|
1386
|
+
|
|
1387
|
+
If the context includes decision records, ADRs, a roadmap, a README, or codebase analysis, your PRIMARY job is to EXTRACT the real, already-made decisions from that material \u2014 not to invent generic ones. Read the provided context and capture each load-bearing choice the project has actually made (its stack, data model, architecture, positioning, deployment posture). Only fall back to informed defaults for a project of this type when the context contains no evidenced decisions.
|
|
1388
|
+
|
|
1336
1389
|
## OUTPUT FORMAT
|
|
1337
1390
|
|
|
1338
|
-
Return a JSON array of
|
|
1391
|
+
Return a JSON array of Active Decisions \u2014 ONE per real decision you find. Each AD must have:
|
|
1339
1392
|
- "id": "AD-1", "AD-2", etc.
|
|
1340
1393
|
- "body": Full markdown block including ### heading, confidence tag, and body text
|
|
1341
1394
|
|
|
@@ -1349,17 +1402,20 @@ The body format for each AD:
|
|
|
1349
1402
|
|
|
1350
1403
|
## GUIDELINES
|
|
1351
1404
|
|
|
1352
|
-
-
|
|
1353
|
-
-
|
|
1354
|
-
-
|
|
1405
|
+
- **Count follows the evidence \u2014 there is NO fixed target.** Seed as many ADs as there are real, distinct decisions in the material. Do NOT pad to a number, and do NOT fabricate decisions to hit a count. When the context has no evidenced decisions, return a small set (2-4) of informed defaults for this project type.
|
|
1406
|
+
- **Confidence reflects evidence:** a decision explicitly documented as settled in the source may be HIGH; an informed default you inferred stays MEDIUM.
|
|
1407
|
+
- Never mint an AD that is not a genuine stance-with-alternatives \u2014 a preference, a fact, or a config value is NOT an AD.
|
|
1408
|
+
- Focus on decisions that genuinely differ by project \u2014 avoid generic truisms.
|
|
1409
|
+
- Each AD should be actionable and falsifiable (something the team could decide differently).
|
|
1355
1410
|
- Cover different concerns: architecture, data, deployment, testing strategy, API design, etc.
|
|
1356
|
-
- Keep each AD body to 4-6 lines \u2014 concise and scannable
|
|
1411
|
+
- Keep each AD body to 4-6 lines \u2014 concise and scannable.
|
|
1412
|
+
- **Do NOT duplicate** any decision already listed as an existing Active Decision in the context \u2014 skip it entirely.
|
|
1357
1413
|
- **Quality bar:** ADs are for product and architecture choices that constrain future work \u2014 technology selections, data model designs, UX principles, strategic positioning. They are NOT for process preferences, configuration choices, or temporary workarounds.
|
|
1358
1414
|
|
|
1359
1415
|
Return ONLY valid JSON \u2014 no preamble, no code fences, no explanation.`;
|
|
1360
1416
|
function buildAdSeedPrompt(ctx) {
|
|
1361
1417
|
const parts = [
|
|
1362
|
-
`
|
|
1418
|
+
ctx.codebaseContext ? `Extract the real Active Decisions for this project from the context below. Seed one AD per genuine decision \u2014 do not cap the count, and do not fabricate.` : `Seed the informed default Active Decisions for this project (no decision docs were supplied \u2014 infer sensible MEDIUM-confidence defaults for this project type).`,
|
|
1363
1419
|
"",
|
|
1364
1420
|
`**Project:** ${ctx.projectName}`,
|
|
1365
1421
|
`**Type:** ${ctx.projectType}`,
|
|
@@ -1372,12 +1428,57 @@ function buildAdSeedPrompt(ctx) {
|
|
|
1372
1428
|
if (ctx.constraints) {
|
|
1373
1429
|
parts.push(`**Constraints:** ${ctx.constraints}`);
|
|
1374
1430
|
}
|
|
1431
|
+
if (ctx.codebaseContext) {
|
|
1432
|
+
parts.push(
|
|
1433
|
+
"",
|
|
1434
|
+
"## Project context \u2014 extract the real decisions from here",
|
|
1435
|
+
ctx.codebaseContext
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
if (ctx.existingDecisions && ctx.existingDecisions.length > 0) {
|
|
1439
|
+
parts.push(
|
|
1440
|
+
"",
|
|
1441
|
+
"## Existing Active Decisions \u2014 do NOT duplicate these",
|
|
1442
|
+
...ctx.existingDecisions.map((d) => `- ${d}`)
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1375
1445
|
parts.push(
|
|
1376
1446
|
"",
|
|
1377
1447
|
'Return a JSON array of AD objects with "id" and "body" fields. No other text.'
|
|
1378
1448
|
);
|
|
1379
1449
|
return parts.join("\n");
|
|
1380
1450
|
}
|
|
1451
|
+
var NORTH_STAR_SYSTEM = `You are helping a builder define the North Star for a software project set up with PAPI.
|
|
1452
|
+
|
|
1453
|
+
A North Star is the ONE outcome that best captures whether the project is succeeding \u2014 a single, measurable, user-centred statement the team can steer by. It is not a feature list and not a vision paragraph.
|
|
1454
|
+
|
|
1455
|
+
IMPORTANT: You are running as a non-interactive API call. Do NOT ask questions in your output.
|
|
1456
|
+
|
|
1457
|
+
## HOW TO PRODUCE IT
|
|
1458
|
+
- If the provided context (brief, docs, decision records) ALREADY states a North Star, goal metric, or primary success measure, EXTRACT and restate it \u2014 do not invent a competing one.
|
|
1459
|
+
- Otherwise, PROPOSE the most fitting North Star from the project's purpose and users. (The calling agent will confirm it with the user before it is saved.)
|
|
1460
|
+
|
|
1461
|
+
## OUTPUT FORMAT
|
|
1462
|
+
Return ONLY the North Star statement \u2014 one or two sentences, concrete and measurable where possible. No heading, no preamble, no quotes, no code fences.`;
|
|
1463
|
+
function buildNorthStarPrompt(inputs) {
|
|
1464
|
+
const parts = [
|
|
1465
|
+
`Define the North Star for this project.`,
|
|
1466
|
+
"",
|
|
1467
|
+
`**Project:** ${inputs.projectName}`,
|
|
1468
|
+
`**Description:** ${inputs.description?.trim() || "(not provided \u2014 infer from context below)"}`,
|
|
1469
|
+
`**Target users:** ${inputs.targetUsers?.trim() || "(not provided \u2014 infer from context below)"}`,
|
|
1470
|
+
`**Problems solved:** ${inputs.problems}`
|
|
1471
|
+
];
|
|
1472
|
+
if (inputs.codebaseContext) {
|
|
1473
|
+
parts.push(
|
|
1474
|
+
"",
|
|
1475
|
+
"## Project context \u2014 extract an existing North Star from here if one is stated",
|
|
1476
|
+
inputs.codebaseContext
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
parts.push("", "Return only the North Star statement.");
|
|
1480
|
+
return parts.join("\n");
|
|
1481
|
+
}
|
|
1381
1482
|
var CONVENTIONS_SYSTEM = `You are a senior software engineer generating CLAUDE.md coding conventions for a new project.
|
|
1382
1483
|
|
|
1383
1484
|
IMPORTANT: You are running as a non-interactive API call. Do NOT ask questions. Produce conventions directly.
|
|
@@ -1497,9 +1598,13 @@ export {
|
|
|
1497
1598
|
AD_REJECTION_RULES,
|
|
1498
1599
|
AD_SEED_SYSTEM,
|
|
1499
1600
|
CONVENTIONS_SYSTEM,
|
|
1601
|
+
CYCLE_DENSITY_TARGETS,
|
|
1500
1602
|
HANDOFF_REGEN_SYSTEM,
|
|
1501
1603
|
INITIAL_TASKS_SYSTEM,
|
|
1604
|
+
NORTH_STAR_SYSTEM,
|
|
1502
1605
|
OUTPUT_QUALITY_RUBRIC,
|
|
1606
|
+
PAPI_DOCS_QUICKSTART_URL,
|
|
1607
|
+
PAPI_PUBLIC_REPO_URL,
|
|
1503
1608
|
PLAN_BOOTSTRAP_INSTRUCTIONS,
|
|
1504
1609
|
PLAN_FULL_INSTRUCTIONS,
|
|
1505
1610
|
PLAN_SYSTEM,
|
|
@@ -1509,11 +1614,15 @@ export {
|
|
|
1509
1614
|
VISION_TASKS_SYSTEM,
|
|
1510
1615
|
buildAdSeedPrompt,
|
|
1511
1616
|
buildConventionsPrompt,
|
|
1617
|
+
buildCycleDensityDirective,
|
|
1512
1618
|
buildHandoffRegenMessage,
|
|
1513
1619
|
buildHandoffsOnlyUserMessage,
|
|
1514
1620
|
buildInitialTasksPrompt,
|
|
1621
|
+
buildNewProjectSetupInstruction,
|
|
1622
|
+
buildNorthStarPrompt,
|
|
1515
1623
|
buildPlanFullInstructionsConditional,
|
|
1516
1624
|
buildPlanUserMessage,
|
|
1625
|
+
buildPreScanInstruction,
|
|
1517
1626
|
buildProductBriefPrompt,
|
|
1518
1627
|
buildReviewSystemPrompt,
|
|
1519
1628
|
buildReviewUserMessage,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papi-ai/server",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.60",
|
|
4
4
|
"description": "PAPI MCP server — AI-powered sprint planning, build execution, and strategy review for software projects",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"mcpName": "io.github.getpapi/papi",
|