@planu/cli 1.0.11 → 1.1.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 (94) hide show
  1. package/dist/config/domain-bundles.json +134 -0
  2. package/dist/config/license-plans.json +7 -1
  3. package/dist/engine/bundle-installer.d.ts +24 -0
  4. package/dist/engine/bundle-installer.d.ts.map +1 -0
  5. package/dist/engine/bundle-installer.js +131 -0
  6. package/dist/engine/bundle-installer.js.map +1 -0
  7. package/dist/engine/git-hook-injector.d.ts +5 -0
  8. package/dist/engine/git-hook-injector.d.ts.map +1 -0
  9. package/dist/engine/git-hook-injector.js +86 -0
  10. package/dist/engine/git-hook-injector.js.map +1 -0
  11. package/dist/engine/merge-risk-assessor/analyzers.d.ts +22 -0
  12. package/dist/engine/merge-risk-assessor/analyzers.d.ts.map +1 -0
  13. package/dist/engine/merge-risk-assessor/analyzers.js +150 -0
  14. package/dist/engine/merge-risk-assessor/analyzers.js.map +1 -0
  15. package/dist/engine/merge-risk-assessor/git-diff-parser.d.ts +16 -0
  16. package/dist/engine/merge-risk-assessor/git-diff-parser.d.ts.map +1 -0
  17. package/dist/engine/merge-risk-assessor/git-diff-parser.js +48 -0
  18. package/dist/engine/merge-risk-assessor/git-diff-parser.js.map +1 -0
  19. package/dist/engine/merge-risk-assessor/index.d.ts +7 -0
  20. package/dist/engine/merge-risk-assessor/index.d.ts.map +1 -0
  21. package/dist/engine/merge-risk-assessor/index.js +98 -0
  22. package/dist/engine/merge-risk-assessor/index.js.map +1 -0
  23. package/dist/engine/spec-prompt-generator.d.ts +12 -0
  24. package/dist/engine/spec-prompt-generator.d.ts.map +1 -0
  25. package/dist/engine/spec-prompt-generator.js +69 -0
  26. package/dist/engine/spec-prompt-generator.js.map +1 -0
  27. package/dist/engine/telemetry/health-checker.d.ts +5 -0
  28. package/dist/engine/telemetry/health-checker.d.ts.map +1 -0
  29. package/dist/engine/telemetry/health-checker.js +297 -0
  30. package/dist/engine/telemetry/health-checker.js.map +1 -0
  31. package/dist/engine/telemetry/index.d.ts +1 -0
  32. package/dist/engine/telemetry/index.d.ts.map +1 -1
  33. package/dist/engine/telemetry/index.js +1 -0
  34. package/dist/engine/telemetry/index.js.map +1 -1
  35. package/dist/index.js +12 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/tools/domain-bundle-handler.d.ts +37 -0
  38. package/dist/tools/domain-bundle-handler.d.ts.map +1 -0
  39. package/dist/tools/domain-bundle-handler.js +84 -0
  40. package/dist/tools/domain-bundle-handler.js.map +1 -0
  41. package/dist/tools/init-project/handler.d.ts.map +1 -1
  42. package/dist/tools/init-project/handler.js +2 -1
  43. package/dist/tools/init-project/handler.js.map +1 -1
  44. package/dist/tools/init-project/scaffold-writer.d.ts +1 -0
  45. package/dist/tools/init-project/scaffold-writer.d.ts.map +1 -1
  46. package/dist/tools/init-project/scaffold-writer.js +11 -0
  47. package/dist/tools/init-project/scaffold-writer.js.map +1 -1
  48. package/dist/tools/merge-risk-handler.d.ts +3 -0
  49. package/dist/tools/merge-risk-handler.d.ts.map +1 -0
  50. package/dist/tools/merge-risk-handler.js +83 -0
  51. package/dist/tools/merge-risk-handler.js.map +1 -0
  52. package/dist/tools/register-domain-bundle-tools.d.ts +3 -0
  53. package/dist/tools/register-domain-bundle-tools.d.ts.map +1 -0
  54. package/dist/tools/register-domain-bundle-tools.js +27 -0
  55. package/dist/tools/register-domain-bundle-tools.js.map +1 -0
  56. package/dist/tools/register-merge-risk-tools.d.ts +5 -0
  57. package/dist/tools/register-merge-risk-tools.d.ts.map +1 -0
  58. package/dist/tools/register-merge-risk-tools.js +6 -0
  59. package/dist/tools/register-merge-risk-tools.js.map +1 -0
  60. package/dist/tools/register-spec-prompt-tools.d.ts +3 -0
  61. package/dist/tools/register-spec-prompt-tools.d.ts.map +1 -0
  62. package/dist/tools/register-spec-prompt-tools.js +5 -0
  63. package/dist/tools/register-spec-prompt-tools.js.map +1 -0
  64. package/dist/tools/register-telemetry-health-tools.d.ts +3 -0
  65. package/dist/tools/register-telemetry-health-tools.d.ts.map +1 -0
  66. package/dist/tools/register-telemetry-health-tools.js +27 -0
  67. package/dist/tools/register-telemetry-health-tools.js.map +1 -0
  68. package/dist/tools/spec-prompt-handler.d.ts +5 -0
  69. package/dist/tools/spec-prompt-handler.d.ts.map +1 -0
  70. package/dist/tools/spec-prompt-handler.js +142 -0
  71. package/dist/tools/spec-prompt-handler.js.map +1 -0
  72. package/dist/tools/telemetry-health-handler.d.ts +3 -0
  73. package/dist/tools/telemetry-health-handler.d.ts.map +1 -0
  74. package/dist/tools/telemetry-health-handler.js +49 -0
  75. package/dist/tools/telemetry-health-handler.js.map +1 -0
  76. package/dist/types/git.d.ts +58 -0
  77. package/dist/types/git.d.ts.map +1 -1
  78. package/dist/types/mcp.d.ts +29 -0
  79. package/dist/types/mcp.d.ts.map +1 -1
  80. package/dist/types/project/planu-config.d.ts +2 -0
  81. package/dist/types/project/planu-config.d.ts.map +1 -1
  82. package/dist/types/telemetry.d.ts +15 -0
  83. package/dist/types/telemetry.d.ts.map +1 -1
  84. package/dist/types/tooling/bundles.d.ts +34 -0
  85. package/dist/types/tooling/bundles.d.ts.map +1 -0
  86. package/dist/types/tooling/bundles.js +3 -0
  87. package/dist/types/tooling/bundles.js.map +1 -0
  88. package/dist/types/tooling/index.d.ts +1 -0
  89. package/dist/types/tooling/index.d.ts.map +1 -1
  90. package/dist/types/tooling.d.ts +1 -1
  91. package/dist/types/tooling.d.ts.map +1 -1
  92. package/package.json +1 -1
  93. package/src/config/domain-bundles.json +134 -0
  94. package/src/config/license-plans.json +7 -1
