@hasna/hooks 0.2.2 → 0.2.4

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.
Files changed (33) hide show
  1. package/README.md +302 -107
  2. package/bin/index.js +316 -51
  3. package/dist/index.js +216 -14
  4. package/hooks/hook-autoformat/LICENSE +191 -0
  5. package/hooks/hook-autostage/LICENSE +191 -0
  6. package/hooks/hook-branchprotect/LICENSE +191 -0
  7. package/hooks/hook-checkdocs/LICENSE +191 -0
  8. package/hooks/hook-checkpoint/LICENSE +191 -0
  9. package/hooks/hook-checktasks/LICENSE +191 -0
  10. package/hooks/hook-commandlog/LICENSE +191 -0
  11. package/hooks/hook-contextrefresh/LICENSE +191 -0
  12. package/hooks/hook-costwatch/LICENSE +191 -0
  13. package/hooks/hook-desktopnotify/LICENSE +191 -0
  14. package/hooks/hook-envsetup/LICENSE +191 -0
  15. package/hooks/hook-errornotify/LICENSE +191 -0
  16. package/hooks/hook-gitguard/LICENSE +191 -0
  17. package/hooks/hook-packageage/LICENSE +191 -0
  18. package/hooks/hook-permissionguard/LICENSE +191 -0
  19. package/hooks/hook-phonenotify/LICENSE +191 -0
  20. package/hooks/hook-precompact/LICENSE +191 -0
  21. package/hooks/hook-promptguard/LICENSE +191 -0
  22. package/hooks/hook-protectfiles/LICENSE +191 -0
  23. package/hooks/hook-sessionlog/LICENSE +191 -0
  24. package/hooks/hook-slacknotify/LICENSE +191 -0
  25. package/hooks/hook-soundnotify/LICENSE +191 -0
  26. package/hooks/hook-stylescheck/LICENSE +191 -0
  27. package/hooks/hook-stylescheck/README.md +41 -0
  28. package/hooks/hook-stylescheck/package.json +52 -0
  29. package/hooks/hook-stylescheck/src/hook.ts +213 -0
  30. package/hooks/hook-stylescheck/tsconfig.json +25 -0
  31. package/hooks/hook-taskgate/LICENSE +191 -0
  32. package/hooks/hook-tddguard/LICENSE +191 -0
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -1,95 +1,112 @@
1
- # Hooks
1
+ # @hasna/hooks
2
2
 
3
- Open source library of 30 Claude Code hooks. Install any hook with a single command.
3
+ Open source lifecycle hooks library for AI coding agents. 30 hooks across 10 categories, installable with a single command. Works with Claude Code and Gemini CLI.
4
4
 
5
- ## Quick Start
6
-
7
- ```bash
8
- # Interactive mode - browse and select hooks
9
- npx @hasna/hooks
10
-
11
- # Install specific hooks
12
- npx @hasna/hooks install gitguard branchprotect checkpoint
5
+ ## Features
13
6
 
14
- # List all available hooks
15
- npx @hasna/hooks list
16
- ```
7
+ - **30 hooks** in 10 categories (git safety, code quality, security, notifications, and more)
8
+ - **Interactive CLI** (`hooks`) with React/Ink TUI for browsing, installing, and managing hooks
9
+ - **MCP server** (13 tools) for agent-driven hook management over stdio or SSE
10
+ - **Library** for programmatic access from Node.js/Bun
11
+ - **Multi-agent support** for Claude Code and Gemini CLI with per-agent event mapping
12
+ - **Agent profiles** with unique 8-char UUID identity system
13
+ - **Zero file copy** -- hooks run from the globally installed package, nothing is copied to your project
14
+ - **Web dashboard** for browsing hooks (Vite + React 19 + TailwindCSS 4)
15
+ - **Health checks** via `hooks doctor` to verify hook installation integrity
16
+ - Global or project-scoped installation
17
17
 
18
18
  ## Installation
19
19
 
20
20
  ```bash
21
- # Global install
22
21
  bun install -g @hasna/hooks
23
-
24
- # Or use npx (no install needed)
25
- npx @hasna/hooks
26
22
  ```
27
23
 
28
- ## NPM Auth (Optional)
29
-
30
- If you need a scoped registry token (publish or private installs), copy an example file and set `NPM_TOKEN`:
24
+ Or with npm:
31
25
 
