Squash the current repository state back into one baseline commit while
preserving the README reframing and repository contents.

Constraint: User explicitly requested a single squashed commit with subject "asdf"
Confidence: high
Scope-risk: broad
Reversibility: clean
Directive: This commit intentionally rewrites published history; coordinate before future force-pushes
Tested: git status clean; local history rewritten to one commit; force-pushed main to origin and instructkr
Not-tested: Fresh clone verification after push
This commit is contained in:
did:key:z6MkqDnb7Siv3Cwj7pGJq4T5EsUisECqR8KpnDLwcaZq5TPr
2026-03-31 03:34:03 -07:00
commit d2542c9a62
1903 changed files with 513517 additions and 0 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,112 @@
import axios from 'axios'
import { readFile, stat } from 'fs/promises'
import type { Message } from '../../types/message.js'
import { checkAndRefreshOAuthTokenIfNeeded } from '../../utils/auth.js'
import { logForDebugging } from '../../utils/debug.js'
import { errorMessage } from '../../utils/errors.js'
import { getAuthHeaders, getUserAgent } from '../../utils/http.js'
import { normalizeMessagesForAPI } from '../../utils/messages.js'
import {
extractAgentIdsFromMessages,
getTranscriptPath,
loadSubagentTranscripts,
MAX_TRANSCRIPT_READ_BYTES,
} from '../../utils/sessionStorage.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import { redactSensitiveInfo } from '../Feedback.js'
type TranscriptShareResult = {
success: boolean
transcriptId?: string
}
export type TranscriptShareTrigger =
| 'bad_feedback_survey'
| 'good_feedback_survey'
| 'frustration'
| 'memory_survey'
export async function submitTranscriptShare(
messages: Message[],
trigger: TranscriptShareTrigger,
appearanceId: string,
): Promise<TranscriptShareResult> {
try {
logForDebugging('Collecting transcript for sharing', { level: 'info' })
const transcript = normalizeMessagesForAPI(messages)
// Collect subagent transcripts
const agentIds = extractAgentIdsFromMessages(messages)
const subagentTranscripts = await loadSubagentTranscripts(agentIds)
// Read raw JSONL transcript (with size guard to prevent OOM)
let rawTranscriptJsonl: string | undefined
try {
const transcriptPath = getTranscriptPath()
const { size } = await stat(transcriptPath)
if (size <= MAX_TRANSCRIPT_READ_BYTES) {
rawTranscriptJsonl = await readFile(transcriptPath, 'utf-8')
} else {
logForDebugging(
`Skipping raw transcript read: file too large (${size} bytes)`,
{ level: 'warn' },
)
}
} catch {
// File may not exist
}
const data = {
trigger,
version: MACRO.VERSION,
platform: process.platform,
transcript,
subagentTranscripts:
Object.keys(subagentTranscripts).length > 0
? subagentTranscripts
: undefined,
rawTranscriptJsonl,
}
const content = redactSensitiveInfo(jsonStringify(data))
await checkAndRefreshOAuthTokenIfNeeded()
const authResult = getAuthHeaders()
if (authResult.error) {
return { success: false }
}
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'User-Agent': getUserAgent(),
...authResult.headers,
}
const response = await axios.post(
'https://api.anthropic.com/api/claude_code_shared_session_transcripts',
{ content, appearance_id: appearanceId },
{
headers,
timeout: 30000,
},
)
if (response.status === 200 || response.status === 201) {
const result = response.data
logForDebugging('Transcript shared successfully', { level: 'info' })
return {
success: true,
transcriptId: result?.transcript_id,
}
}
return { success: false }
} catch (err) {
logForDebugging(errorMessage(err), {
level: 'error',
})
return { success: false }
}
}

View File

@@ -0,0 +1,82 @@
import { useEffect, useRef } from 'react'
import { normalizeFullWidthDigits } from '../../utils/stringUtils.js'
// Delay before accepting a digit as a response, to prevent accidental
// submissions when users start messages with numbers (e.g., numbered lists).
// Short enough to feel instant for intentional presses, long enough to
// cancel when the user types more characters.
const DEFAULT_DEBOUNCE_MS = 400
/**
* Detects when the user types a single valid digit into the prompt input,
* debounces to avoid accidental submissions (e.g., "1. First item"),
* trims the digit from the input, and fires a callback.
*
* Used by survey components that accept numeric responses typed directly
* into the main prompt input.
*/
export function useDebouncedDigitInput<T extends string = string>({
inputValue,
setInputValue,
isValidDigit,
onDigit,
enabled = true,
once = false,
debounceMs = DEFAULT_DEBOUNCE_MS,
}: {
inputValue: string
setInputValue: (value: string) => void
isValidDigit: (char: string) => char is T
onDigit: (digit: T) => void
enabled?: boolean
once?: boolean
debounceMs?: number
}): void {
const initialInputValue = useRef(inputValue)
const hasTriggeredRef = useRef(false)
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
// Latest-ref pattern so callers can pass inline callbacks without causing
// the effect to re-run (which would reset the debounce timer every render).
const callbacksRef = useRef({ setInputValue, isValidDigit, onDigit })
callbacksRef.current = { setInputValue, isValidDigit, onDigit }
useEffect(() => {
if (!enabled || (once && hasTriggeredRef.current)) {
return
}
if (debounceRef.current !== null) {
clearTimeout(debounceRef.current)
debounceRef.current = null
}
if (inputValue !== initialInputValue.current) {
const lastChar = normalizeFullWidthDigits(inputValue.slice(-1))
if (callbacksRef.current.isValidDigit(lastChar)) {
const trimmed = inputValue.slice(0, -1)
debounceRef.current = setTimeout(
(debounceRef, hasTriggeredRef, callbacksRef, trimmed, lastChar) => {
debounceRef.current = null
hasTriggeredRef.current = true
callbacksRef.current.setInputValue(trimmed)
callbacksRef.current.onDigit(lastChar)
},
debounceMs,
debounceRef,
hasTriggeredRef,
callbacksRef,
trimmed,
lastChar,
)
}
}
return () => {
if (debounceRef.current !== null) {
clearTimeout(debounceRef.current)
debounceRef.current = null
}
}
}, [inputValue, enabled, once, debounceMs])
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long