@@ -0,0 +1,134 @@
1
+ {
2
+ "bundles": [
3
+ {
4
+ "id": "stripe-payments",
5
+ "name": "Stripe Payments",
6
+ "description": "Spec templates for checkout/webhooks/refunds + Stripe validation skill",
7
+ "tags": ["payments", "stripe", "backend"],
8
+ "components": {
9
+ "skills": [
10
+ {
11
+ "name": "stripe-validation",
12
+ "content": "# Stripe Validation Skill\nAlways verify idempotency keys. Check webhook signatures. Validate amount in cents."
13
+ }
14
+ ],
15
+ "rules": [
16
+ {
17
+ "filename": "stripe-payments.md",
18
+ "content": "# Stripe Rules\n- Always use idempotency keys\n- Verify webhook signatures with Stripe-Signature header\n- Store amounts in cents (integer)"
19
+ }
20
+ ],
21
+ "specTemplates": [
22
+ {
23
+ "name": "checkout-flow",
24
+ "description": "Stripe checkout session spec template"
25
+ }
26
+ ]
27
+ }
28
+ },
29
+ {
30
+ "id": "auth-supabase",
31
+ "name": "Supabase Auth",
32
+ "description": "Login/signup/RBAC spec templates + RLS rules + security review skill",
33
+ "tags": ["auth", "supabase", "security"],
34
+ "components": {
35
+ "skills": [
36
+ {
37
+ "name": "supabase-security",
38
+ "content": "# Supabase Security\nAlways enable RLS. Never use service_role key client-side. Validate ownership in Server Actions."
39
+ }
40
+ ],
41
+ "rules": [
42
+ {
43
+ "filename": "auth-supabase.md",
44
+ "content": "# Supabase Auth Rules\n- Enable RLS on every table\n- Use anon key client-side only\n- Validate user ownership in every mutation"
45
+ }
46
+ ],
47
+ "specTemplates": [
48
+ {
49
+ "name": "user-auth",
50
+ "description": "Login/signup/session spec template"
51
+ }
52
+ ]
53
+ }
54
+ },
55
+ {
56
+ "id": "rest-api",
57
+ "name": "REST API",
58
+ "description": "CRUD endpoint spec templates + OpenAPI validation skill",
59
+ "tags": ["api", "rest", "backend"],
60
+ "components": {
61
+ "skills": [
62
+ {
63
+ "name": "api-validation",
64
+ "content": "# API Validation\nAll endpoints must return correct HTTP status codes. Validate inputs with Zod. Document with OpenAPI."
65
+ }
66
+ ],
67
+ "rules": [
68
+ {
69
+ "filename": "rest-api.md",
70
+ "content": "# REST API Rules\n- 201 for POST creates, 200 for updates, 204 for deletes\n- Always validate request body with Zod\n- Return consistent error format: { error, code, details }"
71
+ }
72
+ ],
73
+ "specTemplates": [
74
+ {
75
+ "name": "crud-endpoint",
76
+ "description": "CRUD REST endpoint spec template"
77
+ }
78
+ ]
79
+ }
80
+ },
81
+ {
82
+ "id": "nextjs-fullstack",
83
+ "name": "Next.js Fullstack",
84
+ "description": "Server Actions/RSC spec templates + performance skill",
85
+ "tags": ["nextjs", "fullstack", "react"],
86
+ "components": {
87
+ "skills": [
88
+ {
89
+ "name": "nextjs-performance",
90
+ "content": "# Next.js Performance\nPrefer RSC over client components. Use revalidatePath after mutations. Lazy load heavy components."
91
+ }
92
+ ],
93
+ "rules": [
94
+ {
95
+ "filename": "nextjs-fullstack.md",
96
+ "content": "# Next.js Rules\n- Server Actions for all mutations\n- revalidatePath after every data mutation\n- Never use useEffect for data fetching"
97
+ }
98
+ ],
99
+ "specTemplates": [
100
+ {
101
+ "name": "server-action",
102
+ "description": "Next.js Server Action spec template"
103
+ }
104
+ ]
105
+ }
106
+ },
107
+ {
108
+ "id": "react-native",
109
+ "name": "React Native Mobile",
110
+ "description": "Screens/navigation spec templates + mobile CI/CD hooks",
111
+ "tags": ["mobile", "react-native", "ios", "android"],
112
+ "components": {
113
+ "skills": [
114
+ {
115
+ "name": "rn-best-practices",
116
+ "content": "# React Native Best Practices\nUse FlatList for lists. Avoid inline styles. Test on both iOS and Android."
117
+ }
118
+ ],
119
+ "rules": [
120
+ {
121
+ "filename": "react-native.md",
122
+ "content": "# React Native Rules\n- FlatList for all scrollable lists (never ScrollView with map)\n- Memoize expensive components with React.memo\n- Use platform-specific extensions (.ios.ts, .android.ts) for platform code"
123
+ }
124
+ ],
125
+ "specTemplates": [
126
+ {
127
+ "name": "screen-component",
128
+ "description": "React Native screen spec template"
129
+ }
130
+ ]
131
+ }
132
+ }
133
+ ]
134
+ }
@@ -286,7 +286,13 @@
286
286
  "velocity_trend",
