@denisvieiradev/gitwise-core 0.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.
@@ -0,0 +1,58 @@
1
+ import { L as LLMProvider, a as LLMChatRequest, b as LLMChatResponse } from '../types-DnMpR1qf.js';
2
+
3
+ interface MockResponse {
4
+ content: string;
5
+ tokens?: {
6
+ input: number;
7
+ output: number;
8
+ };
9
+ }
10
+ interface PrefixMatcher {
11
+ prefix: string;
12
+ response: MockResponse;
13
+ }
14
+ /**
15
+ * MockLLMProvider — deterministic LLM stub for tests.
16
+ *
17
+ * Usage:
18
+ * const mock = new MockLLMProvider();
19
+ * mock.queueByIndex({ content: "result" }); // first call
20
+ * mock.queueByPrefix("Analyze", { content: "analysis" }); // by prompt prefix
21
+ * await someFunction(mock);
22
+ * mock.assertCallCount(2);
23
+ * mock.assertTotalTokens({ input: 10, output: 5 });
24
+ */
25
+ declare class MockLLMProvider implements LLMProvider {
26
+ private readonly indexedResponses;
27
+ private readonly prefixResponses;
28
+ private callCount;
29
+ private totalInputTokens;
30
+ private totalOutputTokens;
31
+ private readonly calls;
32
+ /** Queue a response to return for the N-th call (0-indexed). */
33
+ queueByIndex(response: MockResponse): this;
34
+ /** Queue a response to return when userMessage starts with the given prefix. */
35
+ queueByPrefix(prefix: string, response: MockResponse): this;
36
+ chat(req: LLMChatRequest): Promise<LLMChatResponse>;
37
+ private buildResponse;
38
+ /** Returns all recorded calls. */
39
+ getCalls(): readonly LLMChatRequest[];
40
+ /** Returns the number of chat() calls made. */
41
+ getCallCount(): number;
42
+ /** Assert the provider was called exactly N times. */
43
+ assertCallCount(expected: number): void;
44
+ /** Assert accumulated token totals. */
45
+ assertTotalTokens(expected: {
46
+ input?: number;
47
+ output?: number;
48
+ }): void;
49
+ getTotalTokens(): {
50
+ input: number;
51
+ output: number;
52
+ };
53
+ reset(): void;
54
+ }
55
+
56
+ declare const __testingPlaceholder__: unique symbol;
57
+
58
+ export { MockLLMProvider, type MockResponse, type PrefixMatcher, __testingPlaceholder__ };
@@ -0,0 +1,83 @@
1
+ // src/testing/mock-llm-provider.ts
2
+ var MockLLMProvider = class {
3
+ indexedResponses = [];
4
+ prefixResponses = [];
5
+ callCount = 0;
6
+ totalInputTokens = 0;
7
+ totalOutputTokens = 0;
8
+ calls = [];
9
+ /** Queue a response to return for the N-th call (0-indexed). */
10
+ queueByIndex(response) {
11
+ this.indexedResponses.push(response);
12
+ return this;
13
+ }
14
+ /** Queue a response to return when userMessage starts with the given prefix. */
15
+ queueByPrefix(prefix, response) {
16
+ this.prefixResponses.push({ prefix, response });
17
+ return this;
18
+ }
19
+ async chat(req) {
20
+ this.calls.push(req);
21
+ const idx = this.callCount++;
22
+ for (const matcher of this.prefixResponses) {
23
+ if (req.userMessage.startsWith(matcher.prefix) || req.systemPrompt.startsWith(matcher.prefix)) {
24
+ return this.buildResponse(matcher.response);
25
+ }
26
+ }
27
+ const indexed = this.indexedResponses[idx];
28
+ if (indexed) {
29
+ return this.buildResponse(indexed);
30
+ }
31
+ return this.buildResponse({ content: `mock-response-${idx}` });
32
+ }
33
+ buildResponse(r) {
34
+ const tokens = r.tokens ?? { input: 10, output: 5 };
35
+ this.totalInputTokens += tokens.input;
36
+ this.totalOutputTokens += tokens.output;
37
+ return { content: r.content, tokens };
38
+ }
39
+ /** Returns all recorded calls. */
40
+ getCalls() {
41
+ return this.calls;
42
+ }
43
+ /** Returns the number of chat() calls made. */
44
+ getCallCount() {
45
+ return this.callCount;
46
+ }
47
+ /** Assert the provider was called exactly N times. */
48
+ assertCallCount(expected) {
49
+ if (this.callCount !== expected) {
50
+ throw new Error(`Expected ${expected} LLM call(s), got ${this.callCount}`);
51
+ }
52
+ }
53
+ /** Assert accumulated token totals. */
54
+ assertTotalTokens(expected) {
55
+ if (expected.input !== void 0 && this.totalInputTokens !== expected.input) {
56
+ throw new Error(`Expected ${expected.input} input tokens, got ${this.totalInputTokens}`);
57
+ }
58
+ if (expected.output !== void 0 && this.totalOutputTokens !== expected.output) {
59
+ throw new Error(`Expected ${expected.output} output tokens, got ${this.totalOutputTokens}`);
60
+ }
61
+ }
62
+ getTotalTokens() {
63
+ return { input: this.totalInputTokens, output: this.totalOutputTokens };
64
+ }
65
+ reset() {
66
+ this.indexedResponses.length = 0;
67
+ this.prefixResponses.length = 0;
68
+ this.calls.length = 0;
69
+ this.callCount = 0;
70
+ this.totalInputTokens = 0;
71
+ this.totalOutputTokens = 0;
72
+ }
73
+ };
74
+
75
+ // src/testing/index.ts
76
+ var __testingPlaceholder__ = /* @__PURE__ */ Symbol.for(
77
+ "@denisvieiradev/gitwise-core/testing#placeholder"
78
+ );
79
+ export {
80
+ MockLLMProvider,
81
+ __testingPlaceholder__
82
+ };
83
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/testing/mock-llm-provider.ts","../../src/testing/index.ts"],"sourcesContent":["import type { LLMProvider, LLMChatRequest, LLMChatResponse } from \"../providers/types.js\";\n\nexport interface MockResponse {\n content: string;\n tokens?: { input: number; output: number };\n}\n\nexport interface PrefixMatcher {\n prefix: string;\n response: MockResponse;\n}\n\n/**\n * MockLLMProvider — deterministic LLM stub for tests.\n *\n * Usage:\n * const mock = new MockLLMProvider();\n * mock.queueByIndex({ content: \"result\" }); // first call\n * mock.queueByPrefix(\"Analyze\", { content: \"analysis\" }); // by prompt prefix\n * await someFunction(mock);\n * mock.assertCallCount(2);\n * mock.assertTotalTokens({ input: 10, output: 5 });\n */\nexport class MockLLMProvider implements LLMProvider {\n private readonly indexedResponses: MockResponse[] = [];\n private readonly prefixResponses: PrefixMatcher[] = [];\n private callCount = 0;\n private totalInputTokens = 0;\n private totalOutputTokens = 0;\n private readonly calls: LLMChatRequest[] = [];\n\n /** Queue a response to return for the N-th call (0-indexed). */\n queueByIndex(response: MockResponse): this {\n this.indexedResponses.push(response);\n return this;\n }\n\n /** Queue a response to return when userMessage starts with the given prefix. */\n queueByPrefix(prefix: string, response: MockResponse): this {\n this.prefixResponses.push({ prefix, response });\n return this;\n }\n\n async chat(req: LLMChatRequest): Promise<LLMChatResponse> {\n this.calls.push(req);\n const idx = this.callCount++;\n\n // Prefix matching takes precedence\n for (const matcher of this.prefixResponses) {\n if (req.userMessage.startsWith(matcher.prefix) || req.systemPrompt.startsWith(matcher.prefix)) {\n return this.buildResponse(matcher.response);\n }\n }\n\n // Index-based fallback\n const indexed = this.indexedResponses[idx];\n if (indexed) {\n return this.buildResponse(indexed);\n }\n\n // Default response\n return this.buildResponse({ content: `mock-response-${idx}` });\n }\n\n private buildResponse(r: MockResponse): LLMChatResponse {\n const tokens = r.tokens ?? { input: 10, output: 5 };\n this.totalInputTokens += tokens.input;\n this.totalOutputTokens += tokens.output;\n return { content: r.content, tokens };\n }\n\n /** Returns all recorded calls. */\n getCalls(): readonly LLMChatRequest[] {\n return this.calls;\n }\n\n /** Returns the number of chat() calls made. */\n getCallCount(): number {\n return this.callCount;\n }\n\n /** Assert the provider was called exactly N times. */\n assertCallCount(expected: number): void {\n if (this.callCount !== expected) {\n throw new Error(`Expected ${expected} LLM call(s), got ${this.callCount}`);\n }\n }\n\n /** Assert accumulated token totals. */\n assertTotalTokens(expected: { input?: number; output?: number }): void {\n if (expected.input !== undefined && this.totalInputTokens !== expected.input) {\n throw new Error(`Expected ${expected.input} input tokens, got ${this.totalInputTokens}`);\n }\n if (expected.output !== undefined && this.totalOutputTokens !== expected.output) {\n throw new Error(`Expected ${expected.output} output tokens, got ${this.totalOutputTokens}`);\n }\n }\n\n getTotalTokens(): { input: number; output: number } {\n return { input: this.totalInputTokens, output: this.totalOutputTokens };\n }\n\n reset(): void {\n this.indexedResponses.length = 0;\n this.prefixResponses.length = 0;\n this.calls.length = 0;\n this.callCount = 0;\n this.totalInputTokens = 0;\n this.totalOutputTokens = 0;\n }\n}\n","export const __testingPlaceholder__ = Symbol.for(\n \"@denisvieiradev/gitwise-core/testing#placeholder\",\n);\n\nexport { MockLLMProvider } from \"./mock-llm-provider.js\";\nexport type { MockResponse, PrefixMatcher } from \"./mock-llm-provider.js\";\n"],"mappings":";AAuBO,IAAM,kBAAN,MAA6C;AAAA,EACjC,mBAAmC,CAAC;AAAA,EACpC,kBAAmC,CAAC;AAAA,EAC7C,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACX,QAA0B,CAAC;AAAA;AAAA,EAG5C,aAAa,UAA8B;AACzC,SAAK,iBAAiB,KAAK,QAAQ;AACnC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,cAAc,QAAgB,UAA8B;AAC1D,SAAK,gBAAgB,KAAK,EAAE,QAAQ,SAAS,CAAC;AAC9C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,KAA+C;AACxD,SAAK,MAAM,KAAK,GAAG;AACnB,UAAM,MAAM,KAAK;AAGjB,eAAW,WAAW,KAAK,iBAAiB;AAC1C,UAAI,IAAI,YAAY,WAAW,QAAQ,MAAM,KAAK,IAAI,aAAa,WAAW,QAAQ,MAAM,GAAG;AAC7F,eAAO,KAAK,cAAc,QAAQ,QAAQ;AAAA,MAC5C;AAAA,IACF;AAGA,UAAM,UAAU,KAAK,iBAAiB,GAAG;AACzC,QAAI,SAAS;AACX,aAAO,KAAK,cAAc,OAAO;AAAA,IACnC;AAGA,WAAO,KAAK,cAAc,EAAE,SAAS,iBAAiB,GAAG,GAAG,CAAC;AAAA,EAC/D;AAAA,EAEQ,cAAc,GAAkC;AACtD,UAAM,SAAS,EAAE,UAAU,EAAE,OAAO,IAAI,QAAQ,EAAE;AAClD,SAAK,oBAAoB,OAAO;AAChC,SAAK,qBAAqB,OAAO;AACjC,WAAO,EAAE,SAAS,EAAE,SAAS,OAAO;AAAA,EACtC;AAAA;AAAA,EAGA,WAAsC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,eAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,gBAAgB,UAAwB;AACtC,QAAI,KAAK,cAAc,UAAU;AAC/B,YAAM,IAAI,MAAM,YAAY,QAAQ,qBAAqB,KAAK,SAAS,EAAE;AAAA,IAC3E;AAAA,EACF;AAAA;AAAA,EAGA,kBAAkB,UAAqD;AACrE,QAAI,SAAS,UAAU,UAAa,KAAK,qBAAqB,SAAS,OAAO;AAC5E,YAAM,IAAI,MAAM,YAAY,SAAS,KAAK,sBAAsB,KAAK,gBAAgB,EAAE;AAAA,IACzF;AACA,QAAI,SAAS,WAAW,UAAa,KAAK,sBAAsB,SAAS,QAAQ;AAC/E,YAAM,IAAI,MAAM,YAAY,SAAS,MAAM,uBAAuB,KAAK,iBAAiB,EAAE;AAAA,IAC5F;AAAA,EACF;AAAA,EAEA,iBAAoD;AAClD,WAAO,EAAE,OAAO,KAAK,kBAAkB,QAAQ,KAAK,kBAAkB;AAAA,EACxE;AAAA,EAEA,QAAc;AACZ,SAAK,iBAAiB,SAAS;AAC/B,SAAK,gBAAgB,SAAS;AAC9B,SAAK,MAAM,SAAS;AACpB,SAAK,YAAY;AACjB,SAAK,mBAAmB;AACxB,SAAK,oBAAoB;AAAA,EAC3B;AACF;;;AC9GO,IAAM,yBAAyB,uBAAO;AAAA,EAC3C;AACF;","names":[]}
@@ -0,0 +1,29 @@
1
+ type ModelTier = "fast" | "balanced" | "powerful";
2
+ interface LLMChatRequest {
3
+ systemPrompt: string;
4
+ userMessage: string;
5
+ tier: ModelTier;
6
+ }
7
+ interface LLMChatResponse {
8
+ content: string;
9
+ tokens: {
10
+ input: number;
11
+ output: number;
12
+ };
13
+ }
14
+ interface LLMProvider {
15
+ chat(req: LLMChatRequest): Promise<LLMChatResponse>;
16
+ }
17
+ interface ModelConfig {
18
+ fast: string;
19
+ balanced: string;
20
+ powerful: string;
21
+ }
22
+ interface ProviderConfig {
23
+ kind: "api" | "claude-code";
24
+ models: ModelConfig;
25
+ apiKey?: string;
26
+ claudeCliPath?: string;
27
+ }
28
+
29
+ export type { LLMProvider as L, ModelTier as M, ProviderConfig as P, LLMChatRequest as a, LLMChatResponse as b, ModelConfig as c };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@denisvieiradev/gitwise-core",
3
+ "version": "0.1.0",
4
+ "description": "Shared logic for gitwise: non-interactive commit/review/pr/release commands, LLM providers, git/github primitives, prompt templates.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./testing": {
14
+ "types": "./dist/testing/index.d.ts",
15
+ "import": "./dist/testing/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "templates",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "test": "node --experimental-vm-modules ../../node_modules/.bin/jest --passWithNoTests",
28
+ "lint": "tsc --noEmit",
29
+ "typecheck": "tsc --noEmit"
30
+ },
31
+ "keywords": [
32
+ "gitwise",
33
+ "git",
34
+ "ai",
35
+ "claude",
36
+ "commit",
37
+ "pull-request",
38
+ "release",
39
+ "code-review"
40
+ ],
41
+ "author": "Denis Vieira <denisvieira05@gmail.com> (https://github.com/denisvieiradev)",
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/denisvieiradev/gitwise.git",
46
+ "directory": "packages/core"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/denisvieiradev/gitwise/issues"
50
+ },
51
+ "homepage": "https://github.com/denisvieiradev/gitwise#readme",
52
+ "engines": {
53
+ "node": ">=22.12.0"
54
+ },
55
+ "dependencies": {
56
+ "@anthropic-ai/sdk": "^0.109.0"
57
+ }
58
+ }
File without changes
@@ -0,0 +1 @@
1
+ {{type}}({{scope}}): {{description}}
@@ -0,0 +1,11 @@
1
+ ## Summary
2
+
3
+ {{summary}}
4
+
5
+ ## Changes
6
+
7
+ {{changelog}}
8
+
9
+ ## Test Plan
10
+
11
+ {{test_plan}}
@@ -0,0 +1,19 @@
1
+ You are a technical writer generating a changelog entry following the Keep a Changelog format (https://keepachangelog.com).
2
+
3
+ Group changes under these sections (omit empty sections):
4
+ ### Added
5
+ ### Changed
6
+ ### Fixed
7
+ ### Removed
8
+ ### Deprecated
9
+ ### Security
10
+
11
+ Rules:
12
+ - Each entry is a bullet point starting with a verb in past tense
13
+ - Be concise but specific; include scope when relevant (e.g., "Added `release` command for automated versioning")
14
+ - Do NOT include commit hashes, author names, or dates
15
+ - Do NOT include a version header — it will be added programmatically
16
+ - Do NOT wrap the output in markdown fences
17
+ - Collapse minor related changes into single entries when appropriate
18
+
19
+ Project: {{projectName}}
@@ -0,0 +1,13 @@
1
+ You are a product communications specialist writing user-facing release notes for version {{version}} of {{projectName}}.
2
+
3
+ Write entirely in {{language}}.
4
+
5
+ Rules:
6
+ - Write for non-technical users (product managers, stakeholders, end users)
7
+ - Focus on WHAT changed and WHY it matters, not HOW it was implemented
8
+ - Use a friendly, professional tone
9
+ - Start with a brief summary paragraph (2-3 sentences)
10
+ - Then list highlights as bullet points
11
+ - End with a brief closing note if appropriate
12
+ - Do NOT include code snippets, file paths, commit hashes, or technical jargon
13
+ - Do NOT wrap the output in markdown fences
@@ -0,0 +1,11 @@
1
+ You are a release engineer analyzing git commits to determine the appropriate semantic version bump.
2
+
3
+ Rules:
4
+ - MAJOR: breaking changes, removed public APIs, incompatible interface changes
5
+ - MINOR: new features, new commands, new public APIs (backward compatible)
6
+ - PATCH: bug fixes, documentation updates, refactoring, dependency updates, chore tasks
7
+
8
+ Current version: {{currentVersion}}
9
+
10
+ Analyze the commits below and respond with JSON only (no markdown fences, no extra text):
11
+ {"suggestion": "major|minor|patch", "reasoning": "one sentence explaining why"}
@@ -0,0 +1,19 @@
1
+ You are a senior code reviewer. Analyze the diff and produce a code review with findings in these categories:
2
+
3
+ ## Critical
4
+ Issues that must be fixed before merging (bugs, security, data loss).
5
+
6
+ ## Suggestions
7
+ Improvements worth considering (performance, readability, patterns).
8
+
9
+ ## Nitpicks
10
+ Minor style or convention issues.
11
+
12
+ For each finding, include:
13
+ - File and line reference
14
+ - Description of the issue
15
+ - Suggested fix
16
+
17
+ End with a summary: total findings count per category and overall recommendation (approve, request changes).
18
+
19
+ {{diff}}