| name | create-hook |
| description | Create a new agentick lifecycle hook or custom hook. Use when asked to add a hook, create reactive behavior, or implement lifecycle logic. |
Create a Hook
Agentick hooks follow React conventions — they're functions starting with use that compose state and lifecycle behavior.
Existing Hooks
Before creating a new hook, check if an existing one covers your use case:
| Hook | Purpose |
|---|
useState | Local state (standard React) |
useEffect | Side effects (standard React) |
useSignal | Reactive signal state |
useKnob | Model-visible, model-settable reactive state |
useOnMount | Run once on first tick |
useOnUnmount | Cleanup on component removal |
useOnTickStart | Run at start of tick 2+ |
useOnTickEnd | Run at end of every tick |
useContinuation | Control whether execution continues |
useAfterCompile | Run after each compilation |
useOnMessage | React to individual messages |
useComState | Subscribe to COM state changes |
useData | Reactive data cache with serialization |
Steps
- Create the hook file in
packages/core/src/hooks/:
import { useState, useEffect } from "react";
import { useCom } from "./context";
export function useMyHook(initialValue: string) {
const ctx = useCom();
const [value, setValue] = useState(initialValue);
useEffect(() => {
return () => {
};
}, []);
return [value, setValue] as const;
}
- Export from the hooks index:
export { useMyHook } from "./my-hook";
- Re-export from core's public API if it should be user-facing:
export { useMyHook } from "./hooks";
Lifecycle Hook Signatures
All lifecycle callbacks follow "data first, ctx last":
useOnMount((ctx) => {});
useOnTickStart((tickState, ctx) => {});
useOnTickEnd((result, ctx) => {});
useAfterCompile((compiled, ctx) => {});
useContinuation((result, ctx) => boolean | void);
useOnMessage((message, ctx, state) => {});
Accessing COM
import { useCom } from "./context";
export function useMyHook() {
const ctx = useCom();
}
Key Files
- All hooks:
packages/core/src/hooks/
- Hook index:
packages/core/src/hooks/index.ts
- Lifecycle hooks:
packages/core/src/hooks/lifecycle.ts
- Signal hooks:
packages/core/src/hooks/signal.ts
- Knob hooks:
packages/core/src/hooks/knob.ts
- COM context:
packages/core/src/hooks/context.ts
Testing
import { describe, it, expect, afterEach } from "vitest";
import { createApp } from "@agentick/core";
import { createTestAdapter, renderAgent, cleanup } from "@agentick/core/testing";
describe("useMyHook", () => {
afterEach(() => cleanup());
it("works in a component", async () => {
const adapter = createTestAdapter({ defaultResponse: "ok" });
function TestAgent() {
const [val] = useMyHook("initial");
return (
<>
<Model model={adapter} />
<System>Value: {val}</System>
<Timeline />
</>
);
}
const app = createApp(TestAgent);
const result = await app.run({
messages: [{ role: "user", content: [{ type: "text", text: "test" }] }],
}).result;
expect(result).toBeDefined();
});
});
After creating, run:
pnpm --filter @agentick/core typecheck
pnpm --filter @agentick/core test