@cmodjs/core 0.1.4 → 0.1.5
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/README.md +4 -0
- package/connect.js +30 -30
- package/docs/commands.md +14 -6
- package/docs/index.md +1 -1
- package/docs/mod.md +3 -3
- package/package.json +1 -1
- package/src/connect.ts +30 -30
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/heyJordanParker/cmod/main/.claude-plugin/icon.png" alt="Claude Mod Manager" width="128">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# @cmodjs/core
|
|
2
6
|
|
|
3
7
|
The library every Claude Mod Manager mod imports. Claude Mod Manager (CMod) installs, tries, builds, checks, and publishes Claude Code mods: plugins with hooks, panes, slash commands, tools, permission rules, and an install step that runs with your consent.
|
package/connect.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createLifecycle, readPlugin } from './runtime/lifecycle.js';
|
|
2
2
|
let lifecycle;
|
|
3
|
-
async function
|
|
3
|
+
async function startMod($, e, next) {
|
|
4
4
|
await lifecycle.start(claudeOf($), readPlugin);
|
|
5
5
|
return next(e);
|
|
6
6
|
}
|
|
7
|
-
function
|
|
7
|
+
function routeToMod(_$, e, next) {
|
|
8
8
|
return lifecycle.route(next.event, e, (passed) => next(passed));
|
|
9
9
|
}
|
|
10
10
|
function claudeOf($) {
|
|
@@ -67,32 +67,32 @@ function claudeOf($) {
|
|
|
67
67
|
}
|
|
68
68
|
export function connect(on, definition) {
|
|
69
69
|
lifecycle = createLifecycle(definition);
|
|
70
|
-
on('session.start',
|
|
71
|
-
on('classic.SessionStart',
|
|
72
|
-
on('classic.SessionEnd',
|
|
73
|
-
on('classic.UserPromptSubmit',
|
|
74
|
-
on('classic.InstructionsLoaded',
|
|
75
|
-
on('classic.PreToolUse',
|
|
76
|
-
on('classic.PermissionRequest',
|
|
77
|
-
on('classic.PermissionDenied',
|
|
78
|
-
on('classic.PostToolUse',
|
|
79
|
-
on('classic.PostToolUseFailure',
|
|
80
|
-
on('classic.PostToolBatch',
|
|
81
|
-
on('classic.SubagentStart',
|
|
82
|
-
on('classic.SubagentStop',
|
|
83
|
-
on('classic.Notification',
|
|
84
|
-
on('classic.PreCompact',
|
|
85
|
-
on('classic.Stop',
|
|
86
|
-
on('classic.StopFailure',
|
|
87
|
-
on('tool.check',
|
|
88
|
-
on('tool.call',
|
|
89
|
-
on('prompt.submit',
|
|
90
|
-
on('prompt.context',
|
|
91
|
-
on('command.run',
|
|
92
|
-
on('session.measure',
|
|
93
|
-
on('skill.prompt',
|
|
94
|
-
on('ui.render',
|
|
95
|
-
on('ui.press',
|
|
96
|
-
on('ui.close',
|
|
97
|
-
on('cmod.call',
|
|
70
|
+
on('session.start', startMod);
|
|
71
|
+
on('classic.SessionStart', routeToMod);
|
|
72
|
+
on('classic.SessionEnd', routeToMod);
|
|
73
|
+
on('classic.UserPromptSubmit', routeToMod);
|
|
74
|
+
on('classic.InstructionsLoaded', routeToMod);
|
|
75
|
+
on('classic.PreToolUse', routeToMod);
|
|
76
|
+
on('classic.PermissionRequest', routeToMod);
|
|
77
|
+
on('classic.PermissionDenied', routeToMod);
|
|
78
|
+
on('classic.PostToolUse', routeToMod);
|
|
79
|
+
on('classic.PostToolUseFailure', routeToMod);
|
|
80
|
+
on('classic.PostToolBatch', routeToMod);
|
|
81
|
+
on('classic.SubagentStart', routeToMod);
|
|
82
|
+
on('classic.SubagentStop', routeToMod);
|
|
83
|
+
on('classic.Notification', routeToMod);
|
|
84
|
+
on('classic.PreCompact', routeToMod);
|
|
85
|
+
on('classic.Stop', routeToMod);
|
|
86
|
+
on('classic.StopFailure', routeToMod);
|
|
87
|
+
on('tool.check', routeToMod);
|
|
88
|
+
on('tool.call', routeToMod);
|
|
89
|
+
on('prompt.submit', routeToMod);
|
|
90
|
+
on('prompt.context', routeToMod);
|
|
91
|
+
on('command.run', routeToMod);
|
|
92
|
+
on('session.measure', routeToMod);
|
|
93
|
+
on('skill.prompt', routeToMod);
|
|
94
|
+
on('ui.render', routeToMod);
|
|
95
|
+
on('ui.press', routeToMod);
|
|
96
|
+
on('ui.close', routeToMod);
|
|
97
|
+
on('cmod.call', routeToMod);
|
|
98
98
|
}
|
package/docs/commands.md
CHANGED
|
@@ -82,22 +82,30 @@ cmod publish [path] [--dry-run]
|
|
|
82
82
|
|
|
83
83
|
Releases the mod at `path` (default: the current folder) at the version in its `plugin.json`:
|
|
84
84
|
|
|
85
|
-
1. Builds the release
|
|
86
|
-
2.
|
|
87
|
-
3.
|
|
88
|
-
4.
|
|
89
|
-
5.
|
|
85
|
+
1. Builds the release from the committed files, leaving out `cli/`, `.github/`, and `.claude/`.
|
|
86
|
+
2. Bundles the hooks module that `hooks/hooks.json` names, with the mod's source and packages, into one readable `.js` file, and points `hooks/hooks.json` at it. Anthropic's plugin directory reads a repository without installing its packages, so it can follow a mod only when all its code is in one file.
|
|
87
|
+
3. Checks the release with `claude plugin validate --strict`, and stops with the validator's message when it fails.
|
|
88
|
+
4. Commits the release as the `release` branch, and builds the release archive from that commit.
|
|
89
|
+
5. Builds the program `cli/` declares, and writes `SHA256SUMS` for every file of the release.
|
|
90
|
+
6. Writes `.claude-plugin/marketplace.json`, listing the archive and the CMod plugin.
|
|
91
|
+
7. Commits that file, tags `v<version>`, pushes the tag and the `release` branch, and creates the GitHub release.
|
|
92
|
+
8. Prints the link to paste as the Repository when you submit the mod at [claude.ai/directory/manage](https://claude.ai/directory/manage), such as `https://github.com/owner/greeter/tree/release`. The portal reads the branch from the link, so the directory follows `release`.
|
|
93
|
+
|
|
94
|
+
The release keeps `package.json` and `bun.lock`, so Claude Code still installs the mod's packages for its install and uninstall steps. `main` keeps the source only.
|
|
90
95
|
|
|
91
96
|
It needs, and refuses to start without:
|
|
92
97
|
|
|
93
98
|
- the mod's folder as the root of a git repository with at least one commit, because `git archive` builds from `HEAD`
|
|
99
|
+
- `node_modules` in the mod's folder, from `bun install`, when the hooks module imports a package
|
|
100
|
+
- a `register` declared as a function in the hooks module, as `cmod new` writes it, because the validator refuses a bundled `register` that is not one
|
|
101
|
+
- the `claude` command, which validates the release
|
|
94
102
|
- an `origin` remote on GitHub
|
|
95
103
|
- a `package.json` with no dependency on a `file:` or `link:` path, which no user has
|
|
96
104
|
- a `"program"` in the `cmod` key of `package.json` that names the program `cli/` declares, when `cli/` declares one
|
|
97
105
|
- the network, to hash the CMod plugin release it lists, unless the mod is CMod itself
|
|
98
106
|
- for a real publish, a working tree with no uncommitted changes, a `v<version>` tag that does not exist yet, a `git push` to `origin` that succeeds, and the `gh` command, which creates the GitHub release
|
|
99
107
|
|
|
100
|
-
`--dry-run` builds and writes everything, and pushes nothing. It allows uncommitted changes and an existing tag, and builds from the last commit.
|
|
108
|
+
`--dry-run` builds and writes everything, and pushes nothing. It allows uncommitted changes and an existing tag, and builds from the last commit. It prints the folder that holds the release, so you can read what the directory will read.
|
|
101
109
|
|
|
102
110
|
## Install mods
|
|
103
111
|
|
package/docs/index.md
CHANGED
|
@@ -30,7 +30,7 @@ my-mod/
|
|
|
30
30
|
|
|
31
31
|
The template is a working example to replace. It counts the prompts of the session in `state.session.prompts`, shows the count in a pane titled with the mod's name and above the prompt, and tests both. Replace the counter, the pane, the component, the Skill, and the tests with the mod's own.
|
|
32
32
|
|
|
33
|
-
A mod imports `@cmodjs/core` by path, such as `../node_modules/@cmodjs/core/mod.js`, because Claude Code loads a plugin's own files only. Types such as `RenderElement` and `
|
|
33
|
+
A mod imports `@cmodjs/core` by path, such as `../node_modules/@cmodjs/core/mod.js`, because Claude Code loads a plugin's own files only. Types such as `RenderElement` and `On` come from the `claude-code` module.
|
|
34
34
|
|
|
35
35
|
## Type-check after the first session
|
|
36
36
|
|
package/docs/mod.md
CHANGED
|
@@ -59,11 +59,11 @@ Each of these says `Fix it, then run /reload-plugins.`
|
|
|
59
59
|
`hooks/hooks.json` names `hooks/register.ts` as the plugin's hooks module. `register.ts` calls `connect` and does nothing else:
|
|
60
60
|
|
|
61
61
|
```ts
|
|
62
|
-
import type {
|
|
62
|
+
import type { On } from 'claude-code'
|
|
63
63
|
import { connect } from '../node_modules/@cmodjs/core/connect.js'
|
|
64
64
|
import { greeter } from '../src/mod.js'
|
|
65
65
|
|
|
66
|
-
export
|
|
66
|
+
export function register(on: On): void {
|
|
67
67
|
connect(on, greeter)
|
|
68
68
|
}
|
|
69
69
|
```
|
|
@@ -72,7 +72,7 @@ export const register: Register = (on) => {
|
|
|
72
72
|
connect<State>(on: On, definition: ModDefinition<State>): void
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
`connect` registers one handler per Claude Code event the mod can use, and routes each event to the mod. Keep `register.ts` this small. Claude Code checks a hooks module before it loads it, and refuses some shapes, such as a `$` passed to a function in another module.
|
|
75
|
+
`connect` registers one handler per Claude Code event the mod can use, and routes each event to the mod. Keep `register.ts` this small. Claude Code checks a hooks module before it loads it, and refuses some shapes, such as a `$` passed to a function in another module. Declare `register` as a function, as here: `cmod publish` bundles the hooks module into one file, and Claude Code refuses a bundled `register` that is not one.
|
|
76
76
|
|
|
77
77
|
## What `mod` can call
|
|
78
78
|
|
package/package.json
CHANGED
package/src/connect.ts
CHANGED
|
@@ -6,12 +6,12 @@ import { createLifecycle, readPlugin, type Lifecycle } from './runtime/lifecycle
|
|
|
6
6
|
|
|
7
7
|
let lifecycle: Lifecycle<object>
|
|
8
8
|
|
|
9
|
-
async function
|
|
9
|
+
async function startMod($: EngineInterface, e: Frozen<Args<'session.start'>>, next: Next<'session.start'>): Promise<EventResult<'session.start'>> {
|
|
10
10
|
await lifecycle.start(claudeOf($), readPlugin)
|
|
11
11
|
return next(e)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
function
|
|
14
|
+
function routeToMod<N extends RoutedEvent>(_$: EngineInterface, e: Frozen<Args<N>>, next: Next<N>): Promise<EventResult<N>> {
|
|
15
15
|
return lifecycle.route(next.event, e, (passed: unknown) => next(passed as Args<N>) as Promise<EventResult<N>>)
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -76,32 +76,32 @@ function claudeOf($: EngineInterface): Claude {
|
|
|
76
76
|
|
|
77
77
|
export function connect<State extends object>(on: On, definition: ModDefinition<State>): void {
|
|
78
78
|
lifecycle = createLifecycle(definition)
|
|
79
|
-
on('session.start',
|
|
80
|
-
on('classic.SessionStart',
|
|
81
|
-
on('classic.SessionEnd',
|
|
82
|
-
on('classic.UserPromptSubmit',
|
|
83
|
-
on('classic.InstructionsLoaded',
|
|
84
|
-
on('classic.PreToolUse',
|
|
85
|
-
on('classic.PermissionRequest',
|
|
86
|
-
on('classic.PermissionDenied',
|
|
87
|
-
on('classic.PostToolUse',
|
|
88
|
-
on('classic.PostToolUseFailure',
|
|
89
|
-
on('classic.PostToolBatch',
|
|
90
|
-
on('classic.SubagentStart',
|
|
91
|
-
on('classic.SubagentStop',
|
|
92
|
-
on('classic.Notification',
|
|
93
|
-
on('classic.PreCompact',
|
|
94
|
-
on('classic.Stop',
|
|
95
|
-
on('classic.StopFailure',
|
|
96
|
-
on('tool.check',
|
|
97
|
-
on('tool.call',
|
|
98
|
-
on('prompt.submit',
|
|
99
|
-
on('prompt.context',
|
|
100
|
-
on('command.run',
|
|
101
|
-
on('session.measure',
|
|
102
|
-
on('skill.prompt',
|
|
103
|
-
on('ui.render',
|
|
104
|
-
on('ui.press',
|
|
105
|
-
on('ui.close',
|
|
106
|
-
on('cmod.call',
|
|
79
|
+
on('session.start', startMod)
|
|
80
|
+
on('classic.SessionStart', routeToMod)
|
|
81
|
+
on('classic.SessionEnd', routeToMod)
|
|
82
|
+
on('classic.UserPromptSubmit', routeToMod)
|
|
83
|
+
on('classic.InstructionsLoaded', routeToMod)
|
|
84
|
+
on('classic.PreToolUse', routeToMod)
|
|
85
|
+
on('classic.PermissionRequest', routeToMod)
|
|
86
|
+
on('classic.PermissionDenied', routeToMod)
|
|
87
|
+
on('classic.PostToolUse', routeToMod)
|
|
88
|
+
on('classic.PostToolUseFailure', routeToMod)
|
|
89
|
+
on('classic.PostToolBatch', routeToMod)
|
|
90
|
+
on('classic.SubagentStart', routeToMod)
|
|
91
|
+
on('classic.SubagentStop', routeToMod)
|
|
92
|
+
on('classic.Notification', routeToMod)
|
|
93
|
+
on('classic.PreCompact', routeToMod)
|
|
94
|
+
on('classic.Stop', routeToMod)
|
|
95
|
+
on('classic.StopFailure', routeToMod)
|
|
96
|
+
on('tool.check', routeToMod)
|
|
97
|
+
on('tool.call', routeToMod)
|
|
98
|
+
on('prompt.submit', routeToMod)
|
|
99
|
+
on('prompt.context', routeToMod)
|
|
100
|
+
on('command.run', routeToMod)
|
|
101
|
+
on('session.measure', routeToMod)
|
|
102
|
+
on('skill.prompt', routeToMod)
|
|
103
|
+
on('ui.render', routeToMod)
|
|
104
|
+
on('ui.press', routeToMod)
|
|
105
|
+
on('ui.close', routeToMod)
|
|
106
|
+
on('cmod.call', routeToMod)
|
|
107
107
|
}
|