SLOPSHOPPER

status-band

入力欄の上に文脈の使用率・5時間/週間の利用枠とリセット時刻(JST)・Cloudクレジットの失効までを1行で出す

band
A shopper browsing a rack in a slop shop
README

obsidian-ai-knowledge-lake

Obsidian の保管庫を「AI と共有する自分のナレッジレイク」にして、作業の多くを AI(Claude Code / Codex)に任せられるようにするための一式。 作者が自分の保管庫で実際に運用している仕組みから、個人の中身を抜いて汎用化したもの。

中身

フォルダ何か最初に読むもの
knowledge-lake-kit/保管庫の器。 フォルダ構成、AI への指示書(AGENTS.md)、各フォルダの README、テンプレート、運用の考え方START_HERE.md(人間)/ SETUP_FOR_AI.md(AI)
agent-tooling/AI に持たせる道具。 スキル(/inbox・/prioritize など)、MCP(Gmail 横断・Google Tasks)、Claude Code の mod、フック、CLI、定期実行README.md(人間)/ FOR_AI.md(AI)

使い方

git clone https://github.com/KaiMiyazawa/obsidian-ai-knowledge-lake.git
  1. 保管庫を作る: knowledge-lake-kit/START_HERE.md を上から順に進める。最後は AI がインタビューしながら保管庫を組み立てる。
  2. 道具を入れる(任意): 保管庫ができて慣れてきたら、保管庫で AI に次のように頼む。 > <クローンした場所>/agent-tooling/FOR_AI.md を読んで、この道具を私の保管庫に入れるのを手伝って

どちらも「AI が読んで、利用者に確認しながら進める」前提で書いてある。全部を一度に入れなくてよい。

注意

  • 秘密の値(トークン・パスワード・Webhook など)は入っていない。作者の個人情報は伏せ字(you@gmail.com、<MAIN_LIST_ID> など)にしてある。
  • 自分の保管庫は private リポジトリで管理する。 このリポジトリを fork して中身を書き足し、公開したままにしない。
  • 道具は作者の環境(macOS、Claude Code・Codex の特定の版)で作ったもの。そのまま動く保証はなく、利用者の環境に合わせて AI と直しながら使う。

ライセンス

MIT。ただし agent-tooling/outside-vault/gtasks-mcp/ は alvinjchoi/gtasks-mcp(MIT)を改造したもので、同フォルダの LICENSE に従う。

Source 2 files
hooks/register.tsx 87 lines
1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register, SessionContextUsage, SessionRateLimit } from 'claude-code'
3
4const usage = atom({ plugin: 'status-band', key: 'usage' } as const, null)
5
6// AGENTS.md「Claude Code Cloud Session への委譲」。期限を過ぎたら表示しない(この定数ごと消してよい)。
7const CLOUD_CREDIT_EXPIRES = Date.parse('2026-11-05T16:59:00+09:00')
8
9const JST_MS = 9 * 3600 * 1000
10const LABELS: Record<string, string> = { five_hour: '5時間', seven_day: '週間' }
11
12const jst = (iso: string, withDate: boolean) => {
13  const t = new Date(Date.parse(iso) + JST_MS).toISOString()
14  const hm = t.slice(11, 16)
15  return withDate ? `${t.slice(5, 7)}/${t.slice(8, 10)} ${hm}` : hm
16}
17
18const levelColor = (p: number) => (p >= 90 ? 'red' : p >= 70 ? 'yellow' : 'green')
19
20const save = async ($: EngineInterface, context: SessionContextUsage, limits: readonly SessionRateLimit[]) => {
21  const model = await $.session.model()
22  await update($, usage, () => ({
23    model,
24    contextPercent: context.percent,
25    limits: limits.map(l => ({ kind: l.kind, percentUsed: l.percentUsed, resetsAt: l.resetsAt })),
26  }))
27}
28
29export const register: Register = on => {
30  on('session.start', async ($, e, next) => {
31    const ran = await next(e)
32    const now = await $.session.usage()
33    await save($, now.context, now.rateLimits)
34
35    return ran
36  })
37
38  on('session.measure', async ($, e, next) => {
39    await save($, e.context, e.rateLimits)
40
41    return next(e)
42  })
43
44  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
45    const u = await read($, usage)
46    if (e.props.hasSurvey || u === null) return next(e)
47
48    const { Box, Text } = $.ui.resolve(e)
49    const now = Date.now()
50    const ctx = u.contextPercent
51    const filled = ctx === undefined ? 0 : Math.round(ctx / 10)
52    const creditDays = Math.ceil((CLOUD_CREDIT_EXPIRES - now) / 86400000)
53
54    return (
55      <Box columnGap={1} flexWrap="wrap">
56        <Text dimColor>[{u.model}]</Text>
57        <Text>
58          文脈 {e.props.bodyColumns >= 65 && `[${'#'.repeat(filled)}${'-'.repeat(10 - filled)}] `}
59        </Text>
60        {ctx === undefined ? (
61          <Text dimColor>未取得</Text>
62        ) : (
63          <Text color={levelColor(ctx)}>{ctx}%</Text>
64        )}
65        {u.limits
66          .filter(l => LABELS[l.kind] !== undefined)
67          .map(l => {
68            const isPast = l.resetsAt !== undefined && Date.parse(l.resetsAt) <= now
69            const reset =
70              l.resetsAt === undefined ? '' : isPast ? '(更新待ち)' : `→${jst(l.resetsAt, l.kind === 'seven_day')}`
71            return (
72              <Text key={l.kind}>
73                | {LABELS[l.kind]} <Text color={levelColor(l.percentUsed)}>{Math.round(l.percentUsed)}%</Text>
74                {reset}
75              </Text>
76            )
77          })}
78        {creditDays > 0 && (
79          <Text color={creditDays <= 7 ? 'yellow' : undefined} dimColor={creditDays > 7}>
80            | Cloudクレジット失効まで{creditDays}日
81          </Text>
82        )}
83      </Box>
84    )
85  })
86}
87
types/index.d.ts 14 lines
1export type Usage = {
2  model: string
3  contextPercent?: number
4  limits: { kind: string; percentUsed: number; resetsAt?: string }[]
5}
6
7declare module 'claude-code' {
8  interface PluginState {
9    'status-band': {
10      usage: Usage | null
11    }
12  }
13}
14