32
26
  ```bash
33
- cp .npmrc.example .npmrc
27
+ npm install -g @hasna/hooks
34
28
  ```
35
29
 
36
- See `CONTRIBUTING.md` for publishing and secrets guidance.
37
-
38
- ## Usage
39
-
40
- ### Interactive Mode
41
-
42
- Run without arguments to browse hooks by category:
30
+ Or use without installing:
43
31
 
44
32
  ```bash
45
- hooks
33
+ npx @hasna/hooks
46
34
  ```
47
35
 
48
- ### Install Hooks
36
+ ## Quick Start
49
37
 
50
38
  ```bash
51
- # Install one or more hooks
39
+ # Interactive mode -- browse and select hooks
40
+ hooks
41
+
42
+ # Install specific hooks
52
43
  hooks install gitguard branchprotect checkpoint
53
44
 
54
- # Hooks are installed to .hooks/ and registered in ~/.claude/settings.json
55
- ```
45
+ # Install all hooks in a category
46
+ hooks install --category "Git Safety"
56
47
 
57
- ### Search
48
+ # Install all 30 hooks
49
+ hooks install --all
50
+
51
+ # List all available hooks
52
+ hooks list
58
53
 
59
- ```bash
60
54
  # Search by name, description, or tags
61
55
  hooks search security
62
- hooks search git
63
- ```
64
56
 
65
- ### List by Category
57
+ # Check health of installed hooks
58
+ hooks doctor
66
59
 
67
- ```bash
68
- hooks list --category "Git Safety"
69
- hooks list --category "Code Quality"
60
+ # Start MCP server for agent integration
61
+ hooks mcp --stdio
70
62
  ```
71
63
 
72
- ### Hook Info
73
-
74
- ```bash
75
- hooks info gitguard
76
- ```
64
+ ## Hook Categories
65
+
66
+ | Category | Count | Description |
67
+ |----------|------:|-------------|
68
+ | Git Safety | 3 | Block destructive git ops, protect branches, create snapshots |
69
+ | Code Quality | 6 | Check tests, lint, bugs, docs, files, and task completion |
70
+ | Security | 2 | Security audits and typosquatting prevention |
71
+ | Notifications | 5 | Phone, desktop, Slack, sound, and inter-agent messages |
72
+ | Context Management | 2 | Re-inject context and save state before compaction |
73
+ | Workflow Automation | 3 | Auto-format, auto-stage, and TDD enforcement |
74
+ | Environment | 1 | Detect nvm/virtualenv/asdf/rbenv activation needs |
75
+ | Permissions | 3 | Auto-approve safe commands, protect files, block prompt injection |
76
+ | Observability | 4 | Session logs, command logs, cost tracking, error detection |
77
+ | Agent Teams | 1 | Validate task completion criteria |
78
+
79
+ ## Hook Events
80
+
81
+ | Event | Timing | Can Block | Matcher |
82
+ |-------|--------|:---------:|---------|
83
+ | PreToolUse | Before tool execution | Yes | Tool name pattern (e.g., `Bash`, `Write\|Edit`) |
84
+ | PostToolUse | After tool execution | No | Tool name pattern |
85
+ | Stop | Session ends | No | Empty string |
86
+ | Notification | System events (e.g., compaction) | No | Empty string |
77
87
 
78
- ### Check Registered Hooks
88
+ Each hook receives JSON on stdin and outputs JSON on stdout:
79
89
 
