SLOPSHOPPER

task-harness

The harness for a substantial task that does not fit one context window: execution always in the main context, split into sessions that are cut by /clear or…

newprompt
★ 1v3.2.0MITupdated 2026-10-07DieGopherLT/dotclaudefiles/plugins/task-harness
A shopper browsing a rack in a slop shop
README

dotclaudefiles

My personal Claude Code plugins repository. Contains multiple plugins for different workflows.

Available Plugins

PluginDescription
dotclaudefilesCommands, agents, skills, and configs I use daily

Install

# Add marketplace (once)
/plugin marketplace add DieGopherLT/dotclaudefiles

# Install plugin
/plugin install dotclaudefiles@diegopher

Setup Dotfiles (Optional)

⚠️ Warning: This will replace your local files with the repo version (backups are created automatically).

# Bash
./scripts/stow-claude.sh

# Fish
./scripts/stow-claude.fish

Status Line (Optional)

Want a status line? Use dotfiles/claude/.config/ccstatusline/settings.json with:

ccstatusline (Node.js) sirmalloc/ccstatusline - Feature-rich status line with extensive customization.

or

cc-status-line (Go) Install DieGopherLT/cc-status-line - Lightweight Go implementation with better performance.

They both look almost identical, Go version made by me includes a visual context indicator.

Update

/plugin marketplace update diegopher
/plugin install dotclaudefiles@diegopher

Uninstall

/plugin uninstall dotclaudefiles@diegopher

Diego (@DieGopherLT)

Source 2 files
hooks/register.ts 61 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register, SessionContextUsage } from 'claude-code'
3
4const lastCompactAt = atom({ plugin: 'task-harness', key: 'lastCompactAt' } as const, 0)
5
6// Absolute on purpose: the window is capped to auto-compact near 500k, so
7// past 400k the next session has too little room left to chain into.
8const BUDGET_TOKENS = 400_000
9
10export const register: Register = on => {
11  // Precompute installs nothing and subagents compact their own transcripts:
12  // only a compaction of the main conversation that stood counts.
13  on('session.compact', async ($, e, next) => {
14    const compacted = await next(e)
15    const isMainCompaction = e.agentId === undefined && e.trigger !== 'precompute' && compacted.skip === undefined
16
17    // A failure here must not undo or repeat a compaction that already stood.
18    if (isMainCompaction) {
19      await stampCompaction($).catch(error => $.ui.log(`task-harness: compaction not recorded: ${error}`))
20    }
21
22    return compacted
23  })
24
25  // A plugin skill may arrive namespaced (task-harness:log-binnacle).
26  on('skill.prompt', async ($, e, next) => {
27    if (e.skill !== 'log-binnacle' && !e.skill.endsWith(':log-binnacle')) {
28      return next(e)
29    }
30
31    const prompt = await next(e)
32
33    return { text: `${prompt.text}\n\n${await describeContextBudget($)}` }
34  })
35}
36
37async function stampCompaction($: EngineInterface): Promise<void> {
38  const now = await $.clock.now()
39  await update($, lastCompactAt, () => now)
40}
41
42// ponytail: lastCompactAt lives in $.state, which dies with the process while
43// startedAt survives a --resume; a compaction before a resume reads as false.
44async function describeContextBudget($: EngineInterface): Promise<string> {
45  const { context, startedAt } = await $.session.usage()
46  const hasCompacted = (await read($, lastCompactAt)) > startedAt
47  const isOverBudget = context.tokens === undefined || context.tokens > BUDGET_TOKENS
48
49  return [
50    '## Context budget (measured by task-harness mod)',
51    `- context_tokens: ${context.tokens ?? 'unknown'}`,
52    `- context_window: ${describeWindow(context)}`,
53    `- compacted_this_session: ${hasCompacted}`,
54    `- budget_verdict: ${isOverBudget || hasCompacted ? 'cut' : 'chain'}`,
55  ].join('\n')
56}
57
58function describeWindow(context: SessionContextUsage): string {
59  return context.percent === undefined ? `${context.window}` : `${context.window} (${context.percent}%)`
60}
61
types/index.d.ts 9 lines
1// Milliseconds on $.clock.now(), compared against $.session.usage().startedAt.
2export type CompactStamp = number
3
4declare module 'claude-code' {
5  interface PluginState {
6    'task-harness': { lastCompactAt: CompactStamp }
7  }
8}
9