clauderipple 0.8.10 → 0.9.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/CHANGELOG.md +60 -0
- package/README.es.md +21 -15
- package/README.ja.md +13 -9
- package/README.ko.md +23 -9
- package/README.md +26 -11
- package/README.pt-BR.md +21 -15
- package/README.zh-CN.md +13 -9
- package/dist/app/package.json +1 -1
- package/dist/cli/src/codex.js +3 -1
- package/dist/cli/src/settings.js +5 -2
- package/dist/mod/.claude-plugin/plugin.json +9 -1
- package/dist/mod/hooks/cache.ts +60 -0
- package/dist/mod/hooks/compact.ts +40 -0
- package/dist/mod/hooks/handoff.ts +43 -0
- package/dist/mod/hooks/i18n.ts +2716 -0
- package/dist/mod/hooks/judge.ts +242 -0
- package/dist/mod/hooks/ladder.js +179 -0
- package/dist/mod/hooks/ladder.ts +174 -0
- package/dist/mod/hooks/modes.ts +93 -0
- package/dist/mod/hooks/register.tsx +1458 -256
- package/dist/mod/hooks/session.ts +62 -0
- package/dist/mod/hooks/settings.js +342 -0
- package/dist/mod/hooks/settings.ts +321 -0
- package/dist/mod/hooks/stats.ts +59 -0
- package/dist/mod/hooks/ui/agents.tsx +138 -0
- package/dist/mod/hooks/ui/alerts.tsx +373 -0
- package/dist/mod/hooks/ui/art.ts +628 -0
- package/dist/mod/hooks/ui/band.tsx +836 -0
- package/dist/mod/hooks/ui/kit.tsx +258 -0
- package/dist/mod/hooks/ui/log.tsx +109 -0
- package/dist/mod/hooks/ui/parts.tsx +217 -0
- package/dist/mod/hooks/ui/ripple.ts +614 -0
- package/dist/mod/hooks/ui/settings.tsx +376 -0
- package/dist/mod/hooks/ui/setup.tsx +148 -0
- package/dist/mod/hooks/ui/theme.ts +217 -0
- package/dist/mod/hooks/ui/view.ts +371 -0
- package/dist/mod/hooks/worker.ts +1 -1
- package/dist/mod/hooks/workers.ts +217 -0
- package/dist/mod/tests/register.test.tsx +1730 -0
- package/dist/mod/tests/ui.test.tsx +853 -0
- package/dist/mod/tsconfig.json +2 -1
- package/dist/mod/types/index.d.ts +320 -1
- package/dist/router/src/admin.js +170 -52
- package/dist/router/src/agents.js +24 -5
- package/dist/router/src/bootstrap.js +4 -2
- package/dist/router/src/config.js +3 -0
- package/dist/router/src/health.js +33 -0
- package/dist/router/src/index.js +1 -0
- package/dist/router/src/ingress/models.js +4 -1
- package/dist/router/src/mod-settings.js +120 -0
- package/dist/router/src/model-policy.js +6 -0
- package/dist/router/src/model-scores.js +159 -0
- package/dist/router/src/picker.js +7 -0
- package/dist/router/src/providers/chatgpt/index.js +23 -5
- package/dist/router/src/providers/chatgpt/translate.js +4 -1
- package/dist/router/src/providers/google/index.js +8 -3
- package/dist/router/src/providers/openai/index.js +6 -1
- package/dist/router/src/providers/openai/translate.js +6 -1
- package/dist/router/src/proxy.js +62 -24
- package/dist/router/src/requestlog.js +78 -20
- package/dist/router/src/version.js +1 -1
- package/dist/ui/app.js +908 -209
- package/dist/ui/favicon-32.png +0 -0
- package/dist/ui/favicon.svg +12 -0
- package/dist/ui/i18n.js +919 -233
- package/dist/ui/index.html +159 -87
- package/dist/ui/style.css +789 -410
- package/docs/ARCHITECTURE.md +141 -0
- package/package.json +7 -3
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SessionCompacted, SessionMessage } from 'claude-code'
|
|
2
|
+
|
|
3
|
+
export const COMPACT_SYSTEM = `Create a faithful continuation record of this coding conversation. Keep the user's intent and changes of mind, decisions and constraints, relevant concepts, files and code touched, errors and their fixes, and user feedback. List user requests in order, distinguish solved from pending work, and state exactly what is happening now. Include a next step only when the conversation justifies it. Preserve useful exact paths and commands, but omit secrets, redundant logs, and obsolete detail. Do not invent verification or claim unfinished work is complete. Write in the user's language.`
|
|
4
|
+
|
|
5
|
+
function clipped(text: string, limit: number): string {
|
|
6
|
+
return text.length > limit ? `${text.slice(0, limit)}\n[truncated]` : text
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function render(messages: readonly SessionMessage[], tight: boolean): string {
|
|
10
|
+
return messages.map((message, index) => {
|
|
11
|
+
const tail = index >= messages.length - 6
|
|
12
|
+
const inputLimit = tail ? (tight ? 4000 : 20000) : 400
|
|
13
|
+
const resultLimit = tail ? (tight ? 4000 : 20000) : (tight ? 300 : 2000)
|
|
14
|
+
const parts = [`${message.role}:\n${message.text}`]
|
|
15
|
+
for (const use of message.toolUses) {
|
|
16
|
+
parts.push(`tool ${use.tool} (${use.tool_use_id}):\n${clipped(JSON.stringify(use.input), inputLimit)}`)
|
|
17
|
+
const result = use.text ?? (use.result !== undefined ? JSON.stringify(use.result) : undefined)
|
|
18
|
+
if (result !== undefined) parts.push(`result${use.isError ? ' ERROR' : ''}:\n${clipped(result, resultLimit)}`)
|
|
19
|
+
}
|
|
20
|
+
if (!message.toolUses.length) {
|
|
21
|
+
for (const result of message.toolResults ?? []) parts.push(`result ${result.tool_use_id}${result.isError ? ' ERROR' : ''}:\n${clipped(result.text, resultLimit)}`)
|
|
22
|
+
}
|
|
23
|
+
return parts.join('\n\n')
|
|
24
|
+
}).join('\n\n---\n\n')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Shrink only tool bulk; losing user intent to hit a budget is worse than engine fallback. */
|
|
28
|
+
export function serializeTranscript(messages: readonly SessionMessage[], budget = 2400000): string | null {
|
|
29
|
+
if (!messages.length) return null
|
|
30
|
+
const full = render(messages, false)
|
|
31
|
+
if (full.length <= budget) return full
|
|
32
|
+
const tight = render(messages, true)
|
|
33
|
+
return tight.length <= budget ? tight : null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** No handle: the engine builds this replacement from role, text and tool blocks. */
|
|
37
|
+
export function compactResult(summary: string): SessionCompacted {
|
|
38
|
+
return { messages: [{ role: 'user', toolUses: [], text:
|
|
39
|
+
`Continuation record from the previous conversation:\n\n${summary.trim()}\n\nResume the pending work from this record. Do not repeat completed work or ask for information already recorded.` }] }
|
|
40
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SessionMessage } from 'claude-code'
|
|
2
|
+
import type { HandoffKind, Settings } from '../types'
|
|
3
|
+
|
|
4
|
+
export const QUICK_PROMPT = `Prepare a concise resume note for a fresh coding chat, in the user's language, about forty lines of bullets. Use only this conversation; do not run tools. Record project, folder and branch if known, the goal, verified completed work, unfinished work and blockers, git state as last checked, decisions and failed approaches, permissions or resume details, and the highest-priority runnable next action. Mark actions requiring the user. Keep only current relevant state and point to files or specs instead of copying them. Exclude secrets, environment values and raw logs. Output only the note, without an introduction or farewell.`
|
|
5
|
+
|
|
6
|
+
export const FULL_PROMPT = `Write a durable handoff for the next coding chat. First verify the working directory, git status, recent commits and whether changes are pushed; check open pull requests if gh is available. Rebuild project-root HANDOFF.md from the current facts, fewer than eighty lines, in the user's language. Include the goal, verified done and undone work, blockers, changed files, decisions and failed approaches, permissions, exact current state and a prioritized next action. Distinguish facts from unverified claims. Never expose secrets, environment values or raw logs. Finish with the saved file path and a concise status for the next chat.`
|
|
7
|
+
|
|
8
|
+
export function wrapForNewChat(text: string, after: Settings['handoffAfter'], kind: HandoffKind, hasSkill: boolean): string {
|
|
9
|
+
const source = kind === 'full' && hasSkill ?
|
|
10
|
+
'First read the saved handoff files named below; if no file is named, read HANDOFF.md. The last answer alone may not contain the handoff.\n\n' : ''
|
|
11
|
+
const action = after === 'confirm' ?
|
|
12
|
+
'State the current status and proposed next action in two lines, then wait for my confirmation without starting work.' :
|
|
13
|
+
'Continue with the justified next action. If it needs my permission or input, explain what is needed and wait.'
|
|
14
|
+
return `${source}Resume context from the previous chat:\n\n${text.trim()}\n\n${action}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const ADVICE_SYSTEM = `Decide whether starting a fresh chat clearly helps this coding work. Recommend it only when completed work is followed by a different task, unrelated history is dead weight, the topic changed, or repeated failed approaches are going in circles. A long chat, high context usage, or an ordinary next step is not enough. When uncertain, do not recommend it. Output only JSON {"handoff":null} or {"handoff":"reason"}, using the user's language and at most eight words.`
|
|
18
|
+
|
|
19
|
+
/** Cadence counts real eligible prompts, not tool steps, pings or judge requests. */
|
|
20
|
+
export function adviceDue(count: number, percent: number): boolean {
|
|
21
|
+
return percent >= 30 && Number.isInteger(count) && count > 0 && count % 2 === 0
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function adviceInput(input: { messages: readonly SessionMessage[]; next: string; contextPercent: number; warm: boolean }): string {
|
|
25
|
+
const users = input.messages.filter(message => message.role === 'user' && message.text.trim())
|
|
26
|
+
const assistant = input.messages.findLast(message => message.role === 'assistant' && message.text.trim())
|
|
27
|
+
const trail = users.slice(-8, -1).map(message => message.text.replace(/\s+/g, ' ').slice(0, 160)).join('\n')
|
|
28
|
+
return [`Context: ${Math.round(input.contextPercent)}%; user messages: ${users.length}; cache: ${input.warm ? 'warm' : 'cold'}`,
|
|
29
|
+
`Initial request: ${users[0]?.text.slice(0, 500) ?? ''}`, `Earlier requests:\n${trail}`,
|
|
30
|
+
`Assistant tail: ${assistant?.text.slice(-700) ?? ''}`, `Next request: ${input.next.slice(0, 600)}`].join('\n\n')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parseAdvice(text: string): string | null {
|
|
34
|
+
const span = /\{[\s\S]*\}/.exec(text)?.[0]
|
|
35
|
+
if (!span) return null
|
|
36
|
+
try {
|
|
37
|
+
const value: unknown = JSON.parse(span)
|
|
38
|
+
if (!value || typeof value !== 'object' || !('handoff' in value)) return null
|
|
39
|
+
return typeof value.handoff === 'string' && value.handoff.trim() ? value.handoff.trim().slice(0, 70) : null
|
|
40
|
+
} catch {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
}
|