80
- ```bash
81
- hooks list --registered
82
- ```
90
+ ```json
91
+ // Input (PreToolUse)
92
+ {
93
+ "session_id": "abc123",
94
+ "cwd": "/path/to/project",
95
+ "tool_name": "Bash",
96
+ "tool_input": { "command": "git push --force" }
97
+ }
83
98
 
84
- ### Remove
99
+ // Output (block)
100
+ { "decision": "block", "reason": "Destructive git operation blocked" }
85
101
 
86
- ```bash
87
- hooks remove gitguard
102
+ // Output (approve)
103
+ { "decision": "approve" }
88
104
  ```
89
105
 
90
106
  ## Available Hooks (30)
91
107
 
92
108
  ### Git Safety (3)
109
+
93
110
  | Hook | Event | Description |
94
111
  |------|-------|-------------|
95
112
  | gitguard | PreToolUse | Blocks destructive git operations like reset --hard, push --force, clean -f |
@@ -97,6 +114,7 @@ hooks remove gitguard
97
114
  | checkpoint | PreToolUse | Creates shadow git snapshots before file modifications for easy rollback |
98
115
 
99
116
  ### Code Quality (6)
117
+
100
118
  | Hook | Event | Description |
101
119
  |------|-------|-------------|
102
120
  | checktests | PostToolUse | Checks for missing tests after file edits |
@@ -107,12 +125,14 @@ hooks remove gitguard
107
125
  | checktasks | PostToolUse | Validates task completion and tracks progress |
108
126
 
109
127
  ### Security (2)
128
+
110
129
  | Hook | Event | Description |
111
130
  |------|-------|-------------|
112
131
  | checksecurity | PostToolUse | Runs security checks via headless agents |
113
132
  | packageage | PreToolUse | Checks package age before install to prevent typosquatting |
114
133
 
115
134
  ### Notifications (5)
135
+
116
136
  | Hook | Event | Description |
117
137
  |------|-------|-------------|
118
138
  | phonenotify | Stop | Sends push notifications to phone via ntfy.sh |
@@ -122,12 +142,14 @@ hooks remove gitguard
122
142
  | soundnotify | Stop | Plays a system sound when Claude finishes (macOS/Linux) |
123
143
 
124
144
  ### Context Management (2)
145
+
125
146
  | Hook | Event | Description |
126
147
  |------|-------|-------------|
127
148
  | contextrefresh | Notification | Re-injects important context every N prompts to prevent drift |
128
149
  | precompact | Notification | Saves session state before context compaction |
129
150
 
130
151
  ### Workflow Automation (3)
152
+
131
153
  | Hook | Event | Description |
132
154
  |------|-------|-------------|
133
155
  | autoformat | PostToolUse | Runs project formatter (Prettier, Biome, Ruff, Black, gofmt) after file edits |
@@ -135,11 +157,13 @@ hooks remove gitguard
135
157
  | tddguard | PreToolUse | Blocks implementation edits unless corresponding test files exist |
136
158
 
137
159
  ### Environment (1)
160
+
138
161
  | Hook | Event | Description |
139
162
  |------|-------|-------------|
140
163
  | envsetup | PreToolUse | Warns when nvm, virtualenv, asdf, or rbenv may need activation before commands |
141
164
 
142
165
  ### Permissions (3)
166
+
143
167
  | Hook | Event | Description |
144
168
  |------|-------|-------------|
145
169
  | permissionguard | PreToolUse | Auto-approves safe read-only commands and blocks dangerous operations |
@@ -147,6 +171,7 @@ hooks remove gitguard
147
171
  | promptguard | PreToolUse | Blocks prompt injection attempts and credential access requests |
148
172
 
149
173
  ### Observability (4)
174
+
150
175
  | Hook | Event | Description |
151
176
  |------|-------|-------------|
152
177
  | sessionlog | PostToolUse | Logs every tool call to .claude/session-log-\<date\>.jsonl |
@@ -155,74 +180,206 @@ hooks remove gitguard
155
180
  | errornotify | PostToolUse | Detects tool failures and logs errors to .claude/errors.log |
156
181
 
157
182
  ### Agent Teams (1)
183
+
158
184
  | Hook | Event | Description |
159
185
  |------|-------|-------------|
160
186
  | taskgate | PostToolUse | Validates task completion criteria before allowing tasks to be marked done |
161
187
 
162
- ## How Hooks Work
188
+ ## CLI Commands
189
+
190
+ | Command | Alias | Description |
191
+ |---------|-------|-------------|
192
+ | `hooks` | `hooks interactive`, `hooks i` | Interactive hook browser (default) |
193
+ | `hooks install <names...>` | `hooks add` | Install one or more hooks |
194
+ | `hooks install --all` | | Install all 30 hooks |
195
+ | `hooks install --category <cat>` | | Install all hooks in a category |
196
+ | `hooks list` | `hooks ls` | List available hooks |
197
+ | `hooks list --registered` | `hooks list -r` | Show currently registered hooks |
198
+ | `hooks list --category <cat>` | `hooks list -c <cat>` | Filter by category |
199
+ | `hooks search <query>` | | Search hooks by name, description, or tags |
200
+ | `hooks info <name>` | | Show detailed info about a hook |
201
+ | `hooks remove <name>` | `hooks rm` | Unregister a hook |
202
+ | `hooks update [names...]` | | Re-register hooks (picks up new package version) |
203
+ | `hooks doctor` | | Check health of installed hooks |
204
+ | `hooks docs [name]` | | Show documentation (general or hook-specific) |
205
+ | `hooks categories` | | List all categories with counts |
206
+ | `hooks init` | | Register a new agent profile |
207
+ | `hooks run <name>` | | Execute a hook (called by AI agents) |
208
+ | `hooks mcp` | | Start MCP server (SSE or `--stdio`) |
209
+ | `hooks upgrade` | | Self-update to latest version |
210
+
211
+ ### Scope Options
212
+
213
+ Most commands accept scope flags:
214
+
215
+ | Flag | Description |
216
+ |------|-------------|
217
+ | `--global`, `-g` | Global scope (`~/.claude/settings.json`) -- default |
218
+ | `--project`, `-p` | Project scope (`.claude/settings.json`) |
219
+ | `--json`, `-j` | Machine-readable JSON output |
220
+ | `--profile <id>` | Agent profile ID to scope hooks to |
221
+ | `--overwrite`, `-o` | Overwrite existing hook registration |
222
+
223
+ ## MCP Server
224
+
225
+ Start the MCP server for AI agent integration:
163
226
 
164
- Claude Code hooks are lifecycle interceptors that run during agent sessions:
227
+ ```bash
228
+ # Stdio transport (for agent MCP registration)
229
+ hooks mcp --stdio
165
230
 
