Skip to content

Commit f910973

Browse files
masseaterclaude
andcommitted
feat: add CLI entry points for doc-engine and repository-lint
- Add docs-tools CLI (gen, check, check-list subcommands) to doc-engine - Add repository-lint CLI (check-lockfile subcommand) - Replace inline bun -e scripts with CLI commands in root package.json - Add check:lockfile to pre-push hook - Bump plugin versions (code-review, github-workflow, research) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e15581a commit f910973

18 files changed

Lines changed: 1442 additions & 125 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"name": "code-review",
2121
"source": "./plugins/code-review",
2222
"description": "PR・コードレビューワークフロー支援",
23-
"version": "0.2.6"
23+
"version": "0.2.7"
2424
},
2525
{
2626
"name": "context",
@@ -56,7 +56,7 @@
5656
"name": "github-workflow",
5757
"source": "./plugins/github-workflow",
5858
"description": "Git/GitHub ワークフロー支援 — Stop 時にブランチ状態とコンフリクトを通知",
59-
"version": "0.0.11"
59+
"version": "0.0.12"
6060
},
6161
{
6262
"name": "mutils",
@@ -86,7 +86,7 @@
8686
"name": "research",
8787
"source": "./plugins/research",
8888
"description": "調査・分析ツール(画像解析、ライブラリ調査、Figma連携)",
89-
"version": "0.1.10"
89+
"version": "0.1.11"
9090
},
9191
{
9292
"name": "sdd",

.husky/pre-push

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set -eu
44
bun run build
55
bun run typecheck
66
bun run check
7+
bun run check:lockfile
78
bun run test
89
bun run knip
910

AGENTS.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ Claude Codeプラグインのマーケットプレイスリポジトリ。全プ
4141

4242
<!-- BEGIN:package-list (auto-generated, do not edit) -->
4343

44-
| パッケージ | 説明 |
45-
| -------------------------------- | ------------------------------------------------------------------------------------------------------------ |
46-
| **@r_masseater/cc-plugin-lib** | Common library for Claude Code plugins - logging and error handling utilities |
47-
| **@r_masseater/doc-engine** | Documentation generation and validation engine — JSDoc extraction, Markdown generation, and plugin list sync |
48-
| **@r_masseater/repository-lint** | Repository linting rules — hook structure, workspace dependency constraints, and version-bump enforcement |
49-
| **@repo/ops-harbor-core** | Core library for Ops Harbor — alerts, work-item filtering, SQLite helpers, and shared type definitions |
50-
| **@repo/ts-config** | Shared TypeScript configuration presets — base, app, plugin, and library |
51-
| **@repo/vitest-config** | Shared Vitest configuration with standardized coverage thresholds for the monorepo |
44+
| パッケージ | 説明 |
45+
| ------------------------------ | ------------------------------------------------------------------------------------------------------------ |
46+
| **@r_masseater/cc-plugin-lib** | Common library for Claude Code plugins - logging and error handling utilities |
47+
| **@r_masseater/doc-engine** | Documentation generation and validation engine — JSDoc extraction, Markdown generation, and plugin list sync |
48+
| **@repo/ops-harbor-core** | Core library for Ops Harbor — alerts, work-item filtering, SQLite helpers, and shared type definitions |
49+
| **@repo/repository-lint** | Repository linting rules — hook structure, workspace dependency constraints, and version-bump enforcement |
50+
| **@repo/ts-config** | Shared TypeScript configuration presets — base, app, plugin, and library |
51+
| **@repo/vitest-config** | Shared Vitest configuration with standardized coverage thresholds for the monorepo |
5252

5353
<!-- END:package-list -->
5454

bun.lock

Lines changed: 15 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
"test:coverage": "turbo run test -- --coverage",
1515
"typecheck": "turbo run typecheck",
1616
"prepare": "husky",
17-
"check:docs": "bun -e \"import{check}from'@r_masseater/doc-engine';await check()\"",
18-
"docs": "bun -e \"import{generate}from'@r_masseater/doc-engine';await generate()\"",
19-
"check:plugin-list": "bun -e \"import{checkList}from'@r_masseater/doc-engine';await checkList()\"",
20-
"hooks:plugin-list-sync": "bun -e \"import{generate}from'@r_masseater/doc-engine';await generate()\"",
21-
"knip": "knip"
17+
"check:docs": "docs-tools check",
18+
"docs": "docs-tools gen",
19+
"check:plugin-list": "docs-tools check-list",
20+
"hooks:plugin-list-sync": "docs-tools gen",
21+
"knip": "knip",
22+
"check:lockfile": "repository-lint check-lockfile"
2223
},
2324
"dependencies": {
2425
"@r_masseater/cc-plugin-lib": "workspace:*",
@@ -30,6 +31,7 @@
3031
"@feature-sliced/steiger-plugin": "^0.5.7",
3132
"@r_masseater/doc-engine": "workspace:*",
3233
"@r_masseater/ops-harbor": "workspace:*",
34+
"@repo/repository-lint": "workspace:*",
3335
"@tailwindcss/vite": "^4.2.2",
3436
"@testing-library/react": "^16.3.2",
3537
"@types/bun": "^1.3.4",

packages/doc-engine/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
"name": "@r_masseater/doc-engine",
33
"private": true,
44
"description": "Documentation generation and validation engine — JSDoc extraction, Markdown generation, and plugin list sync",
5+
"bin": {
6+
"docs-tools": "./src/cli.ts"
7+
},
58
"type": "module",
69
"exports": {
710
".": "./src/index.ts"
811
},
912
"dependencies": {
13+
"citty": "^0.2.0",
1014
"typescript": "latest"
1115
}
1216
}

