@g3nomaly/g3n-mods 0.1.7 → 0.1.8
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/.claude-plugin/marketplace.json +7 -1
- package/README.md +12 -5
- package/mods/agent-adventure/.claude-plugin/plugin.json +23 -0
- package/mods/agent-adventure/hooks/content.ts +263 -0
- package/mods/agent-adventure/hooks/engine.ts +1125 -0
- package/mods/agent-adventure/hooks/hooks.json +1 -0
- package/mods/agent-adventure/hooks/register.tsx +465 -0
- package/mods/agent-adventure/hooks/sprite.ts +424 -0
- package/mods/agent-adventure/types/index.d.ts +248 -0
- package/mods/agent-adventure/view/panes.json +8 -0
- package/mods/agent-adventure/view/view.css +230 -0
- package/mods/agent-adventure/view/view.ts +308 -0
- package/mods/agents-panel/.claude-plugin/plugin.json +9 -1
- package/mods/agents-panel/hooks/register.tsx +18 -4
- package/mods/agents-panel/view/panes.json +8 -0
- package/mods/context-bar/.claude-plugin/plugin.json +9 -1
- package/mods/context-bar/hooks/register.tsx +4 -3
- package/mods/context-bar/view/panes.json +8 -0
- package/mods/custom-crab-agents/hooks/register.tsx +15 -1
- package/mods/delete-guard/.claude-plugin/plugin.json +9 -1
- package/mods/delete-guard/hooks/register.tsx +15 -5
- package/mods/delete-guard/view/panes.json +8 -0
- package/mods/focus-mode/.claude-plugin/plugin.json +6 -0
- package/mods/focus-mode/hooks/register.tsx +7 -4
- package/mods/focus-mode/view/panes.json +8 -0
- package/mods/mini-game/.claude-plugin/plugin.json +9 -1
- package/mods/mini-game/hooks/register.tsx +21 -7
- package/mods/mini-game/view/panes.json +8 -0
- package/mods/tool-timeline/.claude-plugin/plugin.json +9 -1
- package/mods/tool-timeline/hooks/register.tsx +18 -4
- package/mods/tool-timeline/view/panes.json +8 -0
- package/mods/you-should-know/.claude-plugin/plugin.json +3 -3
- package/mods/you-should-know/hooks/register.tsx +173 -45
- package/mods/you-should-know/types/index.d.ts +13 -4
- package/package.json +2 -1
|
@@ -12,6 +12,20 @@ const PANEL = 'agents-panel'
|
|
|
12
12
|
/** Key prefix of the agents panel's own elements, and the one this section carries so the panel shows it. */
|
|
13
13
|
const PANEL_KEY = 'agents:'
|
|
14
14
|
const ADDON_KEY = 'addon:'
|
|
15
|
+
|
|
16
|
+
/** Prop the mods viewer adds to every pane it draws; the Claude Code extension draws on the `vscode` surface too, without it. */
|
|
17
|
+
const VIEWER_PROP = 'g3nViewer'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Tells whether the mods viewer asked for this drawing, rather than the Claude Code extension on the same surface.
|
|
21
|
+
*
|
|
22
|
+
* @param props - The `Pane` props of the `ui.render` argument.
|
|
23
|
+
* @returns `true` when the viewer's prop is set.
|
|
24
|
+
*/
|
|
25
|
+
function isViewer(props: object): boolean {
|
|
26
|
+
return (props as Readonly<Record<string, unknown>>)[VIEWER_PROP] === true
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
const crabs = atom({ plugin: 'custom-crab-agents', key: 'crabs' } as const, [])
|
|
16
30
|
const planned = atom({ plugin: 'custom-crab-agents', key: 'planned' } as const, [])
|
|
17
31
|
const contextWindow = atom({ plugin: 'custom-crab-agents', key: 'window' } as const, 1_000_000)
|
|
@@ -592,7 +606,7 @@ export const register: Register = on => {
|
|
|
592
606
|
const now = await $.clock.now()
|
|
593
607
|
|
|
594
608
|
// The viewer draws the crabs with the mod's own view: hand it the cards as data, inside the key the panel looks for.
|
|
595
|
-
if (e.surface === 'vscode') {
|
|
609
|
+
if (e.surface === 'vscode' && isViewer(e.props)) {
|
|
596
610
|
const { Input } = $.ui.resolve(e)
|
|
597
611
|
const data: CrabsViewData = {
|
|
598
612
|
crabs: list.map(crab => {
|
|
@@ -5,5 +5,13 @@
|
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "g3ntrovert"
|
|
7
7
|
},
|
|
8
|
-
"types": "./types/index.d.ts"
|
|
8
|
+
"types": "./types/index.d.ts",
|
|
9
|
+
"userConfig": {
|
|
10
|
+
"showInClaudeCode": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"title": "Show in Claude Code",
|
|
13
|
+
"default": false,
|
|
14
|
+
"description": "Open this mod's pane in the Claude Code chat when a session starts. The mods viewer shows it either way."
|
|
15
|
+
}
|
|
16
|
+
}
|
|
9
17
|
}
|
|
@@ -424,6 +424,9 @@ const held: Held[] = []
|
|
|
424
424
|
/** The card lists every file instead of the first `SHOWN_FILES`. */
|
|
425
425
|
let isExpanded = false
|
|
426
426
|
|
|
427
|
+
/** The switch stays in the Claude Code chat; else only a waiting delete's card shows there. Set from the plugin options. */
|
|
428
|
+
let isShownInChat = false
|
|
429
|
+
|
|
427
430
|
/**
|
|
428
431
|
* Turns the guard on or off, for this session and every later one.
|
|
429
432
|
*
|
|
@@ -436,12 +439,16 @@ async function setGuard($: EngineInterface, isGuarding: boolean): Promise<void>
|
|
|
436
439
|
}
|
|
437
440
|
|
|
438
441
|
/**
|
|
439
|
-
* Shows the switch again once no delete waits: the pane's title goes back from the card's
|
|
442
|
+
* Shows the switch again once no delete waits: the pane's title goes back from the card's, or the pane leaves the
|
|
443
|
+
* Claude Code chat when the switch is not shown there.
|
|
440
444
|
*
|
|
441
445
|
* @param $ - Engine handle the hook received.
|
|
442
446
|
*/
|
|
443
447
|
async function showSwitch($: EngineInterface): Promise<void> {
|
|
444
|
-
if (held.length === 0)
|
|
448
|
+
if (held.length === 0) {
|
|
449
|
+
if (isShownInChat) await $.ui.open({ id: PANE, title: TITLE }).catch(() => undefined)
|
|
450
|
+
else await $.ui.close({ id: PANE }).catch(() => undefined)
|
|
451
|
+
}
|
|
445
452
|
$.ui.invalidate('ui.render')
|
|
446
453
|
}
|
|
447
454
|
|
|
@@ -483,7 +490,8 @@ async function hold($: EngineInterface, card: Omit<Held, 'settle'>, signal: Abor
|
|
|
483
490
|
const onAbort = (): void => drop('cancel')
|
|
484
491
|
signal?.addEventListener('abort', onAbort, { once: true })
|
|
485
492
|
|
|
486
|
-
//
|
|
493
|
+
// A delete waits on an answer, so its card opens in the Claude Code chat whatever the option says: retitle and focus
|
|
494
|
+
// the switch's pane when it is open already, then redraw it as the card.
|
|
487
495
|
if (held.length === 1) await $.ui.open({ id: PANE, title: HOLD_TITLE, focus: true })
|
|
488
496
|
$.ui.invalidate('ui.render')
|
|
489
497
|
|
|
@@ -508,8 +516,10 @@ async function hold($: EngineInterface, card: Omit<Held, 'settle'>, signal: Abor
|
|
|
508
516
|
* Registers this mod's `delete-guard` command, its session hook, its Bash tool hook, and its pane: the switch, or the card while a delete waits.
|
|
509
517
|
*
|
|
510
518
|
* @param on - Hook registration function the engine passes in.
|
|
519
|
+
* @param options - Plugin options; `showInClaudeCode` keeps the switch in the Claude Code chat.
|
|
511
520
|
*/
|
|
512
|
-
export const register: Register = on => {
|
|
521
|
+
export const register: Register = (on, options) => {
|
|
522
|
+
isShownInChat = options.showInClaudeCode === true
|
|
513
523
|
let trash: string | undefined
|
|
514
524
|
let home = ''
|
|
515
525
|
|
|
@@ -524,7 +534,7 @@ export const register: Register = on => {
|
|
|
524
534
|
description: 'Turn the permanent-delete guard on or off; it stays that way in later sessions',
|
|
525
535
|
argumentHint: 'on | off',
|
|
526
536
|
})
|
|
527
|
-
void $.ui.open({ id: PANE, title: TITLE })
|
|
537
|
+
if (isShownInChat) void $.ui.open({ id: PANE, title: TITLE })
|
|
528
538
|
return next(e)
|
|
529
539
|
})
|
|
530
540
|
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
"title": "Minutes",
|
|
13
13
|
"default": 25,
|
|
14
14
|
"description": "Length of a focus block in minutes"
|
|
15
|
+
},
|
|
16
|
+
"showInClaudeCode": {
|
|
17
|
+
"type": "boolean",
|
|
18
|
+
"title": "Show in Claude Code",
|
|
19
|
+
"default": false,
|
|
20
|
+
"description": "Open this mod's pane in the Claude Code chat when a session starts. The mods viewer shows it either way."
|
|
15
21
|
}
|
|
16
22
|
}
|
|
17
23
|
}
|
|
@@ -81,7 +81,7 @@ async function setLength($: EngineInterface, minutes: number): Promise<void> {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
* Starts a block of `minutes`, replacing one already running
|
|
84
|
+
* Starts a block of `minutes`, replacing one already running.
|
|
85
85
|
*
|
|
86
86
|
* @param $ - Engine handle the hook received, see `EngineInterface`.
|
|
87
87
|
* @param minutes - Length of the block in minutes.
|
|
@@ -89,7 +89,7 @@ async function setLength($: EngineInterface, minutes: number): Promise<void> {
|
|
|
89
89
|
async function startFocus($: EngineInterface, minutes: number): Promise<void> {
|
|
90
90
|
const startedAt = await $.clock.now()
|
|
91
91
|
await update($, block, () => ({ startedAt, endsAt: startedAt + minutes * 60_000, minutes }))
|
|
92
|
-
|
|
92
|
+
$.ui.invalidate('ui.render')
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
@@ -144,7 +144,8 @@ async function awayBlock($: EngineInterface): Promise<FocusBlock | null> {
|
|
|
144
144
|
* from asking anything during a block: questions are refused, and so are permission prompts.
|
|
145
145
|
*
|
|
146
146
|
* @param on - Hook registration function the engine passes in.
|
|
147
|
-
* @param options - Plugin options; `minutes` sets the default block length
|
|
147
|
+
* @param options - Plugin options; `minutes` sets the default block length, `showInClaudeCode` opens the pane in the
|
|
148
|
+
* Claude Code chat at session start.
|
|
148
149
|
*/
|
|
149
150
|
export const register: Register = (on, options) => {
|
|
150
151
|
const configured = Number(options.minutes ?? DEFAULT_MINUTES)
|
|
@@ -158,7 +159,7 @@ export const register: Register = (on, options) => {
|
|
|
158
159
|
description: 'Start a focus block in which Claude asks you nothing (the length you set, or the minutes given), or end the running one',
|
|
159
160
|
argumentHint: '[minutes] | end',
|
|
160
161
|
})
|
|
161
|
-
void $.ui.open({ id: PANE, title: TITLE })
|
|
162
|
+
if (options.showInClaudeCode === true) void $.ui.open({ id: PANE, title: TITLE })
|
|
162
163
|
$.clock.every(1000, () => void tick($))
|
|
163
164
|
return next(e)
|
|
164
165
|
})
|
|
@@ -177,6 +178,8 @@ export const register: Register = (on, options) => {
|
|
|
177
178
|
// A length typed with the command becomes the one the pane starts with next time.
|
|
178
179
|
if (arg !== '') await setLength($, minutes)
|
|
179
180
|
await startFocus($, minutes)
|
|
181
|
+
// Typed in the chat, so the countdown shows there too.
|
|
182
|
+
await $.ui.open({ id: PANE, title: TITLE })
|
|
180
183
|
return { text: `Focus block started: ${minutes} min.` }
|
|
181
184
|
})
|
|
182
185
|
|
|
@@ -5,5 +5,13 @@
|
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "g3ntrovert"
|
|
7
7
|
},
|
|
8
|
-
"types": "./types/index.d.ts"
|
|
8
|
+
"types": "./types/index.d.ts",
|
|
9
|
+
"userConfig": {
|
|
10
|
+
"showInClaudeCode": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"title": "Show in Claude Code",
|
|
13
|
+
"default": false,
|
|
14
|
+
"description": "Open this mod's pane in the Claude Code chat when a session starts. The mods viewer shows it either way."
|
|
15
|
+
}
|
|
16
|
+
}
|
|
9
17
|
}
|
|
@@ -27,7 +27,7 @@ const CELL_WIDTH = 2
|
|
|
27
27
|
const NUMBER_COLORS = [undefined, '#6b8fd6', '#7cc47f', '#e06c6c', '#9b87f5', '#e0775a', '#5fb3c4', '#d0d0d0', '#8a8f98']
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Opens the pane in the Claude Code chat, titled after the game it holds.
|
|
31
31
|
*
|
|
32
32
|
* @param $ - Engine handle the hook received, see `EngineInterface`.
|
|
33
33
|
*/
|
|
@@ -36,14 +36,25 @@ async function openPane($: EngineInterface): Promise<void> {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Titles the pane after the game it holds where the Claude Code chat shows it, and redraws it. A press never opens
|
|
40
|
+
* it there: a pane the mods viewer alone draws stays out of the chat.
|
|
41
|
+
*
|
|
42
|
+
* @param $ - Engine handle the hook received, see `EngineInterface`.
|
|
43
|
+
*/
|
|
44
|
+
async function showGame($: EngineInterface): Promise<void> {
|
|
45
|
+
if ((await $.ui.panes()).some(pane => pane.id === PANE)) await openPane($)
|
|
46
|
+
else $.ui.invalidate('ui.render')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Starts a new Minesweeper board and shows it.
|
|
40
51
|
*
|
|
41
52
|
* @param $ - Engine handle the hook received, see `EngineInterface`.
|
|
42
53
|
*/
|
|
43
54
|
async function startGame($: EngineInterface): Promise<void> {
|
|
44
55
|
const now = await $.clock.now()
|
|
45
56
|
await update($, game, () => newGame(now))
|
|
46
|
-
await
|
|
57
|
+
await showGame($)
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
/**
|
|
@@ -57,13 +68,13 @@ async function showView($: EngineInterface, next: View): Promise<void> {
|
|
|
57
68
|
await update($, view, () => next)
|
|
58
69
|
if (next === 'mole') {
|
|
59
70
|
await update($, whack, current => current ?? newWhack())
|
|
60
|
-
await
|
|
71
|
+
await showGame($)
|
|
61
72
|
return
|
|
62
73
|
}
|
|
63
74
|
await changeWhack($, quit)
|
|
64
75
|
const current = await read($, game)
|
|
65
76
|
if (current === null || current.status === 'won' || current.status === 'lost') await startGame($)
|
|
66
|
-
else await
|
|
77
|
+
else await showGame($)
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
/**
|
|
@@ -161,15 +172,16 @@ async function hint($: EngineInterface, turnId: string): Promise<void> {
|
|
|
161
172
|
* Registers this mod's `mini-game` command, its session, turn and pane hooks.
|
|
162
173
|
*
|
|
163
174
|
* @param on - Hook registration function the engine passes in.
|
|
175
|
+
* @param options - Plugin options; `showInClaudeCode` opens the pane in the Claude Code chat at session start.
|
|
164
176
|
*/
|
|
165
|
-
export const register: Register = on => {
|
|
177
|
+
export const register: Register = (on, options) => {
|
|
166
178
|
on('session.start', async ($, e, next) => {
|
|
167
179
|
await $.command.register({ name: 'mini-game', description: 'Play Minesweeper or whack-a-mole while Claude works' })
|
|
168
180
|
const storedBest = await $.store.get(BEST_KEY)
|
|
169
181
|
if (typeof storedBest === 'number') await update($, best, () => storedBest)
|
|
170
182
|
const storedWhackBest = await $.store.get(WHACK_BEST_KEY)
|
|
171
183
|
if (typeof storedWhackBest === 'number') await update($, whackBest, () => storedWhackBest)
|
|
172
|
-
void openPane($)
|
|
184
|
+
if (options.showInClaudeCode === true) void openPane($)
|
|
173
185
|
$.clock.every(1000, () => void tick($))
|
|
174
186
|
return next(e)
|
|
175
187
|
})
|
|
@@ -179,6 +191,8 @@ export const register: Register = on => {
|
|
|
179
191
|
const next = arg === '' ? await read($, view) : VIEW_ARGS[arg]
|
|
180
192
|
if (next === undefined) return { text: `Unknown game "${arg}". Use /mini-game mines or /mini-game mole.`, exitCode: 1 }
|
|
181
193
|
await showView($, next)
|
|
194
|
+
// Typed in the chat, so the game shows there too.
|
|
195
|
+
await openPane($)
|
|
182
196
|
return {
|
|
183
197
|
text: next === 'mines'
|
|
184
198
|
? 'Minesweeper is open. Clear the board without hitting a mine.'
|
|
@@ -5,5 +5,13 @@
|
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "g3ntrovert"
|
|
7
7
|
},
|
|
8
|
-
"types": "./types/index.d.ts"
|
|
8
|
+
"types": "./types/index.d.ts",
|
|
9
|
+
"userConfig": {
|
|
10
|
+
"showInClaudeCode": {
|
|
11
|
+
"type": "boolean",
|
|
12
|
+
"title": "Show in Claude Code",
|
|
13
|
+
"default": false,
|
|
14
|
+
"description": "Open this mod's pane in the Claude Code chat when a session starts. The mods viewer shows it either way."
|
|
15
|
+
}
|
|
16
|
+
}
|
|
9
17
|
}
|
|
@@ -12,6 +12,19 @@ const selected = atom({ plugin: 'tool-timeline', key: 'selected' } as const, '')
|
|
|
12
12
|
/** Calls kept in state; older ones fall off. */
|
|
13
13
|
const KEEP = 100
|
|
14
14
|
|
|
15
|
+
/** Prop the mods viewer adds to every pane it draws; the Claude Code extension draws on the `vscode` surface too, without it. */
|
|
16
|
+
const VIEWER_PROP = 'g3nViewer'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Tells whether the mods viewer asked for this drawing, rather than the Claude Code extension on the same surface.
|
|
20
|
+
*
|
|
21
|
+
* @param props - The `Pane` props of the `ui.render` argument.
|
|
22
|
+
* @returns `true` when the viewer's prop is set.
|
|
23
|
+
*/
|
|
24
|
+
function isViewer(props: object): boolean {
|
|
25
|
+
return (props as Readonly<Record<string, unknown>>)[VIEWER_PROP] === true
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
/** Approximate pixel width of one cell of the editor's code font. */
|
|
16
29
|
const CELL_PX = 7
|
|
17
30
|
|
|
@@ -250,11 +263,12 @@ async function toggleSelected($: EngineInterface, id: string): Promise<void> {
|
|
|
250
263
|
* @brief Subscribes the timeline's handlers to the engine's events, its command and its pane.
|
|
251
264
|
*
|
|
252
265
|
* @param on - Subscribes a handler to one engine event, optionally filtered.
|
|
266
|
+
* @param options - Plugin options; `showInClaudeCode` opens the pane in the Claude Code chat at session start.
|
|
253
267
|
*/
|
|
254
|
-
export const register: Register = on => {
|
|
268
|
+
export const register: Register = (on, options) => {
|
|
255
269
|
on('session.start', async ($, e, next) => {
|
|
256
|
-
await $.command.register({ name: 'tool-timeline', description: 'Show or hide the tool call timeline' })
|
|
257
|
-
void $.ui.open({ id: PANE, title: TITLE })
|
|
270
|
+
await $.command.register({ name: 'tool-timeline', description: 'Show or hide the tool call timeline in the Claude Code chat' })
|
|
271
|
+
if (options.showInClaudeCode === true) void $.ui.open({ id: PANE, title: TITLE })
|
|
258
272
|
$.clock.every(1000, () => void tick($))
|
|
259
273
|
return next(e)
|
|
260
274
|
})
|
|
@@ -329,7 +343,7 @@ export const register: Register = on => {
|
|
|
329
343
|
const axis = placeCalls(shown, now)
|
|
330
344
|
|
|
331
345
|
// The viewer draws the timeline with the mod's own view: hand it the calls as data.
|
|
332
|
-
if (e.surface === 'vscode') {
|
|
346
|
+
if (e.surface === 'vscode' && isViewer(e.props)) {
|
|
333
347
|
const { Input } = $.ui.resolve(e)
|
|
334
348
|
const spans = screenSpans(list, now)
|
|
335
349
|
const data: TimelineViewData = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "you-should-know",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"description": "A card under
|
|
4
|
+
"description": "A compact card under a reply: the questions it asks you word for word, and for long replies what changed and what it concluded, each line tagged.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "g3ntrovert"
|
|
7
7
|
},
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"minChars": {
|
|
11
11
|
"type": "number",
|
|
12
12
|
"title": "Minimum reply length",
|
|
13
|
-
"default":
|
|
14
|
-
"description": "Replies shorter than this many characters get no
|
|
13
|
+
"default": 800,
|
|
14
|
+
"description": "Replies shorter than this many characters get no changes, conclusions or facts; their questions still show"
|
|
15
15
|
},
|
|
16
16
|
"model": {
|
|
17
17
|
"type": "string",
|