@dezycro-ai/agent-plugin 2.0.0

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 (55) hide show
  1. package/.claude-plugin/plugin.json +7 -0
  2. package/LICENSE +18 -0
  3. package/PERSONAS.md +309 -0
  4. package/README.md +125 -0
  5. package/bin/resolve-auth.js +112 -0
  6. package/dist/hooks/lib/authoring.d.ts +118 -0
  7. package/dist/hooks/lib/authoring.js +277 -0
  8. package/dist/hooks/lib/config.d.ts +28 -0
  9. package/dist/hooks/lib/config.js +175 -0
  10. package/dist/hooks/lib/controller-match.d.ts +16 -0
  11. package/dist/hooks/lib/controller-match.js +96 -0
  12. package/dist/hooks/lib/coverage.d.ts +17 -0
  13. package/dist/hooks/lib/coverage.js +66 -0
  14. package/dist/hooks/lib/hashing.d.ts +8 -0
  15. package/dist/hooks/lib/hashing.js +49 -0
  16. package/dist/hooks/lib/logging.d.ts +13 -0
  17. package/dist/hooks/lib/logging.js +72 -0
  18. package/dist/hooks/lib/publish-spec.d.ts +17 -0
  19. package/dist/hooks/lib/publish-spec.js +64 -0
  20. package/dist/hooks/lib/quality-gate.d.ts +67 -0
  21. package/dist/hooks/lib/quality-gate.js +187 -0
  22. package/dist/hooks/lib/router.d.ts +32 -0
  23. package/dist/hooks/lib/router.js +717 -0
  24. package/dist/hooks/lib/state.d.ts +34 -0
  25. package/dist/hooks/lib/state.js +238 -0
  26. package/dist/hooks/lib/undo.d.ts +11 -0
  27. package/dist/hooks/lib/undo.js +71 -0
  28. package/dist/hooks/lib/verify.d.ts +28 -0
  29. package/dist/hooks/lib/verify.js +94 -0
  30. package/dist/hooks/lib/workbook.d.ts +21 -0
  31. package/dist/hooks/lib/workbook.js +77 -0
  32. package/dist/hooks/types.d.ts +249 -0
  33. package/dist/hooks/types.js +14 -0
  34. package/hooks/companion-runner +108 -0
  35. package/hooks/hooks.json +74 -0
  36. package/install.js +84 -0
  37. package/package.json +50 -0
  38. package/prompts/existing-work-ranker.md +47 -0
  39. package/prompts/prd-drafter.md +53 -0
  40. package/prompts/prd-question.md +66 -0
  41. package/prompts/trd-context-gate.md +57 -0
  42. package/prompts/trd-discovery.md +71 -0
  43. package/prompts/trd-drafter.md +63 -0
  44. package/prompts/trd-question.md +61 -0
  45. package/prompts/trd-reviewer.md +74 -0
  46. package/prompts/workbook-sweep.md +89 -0
  47. package/setup.js +35 -0
  48. package/skills/author/SKILL.md +285 -0
  49. package/skills/import-features/SKILL.md +428 -0
  50. package/skills/init/SKILL.md +133 -0
  51. package/skills/publish-spec/SKILL.md +101 -0
  52. package/skills/status/SKILL.md +76 -0
  53. package/skills/sync/SKILL.md +76 -0
  54. package/skills/verify/SKILL.md +248 -0
  55. package/skills/work/SKILL.md +201 -0