166
- - **PreToolUse**: Runs before a tool executes. Can **block** the operation.
167
- - **PostToolUse**: Runs after a tool executes. Async, non-blocking.
168
- - **Stop**: Runs when a session ends. Async, non-blocking.
169
- - **Notification**: Runs on notification events. Async, non-blocking.
231
+ # SSE transport (default, port 39427)
232
+ hooks mcp
233
+ hooks mcp --port 8080
234
+ ```
170
235
 
171
- Each hook receives JSON on stdin and outputs JSON on stdout:
236
+ ### Registration
237
+
238
+ Add to `~/.claude/mcp.json` or equivalent agent config:
172
239
 
173
240
  ```json
174
- // PreToolUse input
175
241
  {
176
- "session_id": "abc123",
177
- "cwd": "/path/to/project",
178
- "tool_name": "Bash",
179
- "tool_input": { "command": "git push --force" }
242
+ "mcpServers": {
243
+ "hooks": {
244
+ "command": "hooks",
245
+ "args": ["mcp", "--stdio"]
246
+ }
247
+ }
180
248
  }
249
+ ```
181
250
 
182
- // PreToolUse output (block)
183
- { "decision": "block", "reason": "Destructive git operation blocked" }
251
+ ### MCP Tools (13)
184
252
 
185
- // PreToolUse output (approve)
186
- { "decision": "approve" }
187
- ```
253
+ | Tool | Description |
254
+ |------|-------------|
255
+ | `hooks_list` | List all available hooks, optionally filtered by category |
256
+ | `hooks_search` | Search for hooks by name, description, or tags |
257
+ | `hooks_info` | Get detailed information about a specific hook including install status |
258
+ | `hooks_install` | Install one or more hooks by name |
259
+ | `hooks_install_category` | Install all hooks in a category |
260
+ | `hooks_install_all` | Install all available hooks |
261
+ | `hooks_remove` | Remove (unregister) a hook from agent settings |
262
+ | `hooks_doctor` | Check health of installed hooks |
263
+ | `hooks_categories` | List all hook categories with counts |
264
+ | `hooks_docs` | Get documentation (general overview or hook-specific README) |
265
+ | `hooks_registered` | Get list of currently registered hooks for a scope |
266
+ | `hooks_init` | Register a new agent profile |
267
+ | `hooks_profiles` | List all registered agent profiles |
268
+
269
+ ## Agent Profiles
270
+
271
+ Agents can register a profile to get a unique 8-char UUID. This identity is injected into hook input when running with `--profile`.
188
272
 
