whipped 0.1.2 → 0.2.1

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/README.md CHANGED
@@ -194,7 +194,7 @@ Whipped can start a [Cloudflare Tunnel](https://developers.cloudflare.com/cloudf
194
194
 
195
195
  A standalone Chrome/Chromium extension for capturing UI context off any web page. Click the toolbar icon to enter select mode, then click any element to capture its React component and source location. It builds a ready-to-paste **YAML prompt** for your AI coding agent — no server connection required.
196
196
 
197
- Available at [github.com/nxnom/whipped-extension](https://github.com/nxnom/whipped-extension).
197
+ Install from the [Chrome Web Store](https://chromewebstore.google.com/detail/codkafoociihebdklkpfjjoacenkkhci?utm_source=item-share-cb), or build from source at [github.com/nxnom/whipped-extension](https://github.com/nxnom/whipped-extension).
198
198
 
199
199
  ### Visual comments
200
200
 
package/dist/cli.js CHANGED
@@ -20290,10 +20290,10 @@ init_workspace_state();
20290
20290
  // src/daemon/review-pipeline.ts
20291
20291
  import { spawnSync as spawnSync5 } from "node:child_process";
20292
20292
  import { existsSync as existsSync9, readFileSync as readFileSync6 } from "node:fs";
20293
- import { readFile as readFile2, readdir, stat, unlink } from "node:fs/promises";
20293
+ import { readdir, readFile as readFile2, stat, unlink } from "node:fs/promises";
20294
20294
  import { join as join15 } from "node:path";
20295
- init_api_contract();
20296
20295
  init_runtime_config();
20296
+ init_api_contract();
20297
20297
  init_logger();
20298
20298
  init_task_id();
20299
20299
 
@@ -21192,12 +21192,12 @@ When you finish your work:
21192
21192
 
21193
21193
  ${commitInstruction.replace(/^1\. /, "3. ")}
21194
21194
 
21195
- 4. Reconcile memory. Review this task's description, the user's comments, and any decision or correction that came up while you worked. If something **changed, contradicted, reversed, or superseded** an entry in the Memory list above, call \`whipped_update_memory\` on that entry's id so future tasks don't act on stale knowledge (e.g. the user says "stop using short ids, use full kebab-case" \u2192 update the id-convention memory, don't leave the old one). If a durable new fact came up with no matching memory, call \`whipped_save_memory\`. If nothing memory-worthy changed, skip this step. Do NOT create a second memory that conflicts with an existing one \u2014 update the existing one instead.`);
21195
+ 4. Reconcile memory. Review this task's description, the user's comments, and any decision or correction that came up while you worked. If something **changed, contradicted, reversed, or superseded** an entry in the Memory list above, call \`whipped_update_memory\` on that entry's id so future tasks don't act on stale knowledge (e.g. the user says "stop using short ids, use full kebab-case" \u2192 update the id-convention memory, don't leave the old one). Most tasks need NO new memory \u2014 skip this step by default. Only \`whipped_save_memory\` if a cross-cutting rule or non-obvious trap came up that you'd have wanted *before* starting and that isn't already recoverable by reading the code, schema, or a controller \u2014 endpoint shapes, column lists, field names, and per-page layout are code, not memory. Do NOT create a second memory that conflicts with an existing one \u2014 update the existing one instead.`);
21196
21196
  parts.push(`## Memory
21197
21197
 
21198
21198
  This project has its own persistent memory. The \`whipped_save_memory\` / \`whipped_update_memory\` MCP tools ARE this project's memory \u2014 do NOT use your own notes, scratch files, CLAUDE.md, or any other memory system for durable facts.
21199
21199
 
21200
- When you are asked to "remember", "save to memory", "note for next time" \u2014 or you discover/decide a durable fact (a convention, architecture decision, gotcha, or a correction the user made) \u2014 record it in memory.
21200
+ When you are asked to "remember", "save to memory", "note for next time" \u2014 or you hit a cross-cutting convention, an architecture decision, a non-obvious repo-wide gotcha, or a correction the user made \u2014 record it in memory. Do NOT record what is already in the code or schema (endpoint request/response shapes, query params, column lists, field names, colour classes, per-page layout): if your note would cite the file where the truth lives, the file is the memory \u2014 skip it. Keep each entry to one focused fact in 1-3 sentences.
21201
21201
 
21202
21202
  Before recording, check the memory list injected above (each entry shows its \`[id]\`) and \`whipped_search_memory\`. If what you're recording **contradicts, reverses, supersedes, corrects, or is a near-duplicate of** an existing memory, call \`whipped_update_memory\` with that memory's id and overwrite it \u2014 do NOT create a second, conflicting entry. Only \`whipped_save_memory\` when there is genuinely no existing memory about the same thing. Use \`whipped_get_memory\` to read one in full.
21203
21203
 
@@ -22531,10 +22531,11 @@ ${devSystemPromptResult.text}`;
22531
22531
  const conflictGitInstructions = conflictProjectConfig.gitInstructions?.trim() || DEFAULT_GIT_INSTRUCTIONS;
22532
22532
  let outputBuffer = "";
22533
22533
  let hookHandled = false;
22534
+ const conflictSystemPrompt = buildConflictResolutionSystemPrompt(card, conflictedFiles, conflictGitInstructions);
22534
22535
  if (defaultAgent === "opencode") {
22535
22536
  const mcpSpec = buildWhippedMcpServerSpec(getMcpServerPath(), this.options.serverUrl, workspaceId);
22536
22537
  await writeOpencodeFiles(streamId, getServerPort(this.options.serverUrl), mcpSpec, {
22537
- appendSystemPrompt: CONFLICT_RESOLUTION_SYSTEM_PROMPT
22538
+ appendSystemPrompt: conflictSystemPrompt
22538
22539
  }).catch(() => {
22539
22540
  });
22540
22541
  } else if (defaultAgent === "cursor") {
@@ -22544,7 +22545,7 @@ ${devSystemPromptResult.text}`;
22544
22545
  }
22545
22546
  const proc = spawnAgent({
22546
22547
  agentId: defaultAgent,
22547
- prompt: buildConflictResolutionPrompt(card, conflictedFiles, conflictGitInstructions),
22548
+ prompt: buildTaskPrompt(),
22548
22549
  cwd: mergeWorktreePath,
22549
22550
  hookSettingsPath: defaultAgent === "claude" ? CLAUDE_TASK_SETTINGS_PATH : void 0,
22550
22551
  hookServerPort: defaultAgent === "codex" ? getServerPort(this.options.serverUrl) : void 0,
@@ -22553,7 +22554,7 @@ ${devSystemPromptResult.text}`;
22553
22554
  ...defaultAgent === "opencode" ? { [OPENCODE_CONFIG_DIR_ENV]: getOpencodeConfigDir(streamId) } : {},
22554
22555
  ...defaultAgent === "cursor" ? { [CURSOR_CONFIG_DIR_ENV]: getCursorConfigDir(streamId) } : {}
22555
22556
  },
22556
- appendSystemPrompt: defaultAgent !== "opencode" ? CONFLICT_RESOLUTION_SYSTEM_PROMPT : void 0,
22557
+ appendSystemPrompt: defaultAgent !== "opencode" ? conflictSystemPrompt : void 0,
22557
22558
  onOutput: (data) => {
22558
22559
  outputBuffer += data;
22559
22560
  stateHub.broadcastTerminalOutput(workspaceId, streamId, data);
@@ -22715,27 +22716,27 @@ ${secretsSection}` : ""}${systemPrompt?.trim() ? `
22715
22716
 
22716
22717
  ${systemPrompt.trim()}` : ""}`;
22717
22718
  }
22718
- var CONFLICT_RESOLUTION_SYSTEM_PROMPT = `You are a merge conflict resolution agent. Your only job is to resolve git merge conflicts.
22719
+ function buildConflictResolutionSystemPrompt(card, conflictedFiles, gitInstructions) {
22720
+ return `You are a merge conflict resolution agent. Your only job is to resolve git merge conflicts.
22719
22721
 
22720
22722
  Rules:
22721
22723
  - Only edit files to remove conflict markers (<<<<<<< ======= >>>>>>>)
22722
22724
  - Preserve the intent of BOTH sides where possible; when in doubt keep the incoming (task) changes
22723
22725
  - Never refactor, rename, or change logic beyond resolving the conflict markers
22724
- - Exit when done`;
22725
- function buildConflictResolutionPrompt(card, conflictedFiles, gitInstructions) {
22726
- return `Resolve the git merge conflicts in this repository.
22726
+ - Exit when done
22727
+
22728
+ ## Task being merged
22727
22729
 
22728
- Task being merged:
22729
22730
  ${card.description?.trim() ?? ""}
22730
22731
 
22731
- Conflicted files:
22732
+ ## Conflicted files
22733
+
22732
22734
  ${conflictedFiles.map((f2) => `- ${f2}`).join("\n")}
22733
22735
 
22734
- Resolve each conflict, preserving the task's intent. Then stage and commit
22735
- the resolution. Write the commit message following the project's git
22736
- conventions below \u2014 do not use a hard-coded template.
22736
+ Resolve each conflict, preserving the task's intent. Then stage and commit the resolution. Write the commit message following the project's git conventions below \u2014 do not use a hard-coded template.
22737
22737
 
22738
22738
  ## Git conventions
22739
+
22739
22740
  ${gitInstructions}`;
22740
22741
  }
22741
22742
  function buildTaskPrompt() {
@@ -28564,7 +28565,7 @@ process.on("uncaughtException", (err) => {
28564
28565
  if (err.code === "EPIPE" || err.code === "ECONNRESET") return;
28565
28566
  throw err;
28566
28567
  });
28567
- var VERSION9 = true ? "0.1.2" : "0.0.0-dev";
28568
+ var VERSION9 = true ? "0.2.1" : "0.0.0-dev";
28568
28569
  async function isPortAvailable(port, host) {
28569
28570
  return new Promise((resolve5) => {
28570
28571
  const probe = createServer2();
@@ -2370,12 +2370,12 @@ if (agentSlot === "dev" || agentSlot === "assistant") {
2370
2370
  registerTool(
2371
2371
  "whipped_save_memory",
2372
2372
  {
2373
- description: "Save a durable memory so future agents don't re-discover it. Use for conventions, architecture facts, decisions, gotchas, or user corrections worth remembering across tasks. Scope 'project' = specific to this repo; 'global' = a fact shareable across projects (e.g. a framework/library convention). Keep each memory to one focused fact. The user may review project task-lessons; global lessons go to a review queue.\n\nGlobal memory REQUIRES tags \u2014 it only reaches another project that subscribes to one of its tags. Tag with the framework-qualified form when the knowledge is ecosystem-specific, and the bare form only when the fact is truly tool-level and framework-agnostic: Spoosh used via its React bindings \u2192 'spoosh-react'; a fact about Spoosh itself \u2192 'spoosh'; React hooks \u2192 'react-hook' (not bare 'hook'). Reuse an existing tag from the injected memory's tag list before inventing a near-duplicate.",
2373
+ description: "Save a durable memory: a cross-cutting rule or a non-obvious trap that a careful reader of the code would still get wrong. Non-derivability is the bar \u2014 NOT how long it took to find. If the fact lives in one file, the API schema, or a controller (endpoint request/response shapes, query params, column lists, field names, CSS/colour classes, per-page layout), do NOT save it \u2014 that is code, read it when you need it. The test: if your note has to cite the file where the truth lives, the file IS the memory; skip it. Save for conventions, architecture decisions, repo-wide gotchas, or user corrections. Most tasks produce nothing memory-worthy \u2014 that is the expected outcome, not a gap. Keep each memory to ONE focused fact in 1-3 sentences (~60 words max); a multi-paragraph dump is a sign it belongs in the code, not here. Scope 'project' = specific to this repo; 'global' = a fact shareable across projects (e.g. a framework/library convention). The user may review project task-lessons; global lessons go to a review queue.\n\nGlobal memory REQUIRES tags \u2014 it only reaches another project that subscribes to one of its tags. Tag with the framework-qualified form when the knowledge is ecosystem-specific, and the bare form only when the fact is truly tool-level and framework-agnostic: Spoosh used via its React bindings \u2192 'spoosh-react'; a fact about Spoosh itself \u2192 'spoosh'; React hooks \u2192 'react-hook' (not bare 'hook'). Reuse an existing tag from the injected memory's tag list before inventing a near-duplicate.",
2374
2374
  inputSchema: {
2375
2375
  scope: z2.enum(["project", "global"]).describe("'project' (this repo) or 'global' (shareable across projects)"),
2376
2376
  type: z2.enum(["fact", "convention", "decision", "preference", "rule", "lesson", "sharp_edge"]).describe("Kind of memory"),
2377
2377
  title: z2.string().describe("Short one-line summary"),
2378
- content: z2.string().describe("The durable fact, in 1-3 sentences"),
2378
+ content: z2.string().describe("The durable fact, in 1-3 sentences (~60 words). One fact only \u2014 not a page or endpoint spec."),
2379
2379
  sourceType: z2.enum(["user_correction", "explicit_save", "task_lesson"]).default("task_lesson").describe("Why this is being saved \u2014 'user_correction' if the user explicitly told you, else 'task_lesson'"),
2380
2380
  importance: z2.number().int().min(1).max(3).optional().describe("1 normal, 2 high, 3 critical"),
2381
2381
  tags: z2.array(z2.string()).optional().describe(
@@ -27186,6 +27186,7 @@ const EFFORT_OPTIONS = [
27186
27186
  ];
27187
27187
  const MODEL_OPTIONS = {
27188
27188
  claude: [
27189
+ { value: "claude-fable-5", label: "Fable 5" },
27189
27190
  { value: "claude-opus-4-8", label: "Opus 4.8" },
27190
27191
  { value: "claude-opus-4-7", label: "Opus 4.7" },
27191
27192
  { value: "claude-opus-4-6", label: "Opus 4.6" },
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/png" href="/favicon.png" />
7
7
  <title>whipped</title>
8
- <script type="module" crossorigin src="/assets/index-BOR6PC8f.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-Ce4HDOh7.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-Dgu7Q26n.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whipped",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Autonomous AI agent kanban board for Claude, Codex, Opencode, and Cursor.",
5
5
  "type": "module",
6
6
  "files": [