refactor: 拆分 cockpit 為可個別安裝的 statusbar 與 companion #12
@@ -6,9 +6,14 @@
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "cockpit",
|
||||
"source": "./plugins/cockpit",
|
||||
"description": "Cockpit 外掛,內含 hud 模組(輸入框上方單行 HUD):模型、目錄、分支、5h/7d 額度與重置、context、CPU/記憶體/磁碟"
|
||||
"name": "statusbar",
|
||||
"source": "./plugins/statusbar",
|
||||
"description": "輸入框上方單行狀態列:模型、目錄、分支、5h/7d 額度與重置、context、CPU/記憶體/磁碟"
|
||||
},
|
||||
{
|
||||
"name": "companion",
|
||||
"source": "./plugins/companion",
|
||||
"description": "小說角色陪伴:人設注入、語氣、心情與好感、互動記憶、主動關心與待辦,並可用 ingest skill 解析小說建立角色"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,31 +1,44 @@
|
||||
# jsc-marketplace
|
||||
|
||||
JSC 自用 Claude Code mods。
|
||||
JSC 自用 Claude Code mods。每個 mod 皆可單獨安裝、更新與移除。
|
||||
|
||||
| Mod | 說明 |
|
||||
| --- | --- |
|
||||
| [cockpit](plugins/cockpit) | Cockpit 外掛,內含 hud 模組(輸入框上方單行 HUD):模型、目錄、分支、5h/7d 額度與重置、context、CPU/記憶體/磁碟 |
|
||||
| [statusbar](plugins/statusbar) | 輸入框上方單行狀態列:模型、目錄、分支、5h/7d 額度與重置、context、CPU/記憶體/磁碟 |
|
||||
| [companion](plugins/companion) | 小說角色陪伴:人設注入、語氣、心情與好感、互動記憶、主動關心與待辦,並可用 ingest skill 解析小說建立角色 |
|
||||
|
||||
## 安裝
|
||||
|
||||
本機資料夾:
|
||||
|
||||
```bash
|
||||
claude plugin marketplace add /root/jsc-marketplace
|
||||
claude plugin install cockpit@jsc-marketplace
|
||||
```
|
||||
|
||||
或透過 Gitea 遠端:
|
||||
先加入 marketplace,再依需要安裝個別 mod:
|
||||
|
||||
```bash
|
||||
claude plugin marketplace add https://gitea.jsc.idv.tw/jiantw83/jsc-marketplace.git
|
||||
claude plugin install cockpit@jsc-marketplace
|
||||
claude plugin install statusbar@jsc-marketplace
|
||||
claude plugin install companion@jsc-marketplace
|
||||
```
|
||||
|
||||
## 指令
|
||||
|
||||
- `/cockpit hud on | off | compact`:切換狀態列
|
||||
- `/hud on | off | compact`:上述指令的別名
|
||||
- `/statusbar on | off | compact`:切換狀態列
|
||||
- `/companion status | switch <角色> | off | setup`:角色陪伴控制
|
||||
|
||||
## 更新
|
||||
|
||||
```bash
|
||||
claude plugin marketplace update jsc-marketplace
|
||||
claude plugin update statusbar@jsc-marketplace
|
||||
claude plugin update companion@jsc-marketplace
|
||||
```
|
||||
|
||||
更新後需重新啟動 Claude Code 才會生效。
|
||||
|
||||
## 移除
|
||||
|
||||
```bash
|
||||
claude plugin uninstall statusbar@jsc-marketplace
|
||||
claude plugin uninstall companion@jsc-marketplace
|
||||
claude plugin marketplace remove jsc-marketplace
|
||||
```
|
||||
|
||||
## 授權
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "cockpit",
|
||||
"version": "0.1.0",
|
||||
"description": "Cockpit 外掛,內含 hud 模組(輸入框上方單行 HUD):模型、目錄、分支、5h/7d 額度與重置、context、CPU/記憶體/磁碟",
|
||||
"license": "MIT",
|
||||
"types": "./types/index.d.ts",
|
||||
"author": {
|
||||
"name": "JSC"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import type { Register } from 'claude-code'
|
||||
|
||||
// companion 模組:目前為空殼,之後的階段再補內容
|
||||
export const register: Register = () => {}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { Register } from 'claude-code'
|
||||
|
||||
import { register as companion } from './companion'
|
||||
import { register as hud } from './hud'
|
||||
|
||||
export const register: Register = on => {
|
||||
hud(on)
|
||||
companion(on)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "companion",
|
||||
"version": "0.1.0",
|
||||
"description": "小說角色陪伴:人設注入、語氣、心情與好感、互動記憶、主動關心與待辦,並可用 ingest skill 解析小說建立角色",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "JSC"
|
||||
}
|
||||
}
|
||||
+1
-28
@@ -1,19 +1,8 @@
|
||||
import { describe, expect, test } from 'claude-code/testing'
|
||||
|
||||
import { bandLines, fit, lineWidth } from '../hud/format'
|
||||
import type { HudData } from '../hud/format'
|
||||
import { fitCompanion } from './band'
|
||||
import type { View } from './core'
|
||||
|
||||
const DATA: HudData = {
|
||||
model: 'claude-opus-5-5[1m]',
|
||||
cwd: '/home/dev/work/luopan',
|
||||
home: '/home/dev',
|
||||
git: { branch: 'issue-3', dirty: 1, ahead: 0, behind: 0 },
|
||||
usage: { fiveHour: { percent: 42 }, sevenDay: { percent: 71 }, ctxPercent: 31 },
|
||||
sys: { cpu: 23 },
|
||||
tzOffsetMin: 480,
|
||||
}
|
||||
import { lineWidth } from './seg'
|
||||
|
||||
const VIEW: View = {
|
||||
core: { id: 'miko', name: '月見ミコ', facts: {} as never, neuroticism: 50, baseline: { p: 0, a: 0, d: 0 } },
|
||||
@@ -56,19 +45,3 @@ describe('角色行', () => {
|
||||
expect(text({ ...VIEW, todos: 0 }, 200)).not.toContain('☐')
|
||||
})
|
||||
})
|
||||
|
||||
describe('band', () => {
|
||||
test('沒有角色時只有第一行,且與現況逐字相同', () => {
|
||||
for (const cols of [200, 100, 60]) {
|
||||
const lines = bandLines(DATA, cols, false, null)
|
||||
expect(lines.length).toBe(1)
|
||||
expect(lines[0]).toEqual(fit(DATA, cols, false))
|
||||
}
|
||||
})
|
||||
|
||||
test('有角色時第一行不變,第二行是角色行', () => {
|
||||
const lines = bandLines(DATA, 120, false, fitCompanion(VIEW, 120))
|
||||
expect(lines.length).toBe(2)
|
||||
expect(lines[0]).toEqual(fit(DATA, 120, false))
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import { bar, lineWidth } from '../hud/format'
|
||||
import type { Seg } from '../hud/format'
|
||||
import { bar, lineWidth } from './seg'
|
||||
import type { Seg } from './seg'
|
||||
import type { View } from './core'
|
||||
|
||||
const axis = (v: number) => bar(((v + 1) / 2) * 100, 3)
|
||||
+1
-1
@@ -140,7 +140,7 @@ describe('關心:靜音與不打斷', () => {
|
||||
assert.deepEqual((await s.care.tick()).map(c => c.trigger), ['session_start'])
|
||||
})
|
||||
|
||||
test('/cockpit quiet 在期間內靜音,到期後恢復', async () => {
|
||||
test('/companion quiet 在期間內靜音,到期後恢復', async () => {
|
||||
const s = setup()
|
||||
await s.care.quiet(30)
|
||||
await s.care.onUserTurn()
|
||||
@@ -291,7 +291,7 @@ export interface CareEngine {
|
||||
onToolResult(ok: boolean): Promise<void>
|
||||
/** 定時/事件檢查:回傳要立即顯示的 toast 或氣泡 */
|
||||
tick(signals?: Signals): Promise<Care[]>
|
||||
/** /cockpit quiet [分鐘],0 取消靜音 */
|
||||
/** /companion quiet [分鐘],0 取消靜音 */
|
||||
quiet(minutes: number): Promise<void>
|
||||
state(): Promise<CareState>
|
||||
}
|
||||
+3
-3
@@ -23,7 +23,7 @@ const MIKO: CompanionCore = {
|
||||
}
|
||||
const H = 3_600_000
|
||||
const DAY = 24 * H
|
||||
const ROOT = '/h/.cockpit'
|
||||
const ROOT = '/h/.companion'
|
||||
|
||||
function setup(builtins = [MIKO]) {
|
||||
const fs = memoryFs()
|
||||
@@ -39,10 +39,10 @@ describe('啟用', () => {
|
||||
expect((await c.ensureActivated()).first).toBe(false)
|
||||
})
|
||||
|
||||
test('companion off 後沒有角色,且重新啟動不會自動再啟用', async () => {
|
||||
test('off 後沒有角色,且重新啟動不會自動再啟用', async () => {
|
||||
const { c } = setup()
|
||||
await c.ensureActivated()
|
||||
await c.command('companion off')
|
||||
await c.command('off')
|
||||
expect(await c.view()).toBe(null)
|
||||
await c.ensureActivated()
|
||||
expect(await c.view()).toBe(null)
|
||||
@@ -138,7 +138,7 @@ export function createCompanion({ fs, clock, root, builtins = [FUBUKI] }: Compan
|
||||
else if (k === 'quietEnd') u.quiet.end = val
|
||||
else (u as Record<string, unknown>)[k] = val
|
||||
}
|
||||
if (bad.length) return `無法解析:${bad.join(' ')}\n用法:/cockpit setup nickname=… wake=HH:MM sleep=HH:MM tz=+0800 careLimit=N quietStart=HH:MM quietEnd=HH:MM`
|
||||
if (bad.length) return `無法解析:${bad.join(' ')}\n用法:/companion setup nickname=… wake=HH:MM sleep=HH:MM tz=+0800 careLimit=N quietStart=HH:MM quietEnd=HH:MM`
|
||||
if (args.length) await writeJson(fs, userPath, u)
|
||||
return [
|
||||
args.length ? '設定已儲存:' : '目前設定(以 key=value 修改):',
|
||||
@@ -151,14 +151,14 @@ export function createCompanion({ fs, clock, root, builtins = [FUBUKI] }: Compan
|
||||
async function command(raw: string): Promise<string> {
|
||||
const [sub = '', ...rest] = raw.trim().split(/\s+/).filter(Boolean)
|
||||
if (sub === 'status') return status()
|
||||
if (sub === 'switch') return rest[0] ? switchTo(rest[0]) : `用法:/cockpit switch <角色>。可用:${(await available()).join('、')}`
|
||||
if (sub === 'companion' && rest[0] === 'off') {
|
||||
if (sub === 'switch') return rest[0] ? switchTo(rest[0]) : `用法:/companion switch <角色>。可用:${(await available()).join('、')}`
|
||||
if (sub === 'off') {
|
||||
const g = await getGlobal()
|
||||
await writeJson(fs, globalPath, { ...g, active: null, activatedOnce: true })
|
||||
return '助理已關閉,所有資料保留。'
|
||||
}
|
||||
if (sub === 'setup') return setup(rest)
|
||||
return '用法:/cockpit status | switch <角色> | companion off | setup [key=value…]'
|
||||
return '用法:/companion status | switch <角色> | off | setup [key=value…]'
|
||||
}
|
||||
|
||||
return { ensureActivated, view, moodEvent, affinityEvent, command, getUser }
|
||||
+2
-2
@@ -24,7 +24,7 @@ type State = { mood: Pad; moodAt: number; affinity: aff.Affinity }
|
||||
export type Options = {
|
||||
fs: Fs
|
||||
clock: Clock
|
||||
/** 資料根目錄,例如 ~/cockpit */
|
||||
/** 資料根目錄,例如 ~/.companion */
|
||||
root: string
|
||||
/** 當前角色資料夾名稱 */
|
||||
id: string
|
||||
@@ -160,7 +160,7 @@ export function createCompanion({ fs, clock, root, id }: Options) {
|
||||
await fs.write(paths.user, JSON.stringify({ ...(await loadUser()), tone: on }))
|
||||
},
|
||||
|
||||
/** `/cockpit tone on|off` */
|
||||
/** `/companion tone on|off` */
|
||||
async command(line: string): Promise<string | null> {
|
||||
const m = /^tone\s+(on|off)$/.exec(line.trim())
|
||||
if (!m) return null
|
||||
@@ -0,0 +1,17 @@
|
||||
// 角色行用的字串片段與寬度計算(與 statusbar 外掛各自獨立,不互相依賴)
|
||||
export type Seg = { text: string; color?: string; dim?: boolean; bold?: boolean }
|
||||
|
||||
export function bar(p: number, cells = 5): string {
|
||||
const n = Math.max(0, Math.min(cells, Math.round((p / 100) * cells)))
|
||||
return '▰'.repeat(n) + '▱'.repeat(cells - n)
|
||||
}
|
||||
|
||||
const WIDE = /[ᄀ-ᅟ⺀-가-힣豈-︰-﹏-⦆¢-₩]/
|
||||
|
||||
export function width(s: string): number {
|
||||
let w = 0
|
||||
for (const ch of s) w += WIDE.test(ch) ? 2 : 1
|
||||
return w
|
||||
}
|
||||
|
||||
export const lineWidth = (segs: Seg[]) => segs.reduce((w, s) => w + width(s.text), 0)
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { Register } from 'claude-code'
|
||||
|
||||
import { fitCompanion } from './companion/band'
|
||||
import { createCompanion } from './companion/core'
|
||||
import { nodeFs } from './companion/node-fs'
|
||||
|
||||
let companion: ReturnType<typeof createCompanion> | undefined
|
||||
|
||||
export const register: Register = on => {
|
||||
on('session.start', async ($, e, next) => {
|
||||
await $.command.register({
|
||||
name: 'companion',
|
||||
description: '角色陪伴控制:狀態、切換角色、關閉、使用者設定',
|
||||
argumentHint: 'status | switch <角色> | off | setup',
|
||||
})
|
||||
const home = await $.env.get('HOME')
|
||||
companion = createCompanion({ fs: nodeFs, clock: { now: Date.now }, root: `${home ?? ''}/.companion` })
|
||||
const first = await companion.ensureActivated()
|
||||
if (first.first) $.ui.toast(`已啟用 ${first.name}。用 /companion off 可關閉`)
|
||||
|
||||
return next(e)
|
||||
})
|
||||
|
||||
on('command.run', { command: 'companion' }, async ($, e) => ({
|
||||
text: companion ? await companion.command(e.args) : '尚未就緒。',
|
||||
}))
|
||||
|
||||
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
|
||||
const rest = await next(e)
|
||||
const view = await companion?.view()
|
||||
if (e.props.hasSurvey || !view) return rest
|
||||
|
||||
const line = fitCompanion(view, e.props.bodyColumns)
|
||||
const { Box, Text } = $.ui.resolve(e)
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{rest}
|
||||
<Text wrap="truncate">
|
||||
{line.map(s => (
|
||||
<Text color={s.color} dimColor={s.dim} bold={s.bold}>
|
||||
{s.text}
|
||||
</Text>
|
||||
))}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: ingest
|
||||
description: 解析一章輕小說(txt 或 md),抽出世界觀與人物,逐一詢問合併、新建或略過,並寫入 cockpit 角色資料。使用者提供小說章節檔或要求建立/更新角色設定時使用。
|
||||
description: 解析一章輕小說(txt 或 md),抽出世界觀與人物,逐一詢問合併、新建或略過,並寫入 companion 角色資料。使用者提供小說章節檔或要求建立/更新角色設定時使用。
|
||||
---
|
||||
|
||||
# 小說章節解析
|
||||
@@ -23,5 +23,5 @@ description: 解析一章輕小說(txt 或 md),抽出世界觀與人物,
|
||||
## 注意
|
||||
|
||||
- 模型從對白推得的個性可信度偏低,只當先驗。
|
||||
- 不要在 repo 內保存受版權保護的長篇設定或台詞;資料寫在使用者家目錄的 cockpit 資料夾。
|
||||
- 不要在 repo 內保存受版權保護的長篇設定或台詞;資料寫在使用者家目錄的 `~/.companion` 資料夾。
|
||||
- 範例完整流程見 `docs/ingest-walkthrough.md`。
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "statusbar",
|
||||
"version": "0.1.0",
|
||||
"description": "輸入框上方的單行狀態列:模型、目錄、分支、5h/7d 額度與重置、context、CPU/記憶體/磁碟",
|
||||
"license": "MIT",
|
||||
"types": "./types/index.d.ts",
|
||||
"author": {
|
||||
"name": "JSC"
|
||||
}
|
||||
}
|
||||
@@ -232,9 +232,3 @@ export function parseDf(out: string): number | undefined {
|
||||
const m = row && /(\d+)%/.exec(row)
|
||||
return m ? Number(m[1]) : undefined
|
||||
}
|
||||
|
||||
// band 的所有行:第一行恆為 HUD;有角色時才多第二行角色行
|
||||
export function bandLines(d: HudData, columns: number, compact: boolean, companion: Seg[] | null): Seg[][] {
|
||||
const first = fit(d, columns, compact)
|
||||
return companion ? [first, companion] : [first]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ "modules": ["./register.tsx"] }
|
||||
@@ -1,19 +1,16 @@
|
||||
import { atom, read, update } from 'claude-code'
|
||||
import type { EngineInterface, Register, SessionContextUsage, SessionRateLimit } from 'claude-code'
|
||||
|
||||
import type { Mode, Sys, Usage } from '../../types'
|
||||
import { fitCompanion } from '../companion/band'
|
||||
import { createCompanion } from '../companion/core'
|
||||
import { nodeFs } from '../companion/node-fs'
|
||||
import { bandLines, cpuPercent, parseCpu, parseDf, parseGit, parseMem, parseOffset } from './format'
|
||||
import type { Mode, Sys, Usage } from '../types'
|
||||
import { fit, cpuPercent, parseCpu, parseDf, parseGit, parseMem, parseOffset } from './format'
|
||||
import type { CpuSample } from './format'
|
||||
|
||||
const usageAtom = atom({ plugin: 'cockpit', key: 'usage' } as const, {})
|
||||
const gitAtom = atom({ plugin: 'cockpit', key: 'git' } as const, null)
|
||||
const sysAtom = atom({ plugin: 'cockpit', key: 'sys' } as const, {})
|
||||
const modeAtom = atom({ plugin: 'cockpit', key: 'mode' } as const, 'auto')
|
||||
const alertedAtom = atom({ plugin: 'cockpit', key: 'alerted' } as const, [])
|
||||
const effortAtom = atom({ plugin: 'cockpit', key: 'effort' } as const, null)
|
||||
const usageAtom = atom({ plugin: 'statusbar', key: 'usage' } as const, {})
|
||||
const gitAtom = atom({ plugin: 'statusbar', key: 'git' } as const, null)
|
||||
const sysAtom = atom({ plugin: 'statusbar', key: 'sys' } as const, {})
|
||||
const modeAtom = atom({ plugin: 'statusbar', key: 'mode' } as const, 'auto')
|
||||
const alertedAtom = atom({ plugin: 'statusbar', key: 'alerted' } as const, [])
|
||||
const effortAtom = atom({ plugin: 'statusbar', key: 'effort' } as const, null)
|
||||
|
||||
const SYS_MS = 3000
|
||||
const GIT_MS = 15_000
|
||||
@@ -37,7 +34,6 @@ function toUsage(context: SessionContextUsage, rateLimits: SessionRateLimit[]):
|
||||
}
|
||||
|
||||
let home: string | undefined
|
||||
let companion: ReturnType<typeof createCompanion> | undefined
|
||||
let tzOffsetMin = 0
|
||||
let prevCpu: CpuSample | null = null
|
||||
let ticks = 0
|
||||
@@ -98,20 +94,11 @@ async function runHud($: EngineInterface, args: string) {
|
||||
export const register: Register = on => {
|
||||
on('session.start', async ($, e, next) => {
|
||||
await $.command.register({
|
||||
name: 'cockpit',
|
||||
description: 'Cockpit 控制:助理(status、switch、companion off、setup)與 hud 狀態列',
|
||||
argumentHint: 'status | switch <角色> | companion off | setup | hud on | off | compact',
|
||||
})
|
||||
// 別名:/hud <arg> 等同 /cockpit hud <arg>
|
||||
await $.command.register({
|
||||
name: 'hud',
|
||||
description: '切換輸入框上方的狀態列(/cockpit hud 的別名)',
|
||||
name: 'statusbar',
|
||||
description: '切換輸入框上方的狀態列',
|
||||
argumentHint: 'on | off | compact',
|
||||
})
|
||||
home = await $.env.get('HOME')
|
||||
companion = createCompanion({ fs: nodeFs, clock: { now: Date.now }, root: `${home ?? ''}/.cockpit` })
|
||||
const first = await companion.ensureActivated()
|
||||
if (first.first) $.ui.toast(`已啟用 ${first.name}。用 /cockpit companion off 可關閉`)
|
||||
try {
|
||||
const r = await $.process.run(['date', '+%z'], { timeoutMs: 3000 })
|
||||
tzOffsetMin = parseOffset(r.stdout)
|
||||
@@ -150,18 +137,12 @@ export const register: Register = on => {
|
||||
return ran
|
||||
})
|
||||
|
||||
on('command.run', { command: 'hud' }, ($, e) => runHud($, e.args))
|
||||
|
||||
on('command.run', { command: 'cockpit' }, async ($, e, next) => {
|
||||
const [sub, ...rest] = e.args.trim().split(/\s+/)
|
||||
if (sub === 'hud') return runHud($, rest.join(' '))
|
||||
|
||||
return { text: companion ? await companion.command(e.args) : '尚未就緒。' }
|
||||
})
|
||||
on('command.run', { command: 'statusbar' }, ($, e) => runHud($, e.args))
|
||||
|
||||
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
|
||||
const mode = await read($, modeAtom)
|
||||
if (e.props.hasSurvey || mode === 'off') return next(e)
|
||||
const rest = await next(e)
|
||||
if (e.props.hasSurvey || mode === 'off') return rest
|
||||
|
||||
const data = {
|
||||
model: await $.session.model(),
|
||||
@@ -173,23 +154,20 @@ export const register: Register = on => {
|
||||
tzOffsetMin,
|
||||
effort: await read($, effortAtom),
|
||||
}
|
||||
const view = await companion?.view()
|
||||
const cols = e.props.bodyColumns
|
||||
const lines = bandLines(data, cols, mode === 'compact', view ? fitCompanion(view, cols, mode === 'compact') : null)
|
||||
const line = fit(data, e.props.bodyColumns, mode === 'compact')
|
||||
const { Box, Text } = $.ui.resolve(e)
|
||||
|
||||
// 引擎在 band 與輸入框之間已留一行空白,上方補一行讓上下間距相同
|
||||
return (
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
{lines.map(line => (
|
||||
<Text wrap="truncate">
|
||||
{line.map(s => (
|
||||
<Text color={s.color} dimColor={s.dim} bold={s.bold}>
|
||||
{s.text}
|
||||
</Text>
|
||||
))}
|
||||
</Text>
|
||||
))}
|
||||
<Text wrap="truncate">
|
||||
{line.map(s => (
|
||||
<Text color={s.color} dimColor={s.dim} bold={s.bold}>
|
||||
{s.text}
|
||||
</Text>
|
||||
))}
|
||||
</Text>
|
||||
{rest}
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./.claude-plugin/types/tsconfig.json"
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export type Mode = 'auto' | 'compact' | 'off'
|
||||
|
||||
declare module 'claude-code' {
|
||||
interface PluginState {
|
||||
cockpit: {
|
||||
statusbar: {
|
||||
usage: Usage
|
||||
git: Git | null
|
||||
sys: Sys
|
||||
Reference in New Issue
Block a user