@@ -0,0 +1,249 @@
1
+ /**
2
+ * types.ts — shared type definitions for Companion V2 hooks.
3
+ *
4
+ * Hook output types match Claude Code's documented schema (captured from
5
+ * the schema error message hit in the V2.0 dogfood — Anthropic does not
6
+ * publish official TS types as of 2026-05). Hook input payload types are
7
+ * partials because Claude Code's payload shape varies per event and we
8
+ * only consume the fields we use.
9
+ *
10
+ * Companion state and config match the on-disk JSON shape in
11
+ * `.dezycro/companion-state.json` and `.dezycro/config.json`.
12
+ */
13
+ export type HookEventName = 'PreToolUse' | 'PostToolUse' | 'UserPromptSubmit' | 'Stop' | 'SessionStart' | 'SessionEnd' | 'PreCompact' | 'Notification' | 'SubagentStop';
14
+ export type Decision = 'approve' | 'block';
15
+ export type PermissionDecision = 'allow' | 'deny' | 'ask' | 'defer';
16
+ /** Fields valid at the top level of every hook output. */
17
+ interface HookOutputCommon {
18
+ continue?: boolean;
19
+ suppressOutput?: boolean;
20
+ stopReason?: string;
21
+ decision?: Decision;
22
+ reason?: string;
23
+ systemMessage?: string;
24
+ permissionDecision?: PermissionDecision;
25
+ }
26
+ /** PreToolUse: also accepts a hookSpecificOutput with PreToolUse fields. */
27
+ export interface PreToolUseHookOutput extends HookOutputCommon {
28
+ hookSpecificOutput?: {
29
+ hookEventName: 'PreToolUse';
30
+ permissionDecision?: PermissionDecision;
31
+ permissionDecisionReason?: string;
32
+ updatedInput?: Record<string, unknown>;
33
+ };
34
+ }
35
+ /** PostToolUse: optional additionalContext to inject into the next turn. */
36
+ export interface PostToolUseHookOutput extends HookOutputCommon {
37
+ hookSpecificOutput?: {
38
+ hookEventName: 'PostToolUse';
39
+ additionalContext?: string;
40
+ };
41
+ }
42
+ /** UserPromptSubmit: additionalContext REQUIRED if hookSpecificOutput is set. */
43
+ export interface UserPromptSubmitHookOutput extends HookOutputCommon {
44
+ hookSpecificOutput?: {
45
+ hookEventName: 'UserPromptSubmit';
46
+ additionalContext: string;
47
+ };
48
+ }
49
+ /** Stop: no hookSpecificOutput. Only top-level fields are valid. */
50
+ export type StopHookOutput = HookOutputCommon;
51
+ /** SessionStart / SessionEnd / etc: top-level only (no hookSpecificOutput). */
52
+ export type SessionStartHookOutput = HookOutputCommon;
53
+ export type AnyHookOutput = PreToolUseHookOutput | PostToolUseHookOutput | UserPromptSubmitHookOutput | StopHookOutput | SessionStartHookOutput;
54
+ export interface PreToolUsePayload {
55
+ tool_name?: string;
56
+ tool_input?: Record<string, unknown>;
57
+ session_id?: string;
58
+ transcript_path?: string;
59
+ }
60
+ export interface PostToolUsePayload {
61
+ tool_name?: string;
62
+ tool_input?: Record<string, unknown>;
63
+ tool_response?: Record<string, unknown>;
64
+ response?: Record<string, unknown>;
65
+ session_id?: string;
66
+ transcript_path?: string;
67
+ }
68
+ export interface UserPromptSubmitPayload {
69
+ prompt?: string;
70
+ user_message?: string;
71
+ message?: string;
72
+ session_id?: string;
73
+ }
74
+ export interface StopPayload {
75
+ session_id?: string;
76
+ transcript_path?: string;
77
+ }
78
+ export interface SessionStartPayload {
79
+ session_id?: string;
80
+ }
81
+ export interface ControllerEdit {
82
+ path: string;
83
+ editedTs: string;
84
+ }
85
+ export interface CoverageSnapshot {
86
+ uncoveredPaths?: number;
87
+ endpointsNotInSpec?: number;
88
+ staleBaselines?: number;
89
+ surfacedTs?: string | null;
90
+ previous?: CoverageSnapshot | null;
91
+ [extra: string]: unknown;
92
+ }
93
+ export interface LastAutoWorkbookMutation {
94
+ /** Bare tool name, e.g. "add_decision" / "update_task" (no mcp__ prefix). */
95
+ tool: string;
96
+ entityId: string | null;
97
+ createdInTurnId: number;
98
+ createdTs: string;
99
+ /** Only set for update_task — the status to restore on undo. */
100
+ priorStatus: string | null;
101
+ }
102
+ export interface CompanionMetrics {
103
+ autoDecisionLogs: number;
104
+ autoIssueLogs: number;
105
+ autoAssumptionLogs: number;
106
+ autoTaskStatusLogs: number;
107
+ undoCount: number;
108
+ verifyAutoRuns: number;
109
+ verifyAutoFailures: number;
110
+ qualityGateFailures: number;
111
+ contextGateFailures: number;
112
+ lockContention: number;
113
+ forceApprovals: number;
114
+ }
115
+ export interface CompanionState {
116
+ schemaVersion: number;
117
+ currentSessionId: string | null;
118
+ authoringMode: boolean;
119
+ authoringDocId: string | null;
120
+ authoringDocType: 'PRD' | 'TRD' | null;
121
+ authoringTranscriptPath: string | null;
122
+ authoringStartedTs: string | null;
123
+ pulseCount: number;
124
+ lastEditTs: string | null;
125
+ needsSpecPublish: boolean;
126
+ controllerEditsSinceLastPublish: ControllerEdit[];
127
+ coverageSnapshot: CoverageSnapshot | null;
128
+ coverageSnapshotTs: string | null;
129
+ coverageFetchPending: boolean;
130
+ lastVerifyCompletionTs: string | null;
131
+ recentDecisionHashes: string[];
132
+ recentIssueHashes: string[];
133
+ recentAssumptionHashes: string[];
134
+ lastAutoWorkbookMutation: LastAutoWorkbookMutation | null;
135
+ sessionAutoLogCount: number;
136
+ turnAutoLogCount: number;
137
+ currentTurnId: number;
138
+ suppressWorkbookUpdatesUntil: string | null;
139
+ pendingWorkbookSweep: boolean;
140
+ pendingWorkbookSweepReason: string | null;
141
+ lastSeenVerifyCompletionTs: string | null;
142
+ metrics: CompanionMetrics;
143
+ }
144
+ /**
145
+ * Patch shape for state.patch() — partial state + convenience "push" keys
146
+ * that append to the corresponding ring buffer instead of replacing it.
147
+ * `metrics` overrides the full-CompanionState definition to be partial.
148
+ */
149
+ export type StatePatch = Partial<Omit<CompanionState, 'metrics'>> & {
150
+ metrics?: Partial<CompanionMetrics>;
151
+ pushDecisionHash?: string;
152
+ pushIssueHash?: string;
153
+ pushAssumptionHash?: string;
154
+ pushControllerEdit?: ControllerEdit;
155
+ };
156
+ export interface PostEditPulseConfig {
157
+ enabled?: boolean;
158
+ threshold?: number;
159
+ }
160
+ export interface AutoVerifyConfig {
161
+ preTaskDone?: boolean;
162
+ prePush?: boolean;
163
+ postEditPulse?: PostEditPulseConfig;
164
+ autoPublishSpecOnControllerChange?: boolean;
165
+ controllerPaths?: string[];
166
+ verifyTimeoutSeconds?: number;
167
+ publishSpecTimeoutSeconds?: number;
168
+ }
169
+ export interface ThresholdOverrides {
170
+ decision?: number | null;
171
+ issue?: number | null;
172
+ assumption?: number | null;
173
+ taskStatus?: number | null;
174
+ }
175
+ export type PresetName = 'conservative' | 'balanced' | 'aggressive';
176
+ export interface AutoWorkbookUpdatesConfig {
177
+ enabled?: boolean;
178
+ preset?: PresetName;
179
+ maxAutoLogsPerTurn?: number;
180
+ maxAutoLogsPerSession?: number;
181
+ undoWindow?: 'next-turn' | string;
182
+ thresholds?: ThresholdOverrides;
183
+ trivialTurnCharCutoff?: number;
184
+ hashRingBufferSize?: number;
185
+ }
186
+ export interface CoverageNudgesConfig {
187
+ enabled?: boolean;
188
+ staleBaselineDays?: number;
189
+ fetchMode?: 'async' | 'sync' | 'off';
190
+ }
191
+ export interface AuthoringConfig {
192
+ defaultDepth?: 'adaptive' | 'shallow' | 'deep';
193
+ existingWorkScan?: boolean;
194
+ qualityGate?: {
195
+ enabled?: boolean;
196
+ };
197
+ minViableContextGate?: {
198
+ enabled?: boolean;
199
+ };
200
+ }
201
+ export interface CompanionConfig {
202
+ companion?: {
203
+ autoVerify?: AutoVerifyConfig;
204
+ autoWorkbookUpdates?: AutoWorkbookUpdatesConfig;
205
+ coverageNudges?: CoverageNudgesConfig;
206
+ authoring?: AuthoringConfig;
207
+ safeMode?: boolean;
208
+ };
209
+ }
210
+ export interface Thresholds {
211
+ decision: number;
212
+ issue: number;
213
+ assumption: number;
214
+ taskStatus: number;
215
+ }
216
+ export interface ActiveFeature {
217
+ featureId: string | null;
218
+ featureName: string | null;
219
+ }
220
+ export interface SweepDirectiveOpts {
221
+ thresholds: Thresholds;
222
+ perTurnSlotsLeft: number;
223
+ perSessionSlotsLeft: number;
224
+ recentHashes: string[];
225
+ activeFeatureId: string | null;
226
+ activeFeatureName: string | null;
227
+ }
228
+ export interface ReversalDirective {
229
+ tool: 'update_decision' | 'update_issue' | 'update_task';
230
+ args: Record<string, unknown>;
231
+ humanLine: string;
232
+ }
233
+ export type HookEvent = 'session-start' | 'user-prompt-submit' | 'pre-tool-use' | 'post-tool-use' | 'stop' | 'state' | 'authoring-state';
234
+ export interface RouterInvocation {
235
+ event: HookEvent | null;
236
+ route: string | null;
237
+ payload: unknown;
238
+ flags: Record<string, string | boolean>;
239
+ }
240
+ export interface QualityGateBlockingIssue {
241
+ section: string;
242
+ issue: string;
243
+ required_fix: string;
244
+ }
245
+ export interface QualityGateResult {
246
+ passed: boolean;
247
+ blockingIssues: QualityGateBlockingIssue[];
248
+ }
249
+ export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * types.ts — shared type definitions for Companion V2 hooks.
3
+ *
4
+ * Hook output types match Claude Code's documented schema (captured from
5
+ * the schema error message hit in the V2.0 dogfood — Anthropic does not
6
+ * publish official TS types as of 2026-05). Hook input payload types are
7
+ * partials because Claude Code's payload shape varies per event and we
8
+ * only consume the fields we use.
9
+ *
10
+ * Companion state and config match the on-disk JSON shape in
11
+ * `.dezycro/companion-state.json` and `.dezycro/config.json`.
12
+ */
13
+ 'use strict';
14
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * companion-runner — single entry point for all Dezycro Companion hooks.
4
+ *
5
+ * Invocation contract (TRD §4.2):
6
+ * companion-runner <event> [--route=<route>] [--flag[=value] ...]
7
+ *
8
+ * where <event> is one of:
9
+ * session-start | user-prompt-submit | pre-tool-use | post-tool-use | stop
10
+ * | state | authoring-state
11
+ *
12
+ * Reads the hook payload from stdin (JSON, when applicable), dispatches to
13
+ * lib/router.js, and exits with the appropriate status code / stdout payload
14
+ * per Claude Code's hook contract.
15
+ *
16
+ * Live routes:
17
+ * - `pre-tool-use --route=update_task` (pillar 1; TRD §5.1)
18
+ * - `pre-tool-use --route=change_document_status` (pillar 5; TRD §5.5.3)
19
+ * - `post-tool-use --route=edit` (pillars 1+2; TRD §5.1+§5.2)
20
+ *
21
+ * Top-level skill-facing subcommands (`state`, `authoring-state`) do NOT read
22
+ * stdin — they're invoked synchronously from skill markdown via the Bash tool.
23
+ *
24
+ * Other routes exit 0 silently (no-op) so the runner can be wired up via
25
+ * settings.json without destabilizing existing sessions.
26
+ */
27
+
28
+ 'use strict';
29
+
30
+ const router = require('../dist/hooks/lib/router');
31
+
32
+ const SKILL_INVOKED_SUBCOMMANDS = new Set(['state', 'authoring-state']);
33
+
34
+ function parseArgv(argv) {
35
+ const positional = [];
36
+ let route = null;
37
+ const flags = {};
38
+ // argv[0] = node, argv[1] = this script
39
+ for (let i = 2; i < argv.length; i++) {
40
+ const arg = argv[i];
41
+ if (arg.startsWith('--route=')) {
42
+ route = arg.slice('--route='.length);
43
+ } else if (arg === '--route') {
44
+ route = argv[++i] || null;
45
+ } else if (arg.startsWith('--')) {
46
+ // Generic flag parsing: --key=value OR --key (boolean true).
47
+ const body = arg.slice(2);
48
+ const eq = body.indexOf('=');
49
+ if (eq >= 0) {
50
+ flags[body.slice(0, eq)] = body.slice(eq + 1);
51
+ } else {
52
+ flags[body] = true;
53
+ }
54
+ } else {
55
+ positional.push(arg);
56
+ }
57
+ }
58
+ const event = positional[0] || null;
59
+ return { event, route, flags };
60
+ }
61
+
62
+ async function readStdin() {
63
+ if (process.stdin.isTTY) return null;
64
+ return new Promise((resolve) => {
65
+ let data = '';
66
+ let resolved = false;
67
+ const finish = () => {
68
+ if (resolved) return;
69
+ resolved = true;
70
+ if (!data.trim()) return resolve(null);
71
+ try {
72
+ resolve(JSON.parse(data));
73
+ } catch (_err) {
74
+ resolve(null);
75
+ }
76
+ };
77
+ process.stdin.setEncoding('utf8');
78
+ process.stdin.on('data', (chunk) => {
79
+ data += chunk;
80
+ });
81
+ process.stdin.on('end', finish);
82
+ process.stdin.on('error', finish);
83
+ // Hard ceiling so we never hang.
84
+ setTimeout(finish, 250);
85
+ });
86
+ }
87
+
88
+ (async function main() {
89
+ try {
90
+ const { event, route, flags } = parseArgv(process.argv);
91
+ if (!event) {
92
+ // Unknown invocation; exit silently rather than spam stderr.
93
+ process.exit(0);
94
+ }
95
+ // Skill-facing subcommands are invoked synchronously via the Bash tool and
96
+ // don't carry a stdin payload — skip the read so we don't burn 250ms
97
+ // waiting on a pipe that isn't there.
98
+ const skipStdin = SKILL_INVOKED_SUBCOMMANDS.has(event);
99
+ const payload = skipStdin ? null : await readStdin();
100
+ const code = await router.dispatch({ event, route, payload, flags });
101
+ process.exit(typeof code === 'number' ? code : 0);
102
+ } catch (_err) {
103
+ // Per TRD §4.1: per-route try/catch boundaries. A runner crash must never
104
+ // break the user's session — exit 0 and rely on lib/logging.js for
105
+ // diagnostics in real routes.
106
+ process.exit(0);
107
+ }
108
+ })();
@@ -0,0 +1,74 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/companion-runner session-start"
9
+ }
10
+ ]
11
+ }
12
+ ],
13
+ "UserPromptSubmit": [
14
+ {
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/companion-runner user-prompt-submit"
19
+ }
20
+ ]
21
+ }
22
+ ],
23
+ "PreToolUse": [
24
+ {
25
+ "matcher": "mcp__dezycro__update_task|mcp__dezycro-dev__update_task",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/companion-runner pre-tool-use --route=update_task"
30
+ }
31
+ ]
32
+ },
33
+ {
34
+ "matcher": "mcp__dezycro__change_document_status|mcp__dezycro-dev__change_document_status",
35
+ "hooks": [
36
+ {
37
+ "type": "command",
38
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/companion-runner pre-tool-use --route=change_document_status"
39
+ }
40
+ ]
41
+ }
42
+ ],
43
+ "PostToolUse": [
44
+ {
45
+ "matcher": "Edit|Write|MultiEdit",
46
+ "hooks": [
47
+ {
48
+ "type": "command",
49
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/companion-runner post-tool-use --route=edit"
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "matcher": "mcp__dezycro__add_decision|mcp__dezycro-dev__add_decision|mcp__dezycro__add_issue|mcp__dezycro-dev__add_issue|mcp__dezycro__add_assumption|mcp__dezycro-dev__add_assumption|mcp__dezycro__update_task|mcp__dezycro-dev__update_task",
55
+ "hooks": [
56
+ {
57
+ "type": "command",
58
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/companion-runner post-tool-use --route=workbook_mutation"
59
+ }
60
+ ]
61
+ }
62
+ ],
63
+ "Stop": [
64
+ {
65
+ "hooks": [
66
+ {
67
+ "type": "command",
68
+ "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/companion-runner stop"
69
+ }
70
+ ]
71
+ }
72
+ ]
73
+ }
74
+ }
package/install.js ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+
7
+ const HOME = os.homedir();
8
+ const commandsDir = path.join(HOME, ".claude", "commands", "dezycro");
9
+ const skillsDir = path.join(__dirname, "skills");
10
+ const settingsPath = path.join(HOME, ".claude", "settings.json");
11
+ const hooksJsonPath = path.join(__dirname, "hooks", "hooks.json");
12
+ const runnerPath = path.join(__dirname, "hooks", "companion-runner");
13
+
14
+ fs.mkdirSync(commandsDir, { recursive: true });
15
+
16
+ const skills = fs
17
+ .readdirSync(skillsDir, { withFileTypes: true })
18
+ .filter((d) => d.isDirectory() && fs.existsSync(path.join(skillsDir, d.name, "SKILL.md")))
19
+ .map((d) => d.name);
20
+
21
+ for (const name of skills) {
22
+ const src = path.join(skillsDir, name, "SKILL.md");
23
+ const dst = path.join(commandsDir, `${name}.md`);
24
+ fs.copyFileSync(src, dst);
25
+ }
26
+
27
+ let hooksInstalled = false;
28
+ let hooksAlreadyPresent = false;
29
+ try {
30
+ const hooksConfig = JSON.parse(fs.readFileSync(hooksJsonPath, "utf8"));
31
+ const incoming = inlinePluginRoot(hooksConfig.hooks || {}, __dirname);
32
+
33
+ let existing = { hooks: {} };
34
+ if (fs.existsSync(settingsPath)) {
35
+ try { existing = JSON.parse(fs.readFileSync(settingsPath, "utf8")); }
36
+ catch { existing = { hooks: {} }; }
37
+ }
38
+ if (!existing.hooks) existing.hooks = {};
39
+
40
+ const before = JSON.stringify(existing.hooks);
41
+ for (const event of Object.keys(incoming)) {
42
+ const existingEntries = Array.isArray(existing.hooks[event]) ? existing.hooks[event] : [];
43
+ const filtered = existingEntries.filter((e) => !isDezycroEntry(e));
44
+ existing.hooks[event] = [...filtered, ...incoming[event]];
45
+ }
46
+ const after = JSON.stringify(existing.hooks);
47
+
48
+ if (before === after) {
49
+ hooksAlreadyPresent = true;
50
+ } else {
51
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
52
+ fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
53
+ hooksInstalled = true;
54
+ }
55
+
56
+ try { fs.chmodSync(runnerPath, 0o755); } catch {}
57
+ } catch (err) {
58
+ console.log(`\n ⚠ Could not install V2 hooks automatically: ${err.message}`);
59
+ console.log(` Hooks config: ${hooksJsonPath}`);
60
+ console.log(` Merge it into ${settingsPath} manually, or install as a Claude Code plugin.`);
61
+ }
62
+
63
+ console.log(`\n ✓ Dezycro commands installed (${skills.length}) to ~/.claude/commands/dezycro/`);
64
+ console.log(` ${skills.map((s) => `/dezycro:${s}`).join(", ")}`);
65
+ if (hooksInstalled) {
66
+ console.log(` ✓ Companion V2 hooks merged into ~/.claude/settings.json`);
67
+ } else if (hooksAlreadyPresent) {
68
+ console.log(` ✓ Companion V2 hooks already present in ~/.claude/settings.json`);
69
+ }
70
+ console.log(`\n Next: npx dezycro-setup`);
71
+ console.log(` Then: /dezycro:import-features\n`);
72
+
73
+ function inlinePluginRoot(hooks, root) {
74
+ return JSON.parse(JSON.stringify(hooks, (k, v) => {
75
+ if (typeof v === "string") return v.replace(/"?\$\{CLAUDE_PLUGIN_ROOT\}"?/g, root);
76
+ return v;
77
+ }));
78
+ }
79
+
80
+ function isDezycroEntry(entry) {
81
+ if (!entry || typeof entry !== "object") return false;
82
+ const hooks = Array.isArray(entry.hooks) ? entry.hooks : [];
83
+ return hooks.some((h) => h && typeof h.command === "string" && h.command.includes("/hooks/companion-runner"));
84
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@dezycro-ai/agent-plugin",
3
+ "version": "2.0.0",
4
+ "description": "Official Dezycro plugin for AI coding agents (Claude Code today, more to come) — reverse-engineer your codebase into features, PRDs, TRDs, and live verification.",
5
+ "bin": {
6
+ "dezycro-setup": "./setup.js",
7
+ "dezycro-resolve-auth": "./bin/resolve-auth.js"
8
+ },
9
+ "keywords": [
10
+ "dezycro",
11
+ "claude-code",
12
+ "ai-agents",
13
+ "prd",
14
+ "trd",
15
+ "verification"
16
+ ],
17
+ "license": "UNLICENSED",
18
+ "private": false,
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "homepage": "https://docs.dezycro.ai/claude-code/install/",
23
+ "bugs": {
24
+ "url": "https://docs.dezycro.ai/claude-code/install/"
25
+ },
26
+ "files": [
27
+ ".claude-plugin/**/*",
28
+ "dist/**/*",
29
+ "hooks/**/*",
30
+ "prompts/**/*",
31
+ "skills/**/*",
32
+ "bin/**/*",
33
+ "install.js",
34
+ "setup.js",
35
+ "README.md",
36
+ "PERSONAS.md",
37
+ "LICENSE"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc",
41
+ "prepare": "[ -d src ] && tsc || true",
42
+ "prepublishOnly": "npm run build",
43
+ "postinstall": "node install.js",
44
+ "setup": "node setup.js"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^25.9.1",
48
+ "typescript": "^5.6.0"
49
+ }
50
+ }
@@ -0,0 +1,47 @@
1
+ <!--
2
+ P-2 Existing-Work Ranker
3
+ Version: 1
4
+ Surface: PRD authoring SCAN_EXISTING_WORK step
5
+ Source: Dezycro Companion TRD §6.2
6
+ -->
7
+
8
+ # P-2 Existing-Work Ranker
9
+
10
+ ## System prompt
11
+
12
+ ```text
13
+ You are a feature-deduplication assistant for Dezycro.
14
+
15
+ Given a new feature seed paragraph and a list of existing feature search results, return the top 3 most adjacent existing features.
16
+
17
+ Adjacent means: scope overlap, plausible extension target, or close sibling.
18
+
19
+ Return STRICT JSON. No prose.
20
+ ```
21
+
22
+ ## User template
23
+
24
+ ```text
25
+ Seed:
26
+ {{seed_paragraph}}
27
+
28
+ Search results:
29
+ {{search_results_json}}
30
+
31
+ Produce the JSON output.
32
+ ```
33
+
34
+ ## Output schema
35
+
36
+ ```json
37
+ {
38
+ "top": [
39
+ {
40
+ "feature_id": "string",
41
+ "feature_name": "string",
42
+ "adjacency_reason": "string",
43
+ "adjacency_score": 0.0
44
+ }
45
+ ]
46
+ }
47
+ ```
@@ -0,0 +1,53 @@
1
+ <!--
2
+ P-4 PRD Drafter
3
+ Version: 1
4
+ Surface: PRD authoring DRAFT and revise steps
5
+ Source: Dezycro Companion TRD §6.4
6
+ -->
7
+
8
+ # P-4 PRD Drafter
9
+
10
+ ## System prompt
11
+
12
+ ```text
13
+ You are a PRD author for Dezycro. Write in the Dezycro PRD style:
14
+
15
+ Sections (in this order):
16
+ - # <Feature Name>
17
+ - ## Overview (with "### Why it matters" and "### Who uses it" subsections)
18
+ - ## Pillars (table + brief)
19
+ - ## Pillar N: <name> sections with numbered flows (Flow N.1, N.2, …)
20
+ - ## Settings (JSON block in .dezycro/config.json schema)
21
+ - ## Business Rules (bulleted)
22
+ - ## Edge Cases and Error Scenarios (EC-01, EC-02, …)
23
+ - ## Dependencies
24
+ - ## Out of Scope (V1)
25
+
26
+ Dense, reviewable, concrete. No placeholders, no TBDs. Every claim should be grounded in the seed, QA transcript, or adjacent-features context.
27
+
28
+ When a number of pillars or flows could explode, cap at 8 pillars and 6 flows per pillar; overflow goes into "Out of Scope (V1)" or "Future Pillars."
29
+
30
+ Output: pure Markdown. No fences around the whole document.
31
+ ```
32
+
33
+ ## User template
34
+
35
+ ```text
36
+ Seed:
37
+ {{seed}}
38
+
39
+ QA transcript:
40
+ {{qa_transcript_json}}
41
+
42
+ Adjacent features:
43
+ {{adjacent_features_json}}
44
+
45
+ Existing PRD (if revising):
46
+ {{existing_content_or_null}}
47
+
48
+ Write the PRD now.
49
+ ```
50
+
51
+ ## Output schema
52
+
53
+ Free Markdown, validated only by length (>= 500 chars) and required section headings present.