189
- ## Hook Structure
273
+ ```bash
274
+ # Register a profile
275
+ hooks init --agent claude --name "my-agent"
276
+ # Agent profile created
277
+ # Agent ID: a1b2c3d4
278
+ # Type: claude
190
279
 
191
- Each hook follows a consistent structure:
280
+ # Install hooks scoped to a profile
281
+ hooks install gitguard --profile a1b2c3d4
192
282
 
283
+ # The registered settings entry becomes:
284
+ # hooks run gitguard --profile a1b2c3d4
193
285
  ```
194
- hook-{name}/
195
- ├── src/
196
- │ ├── hook.ts # Main hook logic (stdin → stdout)
197
- │ ├── cli.ts # CLI for install/uninstall/status
198
- │ └── index.ts # Library exports
199
- ├── package.json
200
- ├── CLAUDE.md
201
- ├── README.md
202
- └── tsconfig.json
286
+
287
+ Profiles are stored at `~/.hooks/profiles/<agent_id>.json`.
288
+
289
+ ## Library Usage
290
+
291
+ ```typescript
292
+ import {
293
+ HOOKS,
294
+ CATEGORIES,
295
+ searchHooks,
296
+ getHook,
297
+ getHooksByCategory,
298
+ installHook,
299
+ installHooks,
300
+ getRegisteredHooks,
301
+ removeHook,
302
+ createProfile,
303
+ listProfiles,
304
+ type HookMeta,
305
+ type Category,
306
+ } from "@hasna/hooks";
307
+
308
+ // Search for hooks
309
+ const securityHooks = searchHooks("security");
310
+
311
+ // Get hooks by category
312
+ const gitHooks = getHooksByCategory("Git Safety");
313
+
314
+ // Install a hook programmatically
315
+ const result = installHook("gitguard", { scope: "global" });
316
+
317
+ // Check what's registered
318
+ const registered = getRegisteredHooks("global");
319
+
320
+ // Create an agent profile
321
+ const profile = createProfile({ agent_type: "claude", name: "my-bot" });
203
322
  ```
204
323
 
205
- ## Installing Individual Hooks
324
+ ## Writing a Custom Hook
206
325
 
207
- You can also install hooks individually as npm packages:
326
+ Hooks follow a stdin/stdout JSON protocol. Create a new hook at `hooks/hook-<name>/`:
208
327
 
209
- ```bash
210
- bun install -g @hasna/hook-gitguard
211
- bun install -g @hasna/hook-branchprotect
212
- bun install -g @hasna/hook-checkpoint
328
+ ```
329
+ hooks/hook-myhook/
330
+ src/
331
+ hook.ts # Main hook logic (stdin JSON -> stdout JSON)
332
+ package.json
333
+ README.md
213
334
  ```
214
335
 
215
- Then use their built-in CLI:
336
+ ### Hook Template (PreToolUse)
216
337
 
217
- ```bash
218
- hook-gitguard install # Register in Claude settings
219
- hook-gitguard status # Check if registered
220
- hook-gitguard uninstall # Unregister
338
+ ```typescript
339
+ #!/usr/bin/env bun
340
+ import { readFileSync } from "fs";
341
+
342
+ interface HookInput {
343
+ session_id: string;
344
+ cwd: string;
345
+ tool_name: string;
346
+ tool_input: Record<string, unknown>;
347
+ }
348
+
349
+ interface HookOutput {
350
+ decision?: "approve" | "block";
351
+ reason?: string;
352
+ }
353
+
354
+ // Read JSON from stdin
355
+ const input: HookInput = JSON.parse(readFileSync(0, "utf-8"));
356
+
357
+ // Your logic here
358
+ if (input.tool_name === "Bash") {
359
+ const command = input.tool_input.command as string;
360
+ if (command.includes("rm -rf /")) {
361
+ console.log(JSON.stringify({ decision: "block", reason: "Dangerous command" }));
362
+ process.exit(0);
363
+ }
364
+ }
365
+
366
+ // Approve by default
367
+ console.log(JSON.stringify({ decision: "approve" }));
221
368
  ```
222
369
 
