Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ARCHITECTURE.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@
"threshold": "only `runpodctl` was listed, so `runpod config`/`runpod project deploy`/`runpod pod create` and serverless `curl api.runpod.ai/v2/<id>/run` all leaked — same whitelist-too-narrow bypass as VAST_VERBS",
"source": "modules/cloud-guard.ts"
},
{
"id": "SEGMENT_LOCAL_SANCTION",
"state": "pos-conv",
"value": "the hexa-builtin sanction in detectRawCloudCli is per-SEGMENT, not whole-command — one `hexa cloud …` token must not exempt a raw-provider tail",
"threshold": "a top-level `if (isHexaBuiltin(cmd)) return null` exempted the ENTIRE string, so `hexa cloud pods; vastai create instance` leaked (the sanctioned head whitelisted the chained raw CLI) — same whitelist-too-broad failure mode as VAST_VERBS/BOTH_RUNPOD_CLIS; fix judges each segment on its own head",
"source": "modules/cloud-guard.ts"
},
{
"id": "RENT_HUMAN_CONSENT_GATE",
"state": "pos-conv",
"value": "a GPU-pod rent (`hexa cloud rent|up`) is a real-money action gated on a fresh HUMAN consent token (`!sidecar sign rent`) minted only via the TUI bang — the agent's own `sidecar sign` mint and any write into ~/.sidecar/signs/ are code-blocked (SIGN-SELF-MINT/SIGN-FORGE); one-shot, consumed on admission, TTL/one-shot are code constants (no config opt-out). Gate rests on 'the agent cannot emit a `!` bang', not on the text parser being airtight",
"threshold": "renting a pod costs money but nothing forced a human in the loop — the agent could `hexa cloud rent` unilaterally; ports the retired hexa `_sign_guard.hexa` consent gate (old `! sidecar sign commons` pattern). Indirect rents (bash -c/eval/ssh/pool wrapper) are blocked 'run it directly'; structural closure (hexa itself requiring the token) is a documented phase-2",
"source": "modules/sign.ts"
},
{
"id": "COMPANION_SURFACE_NOT_INJECTED",
"state": "pos-conv",
Expand Down
3 changes: 1 addition & 2 deletions CHANGELOG.jsonl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{"ts":"2026-07-24","title":"feat(sign): GPU-pod-rent consent gate — !sidecar sign rent (human-only one-shot token)","body":"New sidecar sign command + GPU-pod-rent consent gate: renting a pod (hexa cloud rent/up) is a real-money action, so pre bash now BLOCKS it (CLOUD-RENT-UNSIGNED) until the HUMAN mints a one-shot consent token via the TUI bang '!sidecar sign rent' — ported from the retired hexa _sign_guard.hexa (old '! sidecar sign commons' pattern). modules/sign.ts: host-global plain-epoch token store ~/.sidecar/signs/<key>.sign, TTL rent=600s, one-shot (consumed atomically on admission via rename-then-unlink; pre-consume stops parallel double-spend); CLI verbs list/mint/check/clear; helpers signFresh/consumeSign/detectSelfMint/signForgeBashTarget/isSignsPath. cloud-guard.ts: detectCloudRent (direct head-match gated; indirect bash-c/eval/ssh/pool wrapper blocked); fixed pre-existing segment-local weakness — isHexaBuiltin exempted the WHOLE command string so 'hexa cloud pods; raw-create' leaked; now sanctioned per-SEGMENT. pre.ts: SIGN-SELF-MINT + SIGN-FORGE(bash) in preBash, rent gate LAST (never burns the one-shot token), SIGN-FORGE(write) in preWrite. TTL/one-shot are CODE constants (no config opt-out). No new hook wiring: both surfaces already route bash and write. Docs: commands/sign.md, templates/sign.md. Skipped as over-eng (sol): repo-scope hash, JSON/nonce token, MCP pre-tool deny — phase-2. Smoke: unsigned/self-mint/forge block; mint->rent allow+consume->re-block; echo/grep allow; indirect block; seg-local block; tsc+lint+ci green."}
{"ts":"2026-07-19","title":"commons: add no-bare-jargon slug","body":"User-facing prose must gloss every jargon/acronym/hyphenated/backtick term at first use (plain-word swap or term(=plain meaning)); elevates the easy-style advisory into an always-on governance hard rule. Also trimmed the lab ARCHITECTURE node under the 300-char cell cap."}
{"ts":"2026-07-18","title":"fix(lab): guard child.stdin null (TS18047) — origin/main tsc RED 복구","body":"child.stdin 은 spawn 타입상 Writable|null 인데 널가드 없이 .write/.end 호출 → strict tsc TS18047(2곳: fable·sol 백엔드). origin/main 이 이 상태로 머지돼 sidecar 전체 ci(npx tsc --noEmit)가 RED. optional chaining(child.stdin?.write/?.end)으로 가드 — stdin 이 null 이면 spawn 실패라 error 핸들러가 이미 settle(127) 처리하므로 안전. tsc exit 0 복구."}
{"ts":"2026-07-18","title":"feat(easy): 0순위 맨-은어 금지 + 경중 2단(인라인풀이/7요소) + bare-jargon-gloss lint 축","body":"LEAN(매턴 주입) 재설계: \"7요소를 사소하지 않은 개념마다\"는 진행보고·요약처럼 은어 섞인 서술에는 안 걸려 통째로 스킵됐다(실패사례 H_9774 CEMENTING류 은어 범벅). 이제 0순위=맨-은어 금지(no bare jargon): 프로젝트 은어·약어·전문용어가 산문에 처음 나오면 그 자리에서 `용어(=쉬운 뜻)`로 풀이. 경중 2단 도입 — Tier1 인라인 풀이(기본·필수·대부분의 산문) vs Tier2 풀 7요소(새 개념·도구 자체가 답의 주인공일 때만). 기존 \"제외\" 목록은 표기-제외로 재정의(풀이 의무는 남음)해 회피 구실 봉쇄. modules/easy.ts header() lean 리드+활성 배너도 7요소 최상위→0순위 풀이 우선으로 lockstep 수정(LEAN과 지시충돌 제거). FULL 레퍼런스에 before→after 리라이트 gold(실패사례 박제)+인라인풀이 미니 gold+체크리스트 1번=bare-jargon 스캔 추가. easy lint 6번째 축 bare-jargon-gloss 추가(하이픈결합·백틱 은어 후보 → 첫 등장 근처 풀이 없으면 경고 · =다음 한글=풀이/=다음 은어=수식 구분 · 쉬운말(용어) 앞쪽형+term(=뜻) 뒤쪽형 양방향 · common allowlist 오탐억제 · advisory exit0). ko(주입대상)+en(canonical base) 동기화. lean byte ko 957→1519·en→1454. tsc 0 · lint BAD 4/4 flag·GOOD 3형태 통과 검증."}
Expand Down Expand Up @@ -27,5 +28,3 @@
{"ts":"2026-07-08","title":"feat(worktree+ing): ING↔stranded task linkage (#7) + worktree stop-check last-Stop nudge (#5)","body":"stranded-work 가시성 마무리 2건(Fable 랭킹 #5·#7). (#7 ING↔stranded 링크) ing Item 에 branch? 필드 추가 + ing add/next 가 현재 branch(BOARD_ROOT 기준·≠main/master/HEAD)를 스탬프 → stranded 워크트리/브랜치를 그 태스크로 역링크. 신규 export findIngForBranch(branch,items): Tier1=branch 정확 스탬프 매치(최신), Tier2=무스탬프 구엔트리용 보수적 fallback(전체 branch명 리터럴이 text 에 포함 AND 이름이 distinctive(len≥8||\"/\") AND 유일매치일 때만), 모호/무매치→null(잘못된 매치가 no-match보다 해로우므로 FP-averse). worktree inject + sidecar end 6/6b 의 각 stranded 라인에 ` — 🔄 ING#<id>: <text>` 인라인(readItems 1콜·stranded 있을 때만·clean=0B). (#5 Stop-time 넛지) 신규 `sidecar worktree stop-check`(warn-only·never block): prompt-scan(다음 프롬프트)·worktree inject(다음 세션)·shipStopCheck(미커밋만)이 모두 못 보는 유일 창=세션 마지막 Stop 에 worktree 에 committed-but-unpushed 남기고 터미널 닫는 경우를 warm-context 에서 상기. stop_hook_active anti-wedge + 0-linked fast-exit(classify per-wt git콜 회피) + git-common-dir keyed dedup(branch@tip: 같은 tip 침묵·새 커밋 재경고·해소된 키 prune) 로 긴 세션서 매 Stop 잔소리 방지. 배선 setup.ts Stop[] + hooks/hooks.json Stop 양쪽(setup-ts-1) + install-hooks --global. Stop 훅은 CC 전용이라 CC만 배선(warn-only → wire-to-prod 위반 아님). help/toolkit/ARCHITECTURE(surface-stop·detail-worktree·module-ing) lockstep. QA: findIngForBranch 5케이스(Tier1/Tier2/short-blocked/no-match/empty) PASS · ing add worktree 내 branch 스탬프 확인 · worktree inject 가 `🔄 ING#N` 링크 표시 · stop-check WARN→dedup 침묵→anti-wedge 침묵→new tip 재경고 전부 출력검증 · ci/help/architecture lint green."}
{"ts":"2026-07-08","title":"feat(load+end): active stranded-work gauges — wt N(M⚠)🔴 in load line (TTL-cached) + end classify() upgrade + stranded-branch check","body":"stranded-work 가시성 후속 2건(Fable 랭킹 #4·#6). (#4) load 라인 wt 게이지가 stranded 워크트리를 능동 표시: `wt N(M⚠)` + M>0면 무조건 🔴(개수 3/10 임계 무시) — 매 턴 항상 보이는 표면이라 방치작업이 즉시 눈에 띔. classify()는 워크트리당 ~4 git콜이라 매 UserPromptSubmit sync 핫패스엔 과함 → readOpenPRs 와 동일한 git-common-dir TTL캐시(60s) sync 헬퍼 readStrandedWt(포slain 재사용·stranded=비-main 워크트리 dirty|unpushed 정의 동일), cold 턴만 git콜 지불. (#6) `sidecar end` 세션종료 점검을 classify() 로 업그레이드: 방치(⚠ dirty/unpushed)와 정리대기(○ clean merged)를 구분(기존 raw count는 구분 불가), stranded 워크트리는 warn++ 하고 항목별 resume/폐기 명령 출력 + 신규 6b 항목으로 워크트리없는 stranded 로컬브랜치(strandedBranches 재사용 — 소유자의 'stale 브랜치' 케이스)도 flag. 둘 다 기존 워크트리 감지(classify/strandedWorktrees/strandedBranches) 재사용, 발명 0. help 무변경(내부 게이지·기존 verb). ARCHITECTURE detail-3(wt 게이지 stranded 🔴) lockstep. QA: end 가 라이브 stranded 브랜치(ing) flag·load 가 임시 stranded 워크트리에 `wt 1(1⚠)🔴`·clean시 기존 표시 유지·ci green."}
{"ts":"2026-07-08","title":"feat(worktree+prompt-scan): surface stranded worktrees/branches at session-start & first-action (stderr→emitInject bugfix + worktree inject verb)","body":"세션이 stale 브랜치/워크트리에 방치되는 걸 세션 시작·첫 행동 시점에 잡는다. Fable investigate-mode 조사로 근본원인 확정: 신호는 이미 계산되는데 stderr로 나가 에이전트 context에 안 닿음(배선 결함, detection 결함 아님). (A) prompt-scan.ts 버그픽스: strandedWorktrees() 블록을 stderr→emitInject(stdout additionalContext, Claude Code 프로토콜)로 라우팅 — 매 UserPromptSubmit(첫 행동+지속) 노출, clean이면 0바이트(self-limiting). 헤더 주석은 \"additionalContext로 주입\"이라 주장했는데 실제 stderr였던 문서-코드 불일치를 정정. keyword/hint는 per-turn bloat 회피 위해 stderr 유지. (B) 신규 `sidecar worktree inject` verb(SessionStart+PreCompact+PostCompact WARN·gc 직후): strandedWorktrees()+신규 strandedBranches()(워크트리없는 stranded 로컬브랜치 — classify() 사각지대, 라이브 `ing` 브랜치가 증거)+refs/reaped tip 카운트를 이전세션 이월 방치작업으로 항목별 resume/보존/폐기 명령과 함께 surface, clean이면 0바이트. config worktree.branchAllow 로 의도적 장수브랜치 예외. WARN(SessionStart는 block 불가·FP-averse·persistence가 teeth). 배선: setup.ts SessionStart/Pre/PostCompact[] + hooks/hooks.json SessionStart 양쪽(setup-ts-1) + install-hooks --global + grep 검증. classify()는 isAgent뿐 아니라 모든 linked worktree 열거라 WORKTREE_GONE_ONLY_PILEUP 의 isAgent-only 사각지대 미반복(단 cross-repo는 여전히 repo-local·별개과제). help/toolkit/ARCHITECTURE(detail-worktree·surface-precompact) lockstep. QA: prompt-scan stranded-wt→stdout inject·worktree inject 라이브 ing 브랜치+임시 stranded wt 둘 다 emit·clean=0바이트·ci/help/architecture lint green."}
{"ts":"2026-07-08","title":"git-context: per-turn stale warning","body":"git-context per-turn stale warning closes READ-STALE gap (silent on clean, loud every turn while behind; both SSOTs + help lockstep)."}
{"ts":"2026-07-20","title":"commons: add cloudflare-global-key slug","body":"Cloudflare API 인증은 계정 Global API Key(secret get cloudflare.global_api_key + 계정 이메일)를 자격증명으로 쓰도록 commons SSOT에 새 슬러그 추가. 스코프 제한 토큰을 글로벌키 자리에 쓰거나 키를 인라인/로그로 노출하는 것은 금지(git-safety·secret CLI 참조)."}
1 change: 1 addition & 0 deletions TOOLKIT.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
{"id":"pool","kind":"hook","usage":"pool {list|add|rm|on|status|specs}","use":"host roster + remote exec + cores/mem/GPU probe (~/.sidecar/pool.json, global)","triggers":["호스트 로스터","host roster","원격 실행","원격에서 실행","어느 머신","어느 호스트","남는 머신","비는 머신","유휴 머신","idle host","원격 머신","다른 컴퓨터에서","pool list","pool status","머신 목록","호스트 목록","서버 목록","cpu 여유","gpu 여유","어디서 돌릴","ssh 로 돌"]}
{"id":"mem-guard","kind":"hook","usage":"mem-guard {status|check|install|uninstall}","use":"OOM prevention — free-RAM preflight before bg-spawn + opt-in launchd notify watchdog"}
{"id":"secret","kind":"hook","usage":"secret <verb> [args]","use":"passthrough to the secret CLI (Keychain creds · get/set/rotate/list/init/backup/sync)","triggers":["api key","API 키","api 키","키가 필요","키 필요","credential","자격증명","access token","비밀키","인증 정보","토큰 저장","secret get","secret set","키 저장"],"detail":"⚠ `get` exposes the value in context — prefer inline `$(secret get <k>)` for tool args"}
{"id":"sign","kind":"hook","usage":"sign [<key>|list|check <key>|clear [<key>]]","use":"user-consent gate — a GPU-pod rent (`hexa cloud rent|up`) is BLOCKED until the HUMAN mints a one-shot token via the TUI bang `!sidecar sign rent`","detail":"agent self-mint/forge denied · TTL 10min · consumed on use · list/check/clear agent-safe (minting is human-only — the `!` bang bypasses PreToolUse)"}
{"id":"lsp","kind":"hook","usage":"lsp {wire|status|rebuild <file>}","use":"editor LSP wiring (.lsp.json; hexa-lang `hexa lsp` for .hexa by default)","detail":"+ background rebuild of prebuilt hexa LSP binaries when their grammar source is edited"}
{"id":"research","kind":"hook","usage":"research {arxiv <query|id> [--n N] [--sort relevance|date|updated] | yt <url|id> [lang] | web <query> [--n N] | fetch <url>}","use":"arXiv / YouTube transcript / keyless web search (DuckDuckGo) / page fetch (no key)","triggers":["arxiv","arXiv","논문 찾","논문 검색","선행연구","관련 연구","related work","survey 논문","서베이","SOTA","최신 연구","preprint","literature review","유튜브 자막","youtube transcript","영상 자막","transcript 추출"]}
{"id":"watch","kind":"hook","usage":"watch <url|path> [question] [flags]","use":"download (yt-dlp) → frames (ffmpeg) + transcript (captions/Whisper) for the agent","triggers":["영상 분석","비디오 분석","동영상 분석","영상 요약","프레임 추출","영상에서 추출","유튜브 영상 보","video frames"]}
Expand Down
5 changes: 5 additions & 0 deletions cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { runVerdict } from "../modules/verdict.ts";
import { runAtlas } from "../modules/atlas.ts";
import { runEnd } from "../modules/end.ts";
import { runSecret } from "../modules/secret.ts";
import { runSign } from "../modules/sign.ts";
import { runLsp } from "../modules/lsp.ts";
import { runWorktree } from "../modules/worktree.ts";
import { runImagine } from "../modules/imagine.ts";
Expand Down Expand Up @@ -143,6 +144,8 @@ hook delegates (wire these into your agent's settings.json):
mem-guard {status|check|install|uninstall} OOM prevention — free-RAM preflight before bg-spawn + opt-in launchd notify watchdog
secret <verb> [args] passthrough to the secret CLI (Keychain creds · get/set/rotate/list/init/backup/sync)
⚠ \`get\` exposes the value in context — prefer inline \`\$(secret get <k>)\` for tool args
sign [<key>|list|check <key>|clear [<key>]] user-consent gate — a GPU-pod rent (\`hexa cloud rent|up\`) is BLOCKED until the HUMAN mints a one-shot token via the TUI bang \`!sidecar sign rent\`
agent self-mint/forge denied · TTL 10min · consumed on use · list/check/clear agent-safe (minting is human-only — the \`!\` bang bypasses PreToolUse)
lsp {wire|status|rebuild <file>} editor LSP wiring (.lsp.json; hexa-lang \`hexa lsp\` for .hexa by default)
+ background rebuild of prebuilt hexa LSP binaries when their grammar source is edited
research {arxiv <query|id> [--n N] [--sort relevance|date|updated] | yt <url|id> [lang] | web <query> [--n N] | fetch <url>} arXiv / YouTube transcript / keyless web search (DuckDuckGo) / page fetch (no key)
Expand Down Expand Up @@ -324,6 +327,8 @@ async function main(): Promise<number> {
return runEnd(rest);
case "secret":
return runSecret(rest);
case "sign":
return runSign(rest);
case "lsp":
return runLsp(rest);
case "worktree":
Expand Down
12 changes: 12 additions & 0 deletions commands/sign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
description: /sign [<key>|list|check <key>|clear [<key>]] — user-consent gate. A GPU-pod rent (`hexa cloud rent|up`) is BLOCKED until the HUMAN mints a one-shot token via the TUI bang `!sidecar sign rent`; list/check/clear are agent-safe, minting is human-only. Triggers — "sign", "렌트 승인", "consent token", "/sign".
argument-hint: "[<key>|list|check <key>|clear [<key>]]"
allowed-tools: Bash
---

⚠️ Minting a token is HUMAN-ONLY. To authorize a GPU-pod rent, type the bang directly in the
prompt: `!sidecar sign rent` — the `!` bang bypasses the tool guard. Running `/sign rent`
(or `sidecar sign rent` as a tool call) is DENIED by design (SIGN-SELF-MINT). This delegator is
for the agent-safe verbs: `list` · `check <key>` · `clear [<key>]`.

!`command -v sidecar >/dev/null 2>&1 && sidecar sign $ARGUMENTS || echo "sidecar CLI not found — install dancinlab/sidecar (~/.sidecar/cli + ~/.local/bin/sidecar on PATH)"`
Loading
Loading