packages/doc-engine/src/cli.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bun
2+
import { defineCommand, runMain } from "citty";
3+
import { check, checkList, generate } from "./index.ts";
4+
5+
const gen = defineCommand({
6+
meta: { name: "gen", description: "Generate docs and sync plugin lists" },
7+
async run() {
8+
await generate();
9+
},
10+
});
11+
12+
const checkCmd = defineCommand({
13+
meta: { name: "check", description: "Validate JSDoc on exported symbols" },
14+
async run() {
15+
await check();
16+
},
17+
});
18+
19+
const checkListCmd = defineCommand({
20+
meta: { name: "check-list", description: "Check plugin list is in sync" },
21+
async run() {
22+
await checkList();
23+
},
24+
});
25+
26+
const main = defineCommand({
27+
meta: { name: "docs-tools", description: "Documentation tools CLI" },
28+
subCommands: { gen, check: checkCmd, "check-list": checkListCmd },
29+
});
30+
31+
runMain(main);

packages/repository-lint/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
{
2-
"name": "@r_masseater/repository-lint",
2+
"name": "@repo/repository-lint",
33
"private": true,
44
"description": "Repository linting rules — hook structure, workspace dependency constraints, and version-bump enforcement",
5+
"bin": {
6+
"repository-lint": "./src/cli.ts"
7+
},
58
"type": "module",
69
"exports": {
710
".": "./src/index.ts"
811
},
912
"scripts": {
1013
"test": "vitest run"
1114
},
15+
"dependencies": {
16+
"citty": "^0.2.0"
17+
},
1218
"devDependencies": {
1319
"@humanwhocodes/momoa": "^3.3.10",
1420
"@repo/vitest-config": "*",
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2+
import { tmpdir } from "node:os";
3+
import { join } from "node:path";
4+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
5+
import { findMissingDeps, parseLockfileDeps } from "./check-lockfile-sync.ts";
6+
7+
function makeLockfile(deps: Record<string, string>): string {
8+
const depsEntries = Object.entries(deps)
9+
.map(([k, v]) => ` "${k}": "${v}",`)
10+
.join("\n");
11+
return [
12+
"{",
13+
' "lockfileVersion": 1,',
14+
' "workspaces": {',
15+
' "": {',
16+
' "dependencies": {',
17+
depsEntries,
18+
" },",
19+
" },",
20+
" },",
21+
' "packages": {}',
22+
"}",
23+
].join("\n");
24+
}
25+
26+
describe("parseLockfileDeps", () => {
27+
test("extracts dependency names from lockfile content", () => {
28+
const content = makeLockfile({
29+
"@r_masseater/cc-plugin-lib": "0.0.3",
30+
"cc-hooks-ts": "latest",
31+
yaml: "^2.8.3",
32+
});
33+
const deps = parseLockfileDeps(content);
34+
expect(deps).toContain("@r_masseater/cc-plugin-lib");
35+
expect(deps).toContain("cc-hooks-ts");
36+
expect(deps).toContain("yaml");
37+
});
38+
39+
test("handles scoped packages", () => {
40+
const content = makeLockfile({ "@google/genai": "^1.38.0" });
41+
expect(parseLockfileDeps(content)).toContain("@google/genai");
42+
});
43+
44+
test("returns empty array for invalid content", () => {
45+
expect(parseLockfileDeps("not json at all {{{")).toStrictEqual([]);
46+
});
47+
48+
test("returns empty array for empty dependencies", () => {
49+
const content = makeLockfile({});
50+
expect(parseLockfileDeps(content)).toStrictEqual([]);
51+
});
52+
53+
test("handles lockfile without packages section", () => {
54+
const content = [
55+
"{",
56+
' "lockfileVersion": 1,',
57+
' "workspaces": {',
58+
' "": {',
59+
' "dependencies": {',
60+
' "yaml": "^2.8.3",',
61+
" },",
62+
" },",
63+
" }",
64+
"}",
65+
].join("\n");
66+
expect(parseLockfileDeps(content)).toContain("yaml");
67+
});
68+
});
69+
70+
describe("findMissingDeps", () => {
71+
let pluginsDir: string;
72+
73+
beforeEach(() => {
74+
pluginsDir = join(tmpdir(), `lockfile-sync-test-${Date.now()}`);
75+
mkdirSync(pluginsDir, { recursive: true });
76+
});
77+
78+
afterEach(() => {
79+
rmSync(pluginsDir, { recursive: true, force: true });
80+
});
81+
82+
test("returns empty when all deps are in lockfile", () => {
83+
const pluginDir = join(pluginsDir, "my-plugin");
84+
mkdirSync(pluginDir);
85+
writeFileSync(
86+
join(pluginDir, "package.json"),
87+
JSON.stringify({ dependencies: { yaml: "^2.8.3" } }),
88+
);
89+
writeFileSync(join(pluginDir, "bun.lock"), makeLockfile({ yaml: "^2.8.3" }));
90+
91+
expect(findMissingDeps(pluginsDir)).toStrictEqual([]);
92+
});
93+
94+
test("reports missing dep", () => {
95+
const pluginDir = join(pluginsDir, "my-plugin");
96+
mkdirSync(pluginDir);
97+
writeFileSync(
98+
join(pluginDir, "package.json"),
99+
JSON.stringify({ dependencies: { yaml: "^2.8.3", valibot: "^1.0.0" } }),
100+
);
101+
writeFileSync(join(pluginDir, "bun.lock"), makeLockfile({ yaml: "^2.8.3" }));
102+
103+
const missing = findMissingDeps(pluginsDir);
104+
expect(missing).toHaveLength(1);
105+
expect(missing[0]).toStrictEqual({ plugin: "my-plugin", dep: "valibot" });
106+
});
107+
108+
test("skips plugins without lockfile", () => {
109+
const pluginDir = join(pluginsDir, "no-lock");
110+
mkdirSync(pluginDir);
111+
writeFileSync(
112+
join(pluginDir, "package.json"),
113+
JSON.stringify({ dependencies: { yaml: "^2.8.3" } }),
114+
);
115+
// No bun.lock
116+
117+
expect(findMissingDeps(pluginsDir)).toStrictEqual([]);
118+
});
119+
120+
test("skips plugins without dependencies", () => {
121+
const pluginDir = join(pluginsDir, "no-deps");
122+
mkdirSync(pluginDir);
123+
writeFileSync(join(pluginDir, "package.json"), JSON.stringify({}));
124+
writeFileSync(join(pluginDir, "bun.lock"), makeLockfile({}));
125+
126+
expect(findMissingDeps(pluginsDir)).toStrictEqual([]);
127+
});
128+
129+
test("detects scoped package missing from lockfile", () => {
130+
const pluginDir = join(pluginsDir, "scoped");
131+
mkdirSync(pluginDir);
132+
writeFileSync(
133+
join(pluginDir, "package.json"),
134+
JSON.stringify({ dependencies: { "@scope/pkg": "^1.0.0" } }),
135+
);
136+
writeFileSync(join(pluginDir, "bun.lock"), makeLockfile({}));
137+
138+
const missing = findMissingDeps(pluginsDir);
139+
expect(missing).toHaveLength(1);
140+
expect(missing[0]).toStrictEqual({ plugin: "scoped", dep: "@scope/pkg" });
141+
});
142+
143+
test("skips plugin with unparseable lockfile", () => {
144+
const pluginDir = join(pluginsDir, "broken");
145+
mkdirSync(pluginDir);
146+
writeFileSync(
147+
join(pluginDir, "package.json"),
148+
JSON.stringify({ dependencies: { yaml: "^2.8.3" } }),
149+
);
150+
writeFileSync(join(pluginDir, "bun.lock"), "completely invalid {{{");
151+
152+
// parseLockfileDeps returns [] → all deps appear missing
153+
const missing = findMissingDeps(pluginsDir);
154+
expect(missing).toHaveLength(1);
155+
expect(missing[0]!.dep).toBe("yaml");
156+
});
157+
158+
test("returns empty for non-existent plugins directory", () => {
159+
expect(findMissingDeps("/nonexistent/path")).toStrictEqual([]);
160+
});
161+
});

0 commit comments

Comments
 (0)