370
+ Key rules:
371
+ - Read from **stdin** (JSON), write to **stdout** (JSON)
372
+ - Log diagnostics to **stderr** only (stdout must be clean JSON)
373
+ - PreToolUse hooks return `{ "decision": "approve" | "block", "reason"?: string }`
374
+ - PostToolUse/Stop/Notification hooks return informational JSON (not blocking)
375
+
376
+ ### Registering a Custom Hook
377
+
378
+ Add the hook to `src/lib/registry.ts` in the `HOOKS` array, then update `dashboard/src/data.ts` to keep the dashboard in sync.
379
+
223
380
  ## Configuration
224
381
 
225
- Hooks are registered in `~/.claude/settings.json`:
382
+ Hooks are registered in `~/.claude/settings.json` (Claude) or `~/.gemini/settings.json` (Gemini):
226
383
 
227
384
  ```json
228
385
  {
@@ -231,7 +388,7 @@ Hooks are registered in `~/.claude/settings.json`:
231
388
  {
232
389
  "matcher": "Bash",
233
390
  "hooks": [
234
- { "type": "command", "command": "hook-gitguard" }
391
+ { "type": "command", "command": "hooks run gitguard" }
235
392
  ]
236
393
  }
237
394
  ]
@@ -239,29 +396,67 @@ Hooks are registered in `~/.claude/settings.json`:
239
396
  }
240
397
  ```
241
398
 
399
+ Event names are mapped per agent:
400
+
401
+ | Internal Event | Claude Code | Gemini CLI |
402
+ |---------------|-------------|------------|
403
+ | PreToolUse | PreToolUse | BeforeTool |
404
+ | PostToolUse | PostToolUse | AfterTool |
405
+ | Stop | Stop | AfterAgent |
406
+ | Notification | Notification | Notification |
407
+
242
408
  ## Development
243
409
 
244
410
  ```bash
245
- # Install dependencies
411
+ git clone https://github.com/hasna/hooks.git
412
+ cd hooks
246
413
  bun install
247
414
 
248
- # Run CLI in development
249
- bun run dev
415
+ bun run dev # Run CLI in development (interactive mode)
416
+ bun run build # Build CLI (bin/) + library (dist/)
417
+ bun run typecheck # TypeScript type checking
418
+ bun test # Run all tests (592 tests, 1816+ assertions)
250
419
 
251
- # Build
252
- bun run build
420
+ # Dashboard (separate Vite + React 19 app)
421
+ bun run dashboard:dev # Dev server at localhost:5173
422
+ bun run dashboard:build # Production build to dashboard/dist/
423
+ ```
253
424
 
254
- # Type check
255
- bun run typecheck
425
+ ## Architecture
426
+
427
+ ```
428
+ src/
429
+ cli/ Commander.js CLI + React/Ink interactive TUI
430
+ components/ App, Header, CategorySelect, HookSelect, SearchView, DataTable, InstallProgress
431
+ index.tsx CLI entry point (all commands)
432
+ lib/ Core logic
433
+ registry.ts Hook metadata registry (HOOKS array, categories, search)
434
+ installer.ts Hook registration in agent settings files
435
+ profiles.ts Agent profile identity system
436
+ mcp/ MCP server (stdio + SSE transport, 13 tools)
437
+ server.ts Tool definitions and transport setup
438
+ hooks/ Hook runtime test
439
+ index.ts Library re-exports
440
+
441
+ hooks/ 30 hook implementations
442
+ hook-gitguard/
443
+ src/hook.ts Main hook logic (stdin -> stdout)
444
+ package.json
445
+ README.md
446
+ hook-branchprotect/
447
+ hook-checkpoint/
448
+ ...
449
+
450
+ dashboard/ Web dashboard (Vite + React 19 + TailwindCSS 4)
451
+ src/data.ts Static copy of hook metadata (sync with registry.ts)
256
452
  ```
257
453
 
258
- ## Contributing
454
+ ### Build Outputs
259
455
 
260
- 1. Fork the repository
261
- 2. Create a new hook in `hooks/hook-{name}/`
262
- 3. Follow the existing hook patterns
263
- 4. Submit a pull request
456
+ Two separate `bun build` invocations:
457
+ - **CLI binary** (`bin/index.js`) -- Commander.js + Ink/React interactive UI
458
+ - **Library** (`dist/index.js` + `dist/index.d.ts`) -- Registry + installer + profile APIs
264
459
 
265
460
  ## License
266
461
 
267
- Apache-2.0
462
+ [Apache License 2.0](LICENSE)