@nivustec/proteus 1.0.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Nivustec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,394 @@
1
+
2
+ <div align="center">
3
+
4
+ <img src="https://proteus.nivustec.com.br/icons/proteus.png" title="Proteus" alt="Proteus logo" width="128">
5
+
6
+ # Proteus
7
+
8
+ [🌍 Website](https://proteus.nivustec.com)
9
+
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@nivustec/proteus"><img alt="npm" src="https://img.shields.io/npm/v/%40nivustec%2Fproteus.svg?color=2ea44f&label=npm" /></a>
13
+ <a href="https://www.npmjs.com/package/@nivustec/proteus"><img alt="downloads" src="https://img.shields.io/npm/dm/%40nivustec%2Fproteus.svg?color=2ea44f" /></a>
14
+ <img alt="node" src="https://img.shields.io/badge/node-%3E%3D18.0.0-2ea44f" />
15
+ <img alt="ts" src="https://img.shields.io/badge/TypeScript-5.x-2ea44f" />
16
+ <a href="#license"><img alt="license" src="https://img.shields.io/badge/license-MIT-2ea44f" /></a>
17
+ </p>
18
+
19
+ Automated data-testid injection for React (JS/TS).
20
+ Proteus “surfs” your codebase and transforms it on save/commit, enforcing qa_prefixed, unique, and semantic test IDs across your components.
21
+ <br/>
22
+ </div>
23
+
24
+ ### Why Proteus?
25
+ - Consistent, predictable, and unique test IDs
26
+ - Zero-runtime: IDs are injected at build/dev time
27
+ - Works with JSX/TSX and plain JS/TS React projects
28
+ - Safe defaults, with flexible strategies
29
+
30
+ ### Features
31
+ - Two strategies only:
32
+ - safe-hash: compact ids like `qa_abc123`; in maps: ``qa_abc123_${item.id}``
33
+ - functional: semantic ids like ``qa_products_image_${item.id}``
34
+ - Uniqueness enforcement with sibling-indexing for repeated tags
35
+ - Map-awareness: automatically appends key/index for dynamic lists
36
+ - Watch mode (inject on save)
37
+ - Pre-commit integration (inject before commit)
38
+ - Default strategy: functional
39
+ - Default include: React frontend files only (`src/**/*.tsx`, `src/**/*.jsx`)
40
+
41
+ ---
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ npm i -D @nivustec/proteus
47
+ # or
48
+ yarn add -D @nivustec/proteus
49
+ ```
50
+
51
+ For local development of Proteus itself:
52
+ ```bash
53
+ # in the Proteus repo
54
+ npm run build
55
+ npm link
56
+
57
+ # in your app repo
58
+ npm link @nivustec/proteus
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Quick Start
64
+
65
+ 1) Initialize a config in your project root:
66
+ ```bash
67
+ proteus init
68
+ ```
69
+
70
+ 2) Inject once:
71
+ ```bash
72
+ proteus inject
73
+ # or specific files
74
+ proteus inject src/components/Button.tsx
75
+ ```
76
+
77
+ 3) Watch mode (auto-inject on save):
78
+ ```bash
79
+ proteus inject --watch
80
+ ```
81
+
82
+ 4) Git pre-commit (Husky optional):
83
+ ```bash
84
+ proteus setup # adds a pre-commit hook
85
+ git commit -m "feat: ..." # Proteus injects before commit
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Configuration
91
+
92
+ Create or edit `proteus.config.json` in your project root.
93
+
94
+ ```json
95
+ {
96
+ "injectOnCommit": true,
97
+ "injectOnSave": true,
98
+ "include": ["src/**/*.tsx", "src/**/*.jsx"],
99
+ "exclude": ["node_modules/**", "dist/**"],
100
+ "strategy": "functional",
101
+ "verbose": false,
102
+ "detectReusableComponents": true,
103
+ "autoExcludePatterns": ["**/ui/**", "**/common/**"]
104
+ }
105
+ ```
106
+
107
+ - injectOnCommit: enable pre-commit injection
108
+ - injectOnSave: enable watch mode injection
109
+ - include/exclude: glob patterns for files
110
+ - strategy: `safe-hash` or `functional`
111
+ - verbose: enable additional logs in CLI mode
112
+ - **detectReusableComponents**: auto-detect and skip reusable UI components (default: `true`)
113
+ - **autoExcludePatterns**: glob patterns to auto-exclude (e.g., `["**/ui/**", "**/common/**"]`)
114
+
115
+ ### 🆕 Reusable Components Detection (v1.1.0)
116
+
117
+ Proteus now automatically detects reusable components (like Button, Input, Card from UI libraries) and skips injecting data-testid directly in them. Instead, it injects at the usage sites, ensuring unique IDs for each instance.
118
+
119
+ **Detection criteria:**
120
+ - File is in a UI/common/shared folder (e.g., `/ui/`, `/common/`, `/shared/`)
121
+ - Uses `React.forwardRef` or `forwardRef<>`
122
+ - Has `{...props}` spread operator
123
+
124
+ **Example:**
125
+
126
+ ```tsx
127
+ // src/components/ui/button.tsx (auto-skipped by Proteus)
128
+ const Button = React.forwardRef(({ ...props }, ref) => {
129
+ return <button {...props} ref={ref} />; // ✅ No fixed data-testid
130
+ });
131
+
132
+ // src/components/Hero.tsx (Proteus injects here)
133
+ <Button onClick={handleClick} data-testid="qa_hero_button_1_abc123">
134
+ Get Started
135
+ </Button>
136
+ <Button variant="outline" data-testid="qa_hero_button_2_def456">
137
+ View on GitHub
138
+ </Button>
139
+ ```
140
+
141
+ **Benefits:**
142
+ - ✅ No duplicate data-testid across multiple Button instances
143
+ - ✅ Each usage gets a unique, contextual ID
144
+ - ✅ Works with any UI library (shadcn, MUI, Ant Design, etc.)
145
+ - ✅ Zero configuration required (auto-detection enabled by default)
146
+
147
+ ---
148
+
149
+ ## Strategies
150
+
151
+ ### functional (semantic, recommended)
152
+
153
+ Rules (simplified):
154
+ - Prefix: `qa_`
155
+ - Base: `qa_<component>_<role>_[<siblingIndex>]_[<descriptor>]`
156
+ - Dynamic lists (map): append key/index as the last segment
157
+
158
+ Examples:
159
+ ```jsx
160
+ // Map wrapper element
161
+ <div className="item" key={item.id} data-testid={`qa_products_item_${item.id}`} />
162
+
163
+ // Image inside map
164
+ <img data-testid={`qa_products_image_${item.id}`} />
165
+
166
+ // Text containers inside map
167
+ <div data-testid={`qa_products_container_name_${item.id}`}>{item.name}</div>
168
+ <div data-testid={`qa_products_container_price_${item.id}`}>{formatCurrency(item.price)}</div>
169
+
170
+ // Duplicate siblings get indexed
171
+ <h1 data-testid={`qa_products_h1_1_${item.id}`}>One</h1>
172
+ <h1 data-testid={`qa_products_h1_2_${item.id}`}>Two</h1>
173
+
174
+ // Outside map, unique suffix may be added to avoid collisions
175
+ <div className="header" data-testid="qa_products_header_<uniq>" />
176
+ ```
177
+
178
+ #### Before/After (functional)
179
+
180
+ ```jsx
181
+ // Before
182
+ export function ProductCard({ item }) {
183
+ return (
184
+ <div className="card">
185
+ <img alt={item.name} />
186
+ <h2>{item.name}</h2>
187
+ <button className="add-to-cart">Add</button>
188
+ </div>
189
+ );
190
+ }
191
+
192
+ // After (auto-injected)
193
+ export function ProductCard({ item }) {
194
+ return (
195
+ <div className="card" data-testid="qa_productcard_container_<uniq>">
196
+ <img alt={item.name} data-testid="qa_productcard_image_img-item-name_<uniq>" />
197
+ <h2 data-testid="qa_productcard_container_1_item-name_<uniq>">{item.name}</h2>
198
+ <button className="add-to-cart" data-testid="qa_productcard_button_add-to-cart_<uniq>">Add</button>
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+
204
+ ### safe-hash (compact)
205
+
206
+ Rules:
207
+ - Prefix: `qa_`
208
+ - Base: `qa_<hash>` (stable per location)
209
+ - In maps: append key/index: ``qa_<hash>_${item.id}``
210
+
211
+ Examples:
212
+ ```jsx
213
+ // Single element
214
+ <div data-testid="qa_ab12cd" />
215
+
216
+ // Inside map
217
+ <div data-testid={`qa_ab12cd_${item.id}`} />
218
+ ```
219
+
220
+ ---
221
+
222
+ ## CLI
223
+
224
+ ```bash
225
+ proteus inject [files...] [options]
226
+
227
+ Options:
228
+ -c, --config <path> Path to config file
229
+ --include <paths> Files to include
230
+ --exclude <paths> Files to exclude
231
+ --strategy <type> safe-hash | functional
232
+ --verbose Enable verbose logs
233
+ --json Enable machine-readable JSON output
234
+ -w, --watch Watch mode for development
235
+
236
+ proteus pre-commit # injects on staged files (used by Husky)
237
+ proteus setup # configures Husky pre-commit hook
238
+ proteus init # writes default proteus.config.json
239
+ ```
240
+
241
+ Husky (Git hooks):
242
+ - Modern Husky (v7+): if `.husky/` exists, `proteus setup` writes `.husky/pre-commit` with `proteus pre-commit`.
243
+ - Legacy fallback: if `.husky/` is not detected, `proteus setup` configures `package.json` scripts/hooks.
244
+
245
+ - Pre-commit processes only staged React files (`*.jsx`, `*.tsx`).
246
+ - Watch mode observes the configured include patterns; defaults to `src/**/*.tsx` and `src/**/*.jsx`.
247
+
248
+ ---
249
+
250
+ ## Agent-Friendly CLI (MCP Support)
251
+
252
+ Proteus can be used by AI agents and other automated systems via its machine-readable JSON output. Use the `--json` flag with the `inject` command to suppress human-readable logs and receive a structured JSON response.
253
+
254
+ ### Usage with --json
255
+
256
+ ```bash
257
+ proteus inject --json
258
+ ```
259
+
260
+ ### Example JSON Output
261
+
262
+ ```json
263
+ {
264
+ "filesProcessed": 5,
265
+ "totalInjected": 12,
266
+ "errors": 0,
267
+ "errorMessages": []
268
+ }
269
+ ```
270
+
271
+ If errors occur, they will be included in the `errorMessages` array:
272
+
273
+ ```json
274
+ {
275
+ "filesProcessed": 1,
276
+ "totalInjected": 0,
277
+ "errors": 1,
278
+ "errorMessages": [
279
+ "Error processing src/components/BrokenComponent.tsx: Parser Error: Unexpected token (1:10)"
280
+ ]
281
+ }
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Universal Agent (Tool Calling) Support
287
+
288
+ To enable any AI agent (Gemini, OpenAI, Anthropic, etc.) to interact with Proteus, we provide a universal JSON Schema for the `proteus_inject_tool` and a dedicated executor function. This approach removes any direct dependency on specific AI SDKs within the Proteus CLI itself.
289
+
290
+ ### 1. Universal Contract (JSON Schema)
291
+
292
+ The `proteus_inject_tool`'s function declaration is available as a pure JSON Schema file at the root of the project:
293
+
294
+ * **`proteus-tool-schema.json`**: This file defines the structure and parameters of the `proteus_inject_tool` in a standard Open API/JSON Schema format, allowing any agent to understand how to call the tool.
295
+
296
+ ### 2. Universal Executor
297
+
298
+ The `executeProteusCLI` function provides a clean API for executing the Proteus CLI and receiving its output in a machine-readable format.
299
+
300
+ * **`src/tool-executor.ts`**: This module exports the `executeProteusCLI` function.
301
+
302
+ ### Example Consumer Agent Usage
303
+
304
+ Here's how a consumer agent (using any AI SDK) would typically integrate with Proteus:
305
+
306
+ 1. **Load the JSON Schema:** The agent loads `proteus-tool-schema.json` to understand the tool's capabilities.
307
+ 2. **Model Interaction:** The agent's model receives a user prompt and, based on the loaded schema, decides to call `proteus_inject_tool` with specific arguments.
308
+ 3. **Execute the Tool:** The agent then calls the `executeProteusCLI` function from `src/tool-executor.ts` with the arguments provided by its model.
309
+ 4. **Process Output:** The agent receives the JSON output from `executeProteusCLI` and can then use its model to generate a natural language response for the user.
310
+
311
+ ```typescript
312
+ // Example of a consumer agent (conceptual, using a generic AI SDK)
313
+ import { readFileSync } from 'fs';
314
+ import { executeProteusCLI } from './src/tool-executor.js'; // Adjust path as needed
315
+
316
+ // 1. Load the Proteus tool schema
317
+ const proteusToolSchema = JSON.parse(readFileSync('proteus-tool-schema.json', 'utf-8'));
318
+
319
+ // Assume your AI SDK has a way to register tools and get model responses
320
+ async function runConsumerAgent(userPrompt: string) {
321
+ // --- Conceptual: AI Model decides to call the tool ---
322
+ // In a real scenario, your AI SDK would handle this based on the userPrompt
323
+ // and the registered proteusToolSchema.
324
+ // For demonstration, we'll simulate a tool call from the model.
325
+
326
+ const simulatedToolCallArgs = {
327
+ files: ['src/components/MyComponent.tsx'],
328
+ strategy: 'functional',
329
+ json_output: true, // Always true for tool calling
330
+ };
331
+
332
+ console.log(`Agent: Simulating tool call to proteus_inject_tool with args:`, simulatedToolCallArgs);
333
+
334
+ try {
335
+ // 2. Execute the Proteus CLI via the universal executor
336
+ const toolOutput = await executeProteusCLI(simulatedToolCallArgs);
337
+
338
+ console.log(`Agent: Proteus CLI returned:`, toolOutput);
339
+
340
+ // --- Conceptual: AI Model processes tool output and generates response ---
341
+ // Your AI model would take toolOutput and generate a natural language response.
342
+ if (toolOutput.errors > 0) {
343
+ console.log(`Agent: I encountered ${toolOutput.errors} errors while injecting test IDs. Please check the logs.`);
344
+ } else {
345
+ console.log(`Agent: Successfully injected ${toolOutput.totalInjected} test IDs into ${toolOutput.filesProcessed} files.`);
346
+ }
347
+
348
+ } catch (error) {
349
+ console.error("Agent: Error executing Proteus CLI:", error);
350
+ }
351
+ }
352
+
353
+ // Example usage
354
+ runConsumerAgent("Inject test IDs into src/components/MyComponent.tsx using the functional strategy.");
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Best Practices
360
+ - Commit generated IDs; they are part of your source (zero runtime)
361
+ - Choose one strategy per repo; we recommend `functional`
362
+ - Ensure `qa_` prefix uniqueness across layers and components
363
+ - Always append a key/index for dynamic maps
364
+
365
+ ---
366
+
367
+ ## Troubleshooting
368
+
369
+ 1) npx proteus not found / CLI not running
370
+ - Ensure the package is installed in your project
371
+ - For local dev: build + `npm link` in Proteus repo, then `npm link @nivustec/proteus` in your app
372
+
373
+ 2) Duplicate logs in watch mode
374
+ - Proteus debounces its own writes; if you still see duplicates, increase the debounce window in `watcher.ts`
375
+
376
+ 3) Pre-commit too verbose
377
+ - Set `verbose: false` (default). Pre-commit runs in silent mode by design.
378
+
379
+ 4) Pre-commit not injecting on non-React files
380
+ - By default, only `*.jsx` and `*.tsx` staged files are processed. Adjust `include` if you need other patterns.
381
+
382
+ 5) IDs are inserted in the wrong place
383
+ - We use AST positions; if you find an edge-case, open an issue with a code sample.
384
+
385
+ 6) Partial staging lost in pre-commit
386
+ - Current hook re-stages full files after injection. If you rely on partial staging (`git add -p`), consider disabling `injectOnCommit` and running `proteus inject` manually.
387
+
388
+ ---
389
+
390
+ ## License
391
+
392
+ MIT © Nivustec
393
+
394
+
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { loadConfig, validateConfig } from "./config.js";
4
+ import { injectTestIds, setupGitHooks, injectStagedFiles } from "./injector.js";
5
+ import { startWatchMode } from "./watcher.js";
6
+ import fs from "fs";
7
+ const pkg = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
8
+ const program = new Command();
9
+ program
10
+ .name("proteus")
11
+ .description("Proteus - The shape-shifting CLI for automatic data-testid injection")
12
+ .version(pkg.version);
13
+ program
14
+ .command("inject")
15
+ .description("Inject data-testid attributes into React components")
16
+ .argument("[files...]", "Specific files to process")
17
+ .option("-c, --config <path>", "Path to config file")
18
+ .option("--include <paths...>", "Files to include")
19
+ .option("--exclude <paths...>", "Files to exclude")
20
+ .option("--strategy <type>", "ID generation strategy (safe-hash | functional)")
21
+ .option("--verbose", "Enable verbose logs")
22
+ .option("--json", "Enable machine-readable JSON output")
23
+ .option("-w, --watch", "Watch mode for development")
24
+ .action(async (files, options) => {
25
+ try {
26
+ // When JSON output is enabled, automatically silence any other logs.
27
+ const silent = options.json;
28
+ let config = loadConfig(options.config, silent);
29
+ if (options.include)
30
+ config.include = options.include;
31
+ if (options.exclude)
32
+ config.exclude = options.exclude;
33
+ if (options.strategy)
34
+ config.strategy = options.strategy;
35
+ if (options.verbose !== undefined)
36
+ config.verbose = options.verbose;
37
+ if (options.json !== undefined)
38
+ config.json = options.json;
39
+ // Verbose and JSON modes are mutually exclusive.
40
+ if (config.verbose && config.json) {
41
+ config.verbose = false;
42
+ }
43
+ validateConfig(config, silent);
44
+ if (options.watch) {
45
+ startWatchMode(config);
46
+ }
47
+ else {
48
+ if (!silent) {
49
+ console.log("🌊 Proteus is transforming your components...");
50
+ }
51
+ const stats = await injectTestIds(config, files);
52
+ if (silent) {
53
+ console.log(JSON.stringify(stats, null, 2));
54
+ }
55
+ else {
56
+ console.log("\n📊 Injection completed:");
57
+ console.log(` Files processed: ${stats.filesProcessed}`);
58
+ console.log(` Test IDs injected: ${stats.totalInjected}`);
59
+ console.log(` Errors: ${stats.errors}`);
60
+ if (stats.errors > 0) {
61
+ console.log("\n❌ Errors occurred:");
62
+ stats.errorMessages.forEach(msg => console.log(` - ${msg}`));
63
+ }
64
+ if (stats.totalInjected === 0) {
65
+ console.log("💡 No new test IDs were injected. All elements may already have data-testid attributes.");
66
+ }
67
+ }
68
+ }
69
+ }
70
+ catch (error) {
71
+ const isJson = options.json;
72
+ if (isJson) {
73
+ console.log(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }, null, 2));
74
+ }
75
+ else {
76
+ console.error("❌ Error:", error instanceof Error ? error.message : error);
77
+ }
78
+ process.exit(1);
79
+ }
80
+ });
81
+ program
82
+ .command("pre-commit")
83
+ .description("Check and inject data-testid in staged files (for Git hooks)")
84
+ .action(async () => {
85
+ try {
86
+ const config = loadConfig(undefined, true);
87
+ config.verbose = false;
88
+ if (!config.injectOnCommit) {
89
+ console.log("ℹ️ injectOnCommit is disabled - skipping pre-commit check");
90
+ process.exit(0);
91
+ }
92
+ const stats = await injectStagedFiles();
93
+ if (stats.errors > 0) {
94
+ console.error("\n❌ Errors occurred during pre-commit injection:");
95
+ stats.errorMessages.forEach(msg => console.error(` - ${msg}`));
96
+ process.exit(1);
97
+ }
98
+ }
99
+ catch (error) {
100
+ console.error("❌ Pre-commit check failed:", error);
101
+ process.exit(1);
102
+ }
103
+ });
104
+ program
105
+ .command("setup")
106
+ .description("Setup Git hooks for automatic injection")
107
+ .action(() => {
108
+ try {
109
+ setupGitHooks();
110
+ console.log("✅ Setup completed successfully");
111
+ console.log("📋 Git hooks configured for automatic data-testid injection");
112
+ console.log("💡 Run 'proteus inject --watch' for development auto-injection");
113
+ }
114
+ catch (error) {
115
+ console.error("❌ Setup failed:", error);
116
+ process.exit(1);
117
+ }
118
+ });
119
+ program
120
+ .command("init")
121
+ .description("Create default config file")
122
+ .action(() => {
123
+ const defaultConfig = {
124
+ injectOnCommit: true,
125
+ injectOnSave: true,
126
+ include: [
127
+ "src/**/*.tsx",
128
+ "src/**/*.jsx",
129
+ ],
130
+ exclude: ["node_modules/**", "dist/**"],
131
+ strategy: "functional",
132
+ };
133
+ fs.writeFileSync("proteus.config.json", JSON.stringify(defaultConfig, null, 2));
134
+ console.log("✅ Created proteus.config.json");
135
+ console.log("💡 Run 'proteus setup' to configure Git hooks");
136
+ });
137
+ program.parse();
@@ -0,0 +1,3 @@
1
+ import { ProteuConfig } from "./types.js";
2
+ export declare function loadConfig(configPath?: string, silent?: boolean): ProteuConfig;
3
+ export declare function validateConfig(config: ProteuConfig, silent?: boolean): boolean;
package/dist/config.js ADDED
@@ -0,0 +1,44 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ const defaultConfig = {
3
+ injectOnCommit: true,
4
+ injectOnSave: true,
5
+ include: ["src/**/*.tsx", "src/**/*.jsx"],
6
+ exclude: ["node_modules/**", "dist/**"],
7
+ strategy: "functional",
8
+ verbose: false,
9
+ detectReusableComponents: true,
10
+ autoExcludePatterns: [],
11
+ };
12
+ export function loadConfig(configPath, silent = false) {
13
+ const configFile = configPath || "proteus.config.json";
14
+ if (existsSync(configFile)) {
15
+ try {
16
+ const userConfig = JSON.parse(readFileSync(configFile, "utf-8"));
17
+ if (!silent)
18
+ console.log(`✅ Loaded config from ${configFile}`);
19
+ return { ...defaultConfig, ...userConfig };
20
+ }
21
+ catch (error) {
22
+ if (!silent)
23
+ console.warn(`Warning: Could not parse ${configFile}, using defaults`);
24
+ }
25
+ }
26
+ else {
27
+ if (!silent)
28
+ console.log(`ℹ️ Config file ${configFile} not found, using defaults`);
29
+ }
30
+ return defaultConfig;
31
+ }
32
+ export function validateConfig(config, silent = false) {
33
+ const validStrategies = ["safe-hash", "functional"];
34
+ if (config.strategy && !validStrategies.includes(config.strategy)) {
35
+ throw new Error(`Invalid strategy: ${config.strategy}`);
36
+ }
37
+ if (!silent) {
38
+ console.log(`✅ Config validated:`);
39
+ console.log(` - injectOnCommit: ${config.injectOnCommit}`);
40
+ console.log(` - injectOnSave: ${config.injectOnSave}`);
41
+ console.log(` - strategy: ${config.strategy}`);
42
+ }
43
+ return true;
44
+ }
@@ -0,0 +1,10 @@
1
+ import { ProteuConfig } from "./types.js";
2
+ export interface InjectionStats {
3
+ filesProcessed: number;
4
+ totalInjected: number;
5
+ errors: number;
6
+ errorMessages: string[];
7
+ }
8
+ export declare function injectTestIds(config: ProteuConfig, specificFiles?: string[]): Promise<InjectionStats>;
9
+ export declare function injectStagedFiles(): Promise<InjectionStats>;
10
+ export declare function setupGitHooks(): void;