287
287
  "version_spec",
288
288
  "workload_distribution",
289
- "security_scan_pro"
289
+ "security_scan_pro",
290
+ "assess_merge_risk",
291
+ "expose_spec_as_prompt",
292
+ "list_spec_prompts",
293
+ "telemetry_health",
294
+ "apply_domain_bundle",
295
+ "list_domain_bundles"
290
296
  ],
291
297
  "alwaysAllowed": [
292
298
  "activate_license",
@@ -0,0 +1,24 @@
1
+ import type { DomainBundle, BundleInstallResult } from '../types/index.js';
2
+ /**
3
+ * Returns all available domain bundles (synchronous after first load).
4
+ * Loads config lazily on first call.
5
+ */
6
+ export declare function listBundlesAsync(): Promise<DomainBundle[]>;
7
+ /**
8
+ * Returns all available domain bundles from the config file.
9
+ * Exported for use in tool handlers — call after ensuring config is loaded.
10
+ */
11
+ export declare function listBundles(bundles: DomainBundle[]): DomainBundle[];
12
+ /**
13
+ * Installs a domain bundle into the given project path.
14
+ *
15
+ * - Skills → {projectPath}/.claude/skills/{name}
16
+ * - Rules → {projectPath}/.claude/rules/{filename}
17
+ * - Spec templates → {projectPath}/planu/templates/{name}.md
18
+ *
19
+ * If a file already exists and overwrite is false, it is added to skipped.
20
+ */
21
+ export declare function installBundle(bundleId: string, projectPath: string, options: {
22
+ overwrite?: boolean;
23
+ }): Promise<BundleInstallResult>;
24
+ //# sourceMappingURL=bundle-installer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-installer.d.ts","sourceRoot":"","sources":["../../src/engine/bundle-installer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAiB,MAAM,mBAAmB,CAAC;AAwB1F;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAEhE;AAED;;;GAGG;AAEH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAEnE;AAuBD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAkE9B"}
@@ -0,0 +1,131 @@
1
+ // Planu — bundle-installer.ts
2
+ // Installs domain bundle components (skills, rules, spec templates) into a project.
3
+ import { readFile, writeFile, mkdir, access } from 'node:fs/promises';
4
+ import { join, dirname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ // ---------------------------------------------------------------------------
7
+ // Config loading
8
+ // ---------------------------------------------------------------------------
9
+ const CONFIG_PATH = join(dirname(fileURLToPath(import.meta.url)), '../config/domain-bundles.json');
10
+ let cachedBundles = null;
11
+ async function loadBundles() {
12
+ if (cachedBundles !== null) {
13
+ return cachedBundles;
14
+ }
15
+ const raw = await readFile(CONFIG_PATH, 'utf-8');
16
+ const config = JSON.parse(raw);
17
+ cachedBundles = config.bundles;
18
+ return cachedBundles;
19
+ }
20
+ // ---------------------------------------------------------------------------
21
+ // Public API
22
+ // ---------------------------------------------------------------------------
23
+ /**
24
+ * Returns all available domain bundles (synchronous after first load).
25
+ * Loads config lazily on first call.
26
+ */
27
+ export async function listBundlesAsync() {
28
+ return loadBundles();
29
+ }
30
+ /**
31
+ * Returns all available domain bundles from the config file.
32
+ * Exported for use in tool handlers — call after ensuring config is loaded.
33
+ */
34
+ /* v8 ignore start */
35
+ export function listBundles(bundles) {
36
+ return bundles;
37
+ }
38
+ /* v8 ignore stop */
39
+ /**
40
+ * Checks if a file exists at the given path.
41
+ */
42
+ async function fileExists(filePath) {
43
+ try {
44
+ await access(filePath);
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /**
52
+ * Writes a file, creating parent directories as needed.
53
+ */
54
+ async function writeFileWithDirs(filePath, content) {
55
+ await mkdir(dirname(filePath), { recursive: true });
56
+ await writeFile(filePath, content, 'utf-8');
57
+ }
58
+ /**
59
+ * Installs a domain bundle into the given project path.
60
+ *
61
+ * - Skills → {projectPath}/.claude/skills/{name}
62
+ * - Rules → {projectPath}/.claude/rules/{filename}
63
+ * - Spec templates → {projectPath}/planu/templates/{name}.md
64
+ *
65
+ * If a file already exists and overwrite is false, it is added to skipped.
66
+ */
67
+ export async function installBundle(bundleId, projectPath, options) {
68
+ const overwrite = options.overwrite ?? false;
69
+ const result = {
70
+ bundleId,
71
+ installed: [],
72
+ skipped: [],
73
+ errors: [],
74
+ };
75
+ const bundles = await loadBundles();
76
+ const bundle = bundles.find((b) => b.id === bundleId);
77
+ if (!bundle) {
78
+ result.errors.push(`Bundle "${bundleId}" not found`);
79
+ return result;
80
+ }
81
+ // Install skills
82
+ for (const skill of bundle.components.skills) {
83
+ const skillPath = join(projectPath, '.claude', 'skills', skill.name);
84
+ try {
85
+ if (!overwrite && (await fileExists(skillPath))) {
86
+ result.skipped.push(skillPath);
87
+ continue;
88
+ }
89
+ await writeFileWithDirs(skillPath, skill.content);
90
+ result.installed.push(skillPath);
91
+ }
92
+ catch (err) {
93
+ result.errors.push(`Failed to install skill "${skill.name}": ${String(err)}`);
94
+ }
95
+ }
96
+ // Install rules
97
+ for (const rule of bundle.components.rules) {
98
+ const rulePath = join(projectPath, '.claude', 'rules', rule.filename);
99
+ try {
100
+ if (!overwrite && (await fileExists(rulePath))) {
101
+ result.skipped.push(rulePath);
102
+ continue;
103
+ }
104
+ await writeFileWithDirs(rulePath, rule.content);
105
+ result.installed.push(rulePath);
106
+ /* v8 ignore next 3 */
107
+ }
108
+ catch (err) {
109
+ result.errors.push(`Failed to install rule "${rule.filename}": ${String(err)}`);
110
+ }
111
+ }
112
+ // Install spec templates
113
+ for (const template of bundle.components.specTemplates) {
114
+ const templatePath = join(projectPath, 'planu', 'templates', `${template.name}.md`);
115
+ try {
116
+ if (!overwrite && (await fileExists(templatePath))) {
117
+ result.skipped.push(templatePath);
118
+ continue;
119
+ }
120
+ const content = `# ${template.description}\n`;
121
+ await writeFileWithDirs(templatePath, content);
122
+ result.installed.push(templatePath);
123
+ /* v8 ignore next 3 */
124
+ }
125
+ catch (err) {
126
+ result.errors.push(`Failed to install template "${template.name}": ${String(err)}`);
127
+ }
128
+ }
129
+ return result;
130
+ }
131
+ //# sourceMappingURL=bundle-installer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-installer.js","sourceRoot":"","sources":["../../src/engine/bundle-installer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { HookSystem, PlanuAutoStageResult } from '../types/index.js';
2
+ export type { HookSystem, PlanuAutoStageResult };
3
+ export declare function detectHookSystem(projectPath: string): Promise<HookSystem>;
4
+ export declare function injectPlanuAutoStage(projectPath: string): Promise<PlanuAutoStageResult>;
5
+ //# sourceMappingURL=git-hook-injector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-hook-injector.d.ts","sourceRoot":"","sources":["../../src/engine/git-hook-injector.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE1E,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,CAAC;AA6BjD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAiB/E;AAoBD,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA6B7F"}
@@ -0,0 +1,86 @@
1
+ // engine/git-hook-injector.ts — Inject planu auto-stage snippet into project git pre-commit hook
2
+ // SPEC-347: auto-stage planu HTML files so they are never left out of commits
3
+ import { readFile, writeFile, access, constants } from 'node:fs/promises';
4
+ import { join, resolve } from 'node:path';
5
+ const PLANU_SNIPPET = `\n# Planu: auto-stage generated HTML reports\ngit add planu/ 2>/dev/null || true\n`;
6
+ const PLANU_MARKER = 'git add planu/';
7
+ async function fileExists(filePath) {
8
+ try {
9
+ await access(filePath, constants.F_OK);
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ async function packageJsonHasDep(projectPath, depName) {
17
+ try {
18
+ const pkgPath = join(projectPath, 'package.json');
19
+ const raw = await readFile(pkgPath, 'utf-8');
20
+ const pkg = JSON.parse(raw);
21
+ const deps = {
22
+ ...pkg.dependencies,
23
+ ...pkg.devDependencies,
24
+ };
25
+ return depName in deps;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ export async function detectHookSystem(projectPath) {
32
+ if (await fileExists(join(projectPath, '.husky'))) {
33
+ return 'husky';
34
+ }
35
+ if ((await fileExists(join(projectPath, 'lefthook.yml'))) ||
36
+ (await fileExists(join(projectPath, 'lefthook.yaml')))) {
37
+ return 'lefthook';
38
+ }
39
+ if (await packageJsonHasDep(projectPath, 'simple-git-hooks')) {
40
+ return 'simple-git-hooks';
41
+ }
42
+ if (await fileExists(join(projectPath, '.git', 'hooks', 'pre-commit'))) {
43
+ return 'native';
44
+ }
45
+ return 'none';
46
+ }
47
+ async function injectIntoFile(hookFile) {
48
+ let content;
49
+ try {
50
+ content = await readFile(hookFile, 'utf-8');
51
+ }
52
+ catch {
53
+ content = '#!/bin/sh\n';
54
+ }
55
+ if (content.includes(PLANU_MARKER)) {
56
+ return { injected: false, alreadyPresent: true };
57
+ }
58
+ await writeFile(hookFile, content + PLANU_SNIPPET, 'utf-8');
59
+ return { injected: true, alreadyPresent: false };
60
+ }
61
+ export async function injectPlanuAutoStage(projectPath) {
62
+ const hookSystem = await detectHookSystem(projectPath);
63
+ const absPath = resolve(projectPath);
64
+ if (hookSystem === 'husky') {
65
+ const hookFile = join(absPath, '.husky', 'pre-commit');
66
+ const { injected, alreadyPresent } = await injectIntoFile(hookFile);
67
+ return { injected, hookSystem, hookFile, alreadyPresent };
68
+ }
69
+ if (hookSystem === 'native') {
70
+ const hookFile = join(absPath, '.git', 'hooks', 'pre-commit');
71
+ const { injected, alreadyPresent } = await injectIntoFile(hookFile);
72
+ return { injected, hookSystem, hookFile, alreadyPresent };
73
+ }
74
+ // lefthook / simple-git-hooks: cannot auto-inject, return manual instructions
75
+ const manualInstructions = hookSystem === 'lefthook'
76
+ ? `Add to lefthook.yml under pre-commit commands:\n - run: git add planu/ 2>/dev/null || true`
77
+ : `Add to package.json "simple-git-hooks"."pre-commit":\n "git add planu/ 2>/dev/null || true"`;
78
+ return {
79
+ injected: false,
80
+ hookSystem,
81
+ hookFile: '',
82
+ alreadyPresent: false,
83
+ manualInstructions,
84
+ };
85
+ }
86
+ //# sourceMappingURL=git-hook-injector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-hook-injector.js","sourceRoot":"","sources":["../../src/engine/git-hook-injector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { AnalyzerResult } from '../../types/index.js';
2
+ /**
3
+ * 30% weight — detects security-sensitive patterns in diff and file paths.
4
+ */
5
+ export declare function analyzeSecuritySurface(files: string[], diff: string): AnalyzerResult;
6
+ /**
7
+ * 25% weight — checks if tests accompany code changes.
8
+ */
9
+ export declare function analyzeCoverageDelta(linesAdded: number, files: string[]): AnalyzerResult;
10
+ /**
11
+ * 20% weight — estimates blast radius by file count and core file changes.
12
+ */
13
+ export declare function analyzeBlastRadius(filesChanged: number, files: string[]): AnalyzerResult;
14
+ /**
15
+ * 15% weight — detects schema migrations and irreversible changes.
16
+ */
17
+ export declare function analyzeReversibility(files: string[], diff: string): AnalyzerResult;
18
+ /**
19
+ * 10% weight — checks spec compliance via context.
20
+ */
21
+ export declare function analyzeSpecCompliance(files: string[], context?: string): AnalyzerResult;
22
+ //# sourceMappingURL=analyzers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzers.d.ts","sourceRoot":"","sources":["../../../src/engine/merge-risk-assessor/analyzers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAa3D;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAoCpF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,CAiCxF;AAID;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,CA6BxF;AAMD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAwBlF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,cAAc,CAsBvF"}
@@ -0,0 +1,150 @@
1
+ const SECURITY_PATTERNS = [
2
+ /password/i,
3
+ /token/i,
4
+ /secret/i,
5
+ /apiKey/i,
6
+ /api_key/i,
7
+ /process\.env/,
8
+ ];
9
+ const SECURITY_PATHS = ['auth/', 'middleware/', 'oauth/', 'jwt/', 'permissions/'];
10
+ /**
11
+ * 30% weight — detects security-sensitive patterns in diff and file paths.
12
+ */
13
+ export function analyzeSecuritySurface(files, diff) {
14
+ const sensitiveFiles = files.filter((f) => SECURITY_PATHS.some((p) => f.includes(p)));
15
+ const patternMatches = SECURITY_PATTERNS.filter((p) => p.test(diff));
16
+ if (sensitiveFiles.length > 0 && patternMatches.length > 0) {
17
+ return {
18
+ score: 90,
19
+ reason: `Security-sensitive files modified (${sensitiveFiles[0]}) with credential patterns detected`,
20
+ };
21
+ }
22
+ if (sensitiveFiles.length > 0) {
23
+ return {
24
+ score: 70,
25
+ reason: `Security-sensitive paths modified: ${sensitiveFiles.slice(0, 2).join(', ')}`,
26
+ };
27
+ }
28
+ if (patternMatches.length >= 3) {
29
+ return {
30
+ score: 60,
31
+ reason: `Multiple credential patterns in diff (password, token, secret, etc.)`,
32
+ };
33
+ }
34
+ if (patternMatches.length > 0) {
35
+ return {
36
+ score: 35,
37
+ reason: `Credential-related patterns detected in diff`,
38
+ };
39
+ }
40
+ return { score: 10, reason: 'No security-sensitive patterns detected' };
41
+ }
42
+ /**
43
+ * 25% weight — checks if tests accompany code changes.
44
+ */
45
+ export function analyzeCoverageDelta(linesAdded, files) {
46
+ const hasTests = files.some((f) => f.includes('.test.ts') ||
47
+ f.includes('.spec.ts') ||
48
+ f.includes('.test.js') ||
49
+ f.includes('tests/') ||
50
+ f.includes('__tests__/'));
51
+ if (linesAdded === 0) {
52
+ return { score: 10, reason: 'No new lines added — deletion/refactor only' };
53
+ }
54
+ if (hasTests) {
55
+ const codeFiles = files.filter((f) => !f.includes('.test.') && !f.includes('.spec.') && !f.includes('__tests__/'));
56
+ if (codeFiles.length === 0) {
57
+ return { score: 5, reason: 'Test-only changes' };
58
+ }
59
+ return { score: 20, reason: 'Test files included in the changeset' };
60
+ }
61
+ if (linesAdded > 200) {
62
+ return { score: 85, reason: `Large addition (${linesAdded} lines) with no test coverage` };
63
+ }
64
+ if (linesAdded > 50) {
65
+ return { score: 65, reason: `Significant addition (${linesAdded} lines) without tests` };
66
+ }
67
+ return { score: 45, reason: 'No test files in changeset' };
68
+ }
69
+ const CORE_FILES = ['index.ts', 'types/index.ts', 'base-store.ts'];
70
+ /**
71
+ * 20% weight — estimates blast radius by file count and core file changes.
72
+ */
73
+ export function analyzeBlastRadius(filesChanged, files) {
74
+ const touchesCoreFiles = files.some((f) => CORE_FILES.some((c) => f.endsWith(c)));
75
+ let baseScore;
76
+ let reason;
77
+ if (filesChanged > 20) {
78
+ baseScore = 80;
79
+ reason = `Very large changeset: ${filesChanged} files modified`;
80
+ }
81
+ else if (filesChanged > 10) {
82
+ baseScore = 50;
83
+ reason = `Large changeset: ${filesChanged} files modified`;
84
+ }
85
+ else if (filesChanged > 5) {
86
+ baseScore = 30;
87
+ reason = `Moderate changeset: ${filesChanged} files modified`;
88
+ }
89
+ else {
90
+ baseScore = 10;
91
+ reason = `Small changeset: ${filesChanged} files modified`;
92
+ }
93
+ if (touchesCoreFiles) {
94
+ const capped = Math.min(100, Math.round(baseScore * 1.5));
95
+ return {
96
+ score: capped,
97
+ reason: `${reason} — includes core files (index.ts, types/index.ts, or base-store.ts)`,
98
+ };
99
+ }
100
+ return { score: baseScore, reason };
101
+ }
102
+ const MIGRATION_PATTERNS = [/migration/i, /ALTER\s+TABLE/i, /DROP\s+TABLE/i, /DROP\s+COLUMN/i];
103
+ const SCHEMA_FILES = ['schema.ts', 'schema.js', 'migrations/', 'migrate/'];
104
+ /**
105
+ * 15% weight — detects schema migrations and irreversible changes.
106
+ */
107
+ export function analyzeReversibility(files, diff) {
108
+ const hasMigrationFile = files.some((f) => SCHEMA_FILES.some((s) => f.includes(s)));
109
+ const hasDestructivePatterns = MIGRATION_PATTERNS.filter((p) => p.test(diff));
110
+ if (hasMigrationFile && hasDestructivePatterns.length > 0) {
111
+ return {
112
+ score: 95,
113
+ reason: 'Migration files with destructive SQL patterns (ALTER/DROP) detected',
114
+ };
115
+ }
116
+ if (hasMigrationFile) {
117
+ return { score: 70, reason: 'Database migration files in changeset' };
118
+ }
119
+ if (hasDestructivePatterns.length > 0) {
120
+ return {
121
+ score: 60,
122
+ reason: 'Destructive schema patterns detected in diff',
123
+ };
124
+ }
125
+ return { score: 10, reason: 'No migration or schema changes detected' };
126
+ }
127
+ /**
128
+ * 10% weight — checks spec compliance via context.
129
+ */
130
+ export function analyzeSpecCompliance(files, context) {
131
+ // Unused but kept for API consistency
132
+ void files;
133
+ if (context && /SPEC-\d+/i.test(context)) {
134
+ return {
135
+ score: 20,
136
+ reason: 'Spec ID found in context — changes appear spec-driven',
137
+ };
138
+ }
139
+ if (context && context.trim().length > 10) {
140
+ return {
141
+ score: 35,
142
+ reason: 'Context provided but no spec ID — partially compliant',
143
+ };
144
+ }
145
+ return {
146
+ score: 50,
147
+ reason: 'No spec context provided — cannot verify spec-driven development',
148
+ };
149
+ }
150
+ //# sourceMappingURL=analyzers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzers.js","sourceRoot":"","sources":["../../../src/engine/merge-risk-assessor/analyzers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { DiffStats } from '../../types/index.js';
2
+ export type { DiffStats };
3
+ /**
4
+ * Runs `git diff --stat {baseBranch}...{branch}` and parses the output.
5
+ * Uses execFile (not exec) to prevent shell injection.
6
+ */
7
+ export declare function getDiffStats(projectPath: string, branch: string, baseBranch: string): Promise<DiffStats>;
8
+ /**
9
+ * Parses the output of `git diff --stat` into structured stats.
10
+ * Example output:
11
+ * src/foo.ts | 10 +++--
12
+ * src/bar.ts | 4 +
13
+ * 2 files changed, 11 insertions(+), 4 deletions(-)
14
+ */
15
+ export declare function parseDiffStatOutput(output: string): DiffStats;
16
+ //# sourceMappingURL=git-diff-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-diff-parser.d.ts","sourceRoot":"","sources":["../../../src/engine/merge-risk-assessor/git-diff-parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,YAAY,EAAE,SAAS,EAAE,CAAC;AAI1B;;;GAGG;AACH,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,SAAS,CAAC,CAQpB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAiC7D"}