@towles/tool 0.0.41 → 0.0.48
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/README.md +67 -109
- package/package.json +51 -41
- package/src/commands/base.ts +3 -18
- package/src/commands/config.ts +9 -8
- package/src/commands/doctor.ts +4 -1
- package/src/commands/gh/branch-clean.ts +10 -4
- package/src/commands/gh/branch.ts +6 -3
- package/src/commands/gh/pr.ts +10 -3
- package/src/commands/graph-template.html +1214 -0
- package/src/commands/graph.test.ts +176 -0
- package/src/commands/graph.ts +970 -0
- package/src/commands/install.ts +8 -2
- package/src/commands/journal/daily-notes.ts +9 -5
- package/src/commands/journal/meeting.ts +12 -6
- package/src/commands/journal/note.ts +12 -6
- package/src/commands/ralph/plan/add.ts +75 -0
- package/src/commands/ralph/plan/done.ts +82 -0
- package/src/commands/ralph/{task → plan}/list.test.ts +5 -5
- package/src/commands/ralph/{task → plan}/list.ts +28 -39
- package/src/commands/ralph/plan/remove.ts +71 -0
- package/src/commands/ralph/run.test.ts +521 -0
- package/src/commands/ralph/run.ts +126 -189
- package/src/commands/ralph/show.ts +88 -0
- package/src/config/settings.ts +8 -27
- package/src/{commands/ralph/lib → lib/ralph}/execution.ts +4 -14
- package/src/lib/ralph/formatter.ts +238 -0
- package/src/{commands/ralph/lib → lib/ralph}/state.ts +17 -42
- package/src/utils/date-utils.test.ts +2 -1
- package/src/utils/date-utils.ts +2 -2
- package/LICENSE.md +0 -20
- package/src/commands/index.ts +0 -55
- package/src/commands/observe/graph.test.ts +0 -89
- package/src/commands/observe/graph.ts +0 -1640
- package/src/commands/observe/report.ts +0 -166
- package/src/commands/observe/session.ts +0 -385
- package/src/commands/observe/setup.ts +0 -180
- package/src/commands/observe/status.ts +0 -146
- package/src/commands/ralph/lib/formatter.ts +0 -298
- package/src/commands/ralph/lib/marker.ts +0 -108
- package/src/commands/ralph/marker/create.ts +0 -23
- package/src/commands/ralph/plan.ts +0 -73
- package/src/commands/ralph/progress.ts +0 -44
- package/src/commands/ralph/ralph.test.ts +0 -673
- package/src/commands/ralph/task/add.ts +0 -105
- package/src/commands/ralph/task/done.ts +0 -73
- package/src/commands/ralph/task/remove.ts +0 -62
- package/src/config/context.ts +0 -7
- package/src/constants.ts +0 -3
- package/src/utils/anthropic/types.ts +0 -158
- package/src/utils/exec.ts +0 -8
- package/src/utils/git/git.ts +0 -25
- /package/src/{commands → lib}/journal/utils.ts +0 -0
- /package/src/{commands/ralph/lib → lib/ralph}/index.ts +0 -0
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from "@oclif/core";
|
|
2
|
-
import pc from "picocolors";
|
|
3
|
-
import { BaseCommand } from "../../base.js";
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_STATE_FILE,
|
|
6
|
-
DEFAULT_MAX_ITERATIONS,
|
|
7
|
-
loadState,
|
|
8
|
-
saveState,
|
|
9
|
-
createInitialState,
|
|
10
|
-
addTaskToState,
|
|
11
|
-
resolveRalphPath,
|
|
12
|
-
} from "../lib/state.js";
|
|
13
|
-
import { findSessionByMarker } from "../lib/marker.js";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Add a new task to ralph state
|
|
17
|
-
*/
|
|
18
|
-
export default class TaskAdd extends BaseCommand {
|
|
19
|
-
static override description = "Add a new task";
|
|
20
|
-
|
|
21
|
-
static override examples = [
|
|
22
|
-
'<%= config.bin %> ralph task add "Fix the login bug"',
|
|
23
|
-
'<%= config.bin %> ralph task add "Implement feature X" --sessionId abc123',
|
|
24
|
-
'<%= config.bin %> ralph task add "Implement feature X" --findMarker RALPH_MARKER_abc123',
|
|
25
|
-
'<%= config.bin %> ralph task add "Backend refactor" --label backend',
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
static override args = {
|
|
29
|
-
description: Args.string({
|
|
30
|
-
description: "Task description",
|
|
31
|
-
required: true,
|
|
32
|
-
}),
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
static override flags = {
|
|
36
|
-
...BaseCommand.baseFlags,
|
|
37
|
-
stateFile: Flags.string({
|
|
38
|
-
char: "s",
|
|
39
|
-
description: `State file path (default: ${DEFAULT_STATE_FILE})`,
|
|
40
|
-
}),
|
|
41
|
-
sessionId: Flags.string({
|
|
42
|
-
description: "Claude session ID for resuming from prior research",
|
|
43
|
-
}),
|
|
44
|
-
findMarker: Flags.string({
|
|
45
|
-
char: "m",
|
|
46
|
-
description: "Find session by full marker (e.g., RALPH_MARKER_abc123)",
|
|
47
|
-
}),
|
|
48
|
-
label: Flags.string({
|
|
49
|
-
char: "l",
|
|
50
|
-
description: "Label for grouping/filtering tasks",
|
|
51
|
-
}),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
async run(): Promise<void> {
|
|
55
|
-
const { args, flags } = await this.parse(TaskAdd);
|
|
56
|
-
const ralphSettings = this.settings.settingsFile.settings.ralphSettings;
|
|
57
|
-
const stateFile = resolveRalphPath(flags.stateFile, "stateFile", ralphSettings);
|
|
58
|
-
|
|
59
|
-
const description = args.description.trim();
|
|
60
|
-
|
|
61
|
-
if (!description || description.length < 3) {
|
|
62
|
-
this.error("Task description too short (min 3 chars)");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Resolve session ID from --sessionId or --findMarker
|
|
66
|
-
let sessionId = flags.sessionId;
|
|
67
|
-
let marker: string | undefined;
|
|
68
|
-
if (flags.findMarker) {
|
|
69
|
-
if (sessionId) {
|
|
70
|
-
this.error("Cannot use both --sessionId and --findMarker");
|
|
71
|
-
}
|
|
72
|
-
marker = flags.findMarker;
|
|
73
|
-
console.log(pc.dim(`Searching for marker: ${marker}...`));
|
|
74
|
-
sessionId = (await findSessionByMarker(marker)) ?? undefined;
|
|
75
|
-
if (!sessionId) {
|
|
76
|
-
this.error(
|
|
77
|
-
`Marker not found: ${marker}\nMake sure Claude output this marker during research.`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
console.log(pc.cyan(`Found session: ${sessionId.slice(0, 8)}...`));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let state = loadState(stateFile);
|
|
84
|
-
|
|
85
|
-
if (!state) {
|
|
86
|
-
state = createInitialState(DEFAULT_MAX_ITERATIONS);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const newTask = addTaskToState(state, description, sessionId, marker, flags.label);
|
|
90
|
-
saveState(state, stateFile);
|
|
91
|
-
|
|
92
|
-
console.log(pc.green(`✓ Added task #${newTask.id}: ${newTask.description}`));
|
|
93
|
-
if (flags.label) {
|
|
94
|
-
console.log(pc.cyan(` Label: ${flags.label}`));
|
|
95
|
-
}
|
|
96
|
-
if (sessionId) {
|
|
97
|
-
console.log(pc.cyan(` Session: ${sessionId.slice(0, 8)}...`));
|
|
98
|
-
}
|
|
99
|
-
if (marker) {
|
|
100
|
-
console.log(pc.dim(` Marker: ${marker}`));
|
|
101
|
-
}
|
|
102
|
-
console.log(pc.dim(`State saved to: ${stateFile}`));
|
|
103
|
-
console.log(pc.dim(`Total tasks: ${state.tasks.length}`));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from "@oclif/core";
|
|
2
|
-
import pc from "picocolors";
|
|
3
|
-
import { BaseCommand } from "../../base.js";
|
|
4
|
-
import { DEFAULT_STATE_FILE, loadState, saveState, resolveRalphPath } from "../lib/state.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Mark a ralph task as done
|
|
8
|
-
*/
|
|
9
|
-
export default class TaskDone extends BaseCommand {
|
|
10
|
-
static override description = "Mark a task as done by ID";
|
|
11
|
-
|
|
12
|
-
static override examples = [
|
|
13
|
-
"<%= config.bin %> ralph task done 1",
|
|
14
|
-
"<%= config.bin %> ralph task done 5 --stateFile custom-state.json",
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
static override args = {
|
|
18
|
-
id: Args.integer({
|
|
19
|
-
description: "Task ID to mark done",
|
|
20
|
-
required: true,
|
|
21
|
-
}),
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
static override flags = {
|
|
25
|
-
...BaseCommand.baseFlags,
|
|
26
|
-
stateFile: Flags.string({
|
|
27
|
-
char: "s",
|
|
28
|
-
description: `State file path (default: ${DEFAULT_STATE_FILE})`,
|
|
29
|
-
}),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
async run(): Promise<void> {
|
|
33
|
-
const { args, flags } = await this.parse(TaskDone);
|
|
34
|
-
const ralphSettings = this.settings.settingsFile.settings.ralphSettings;
|
|
35
|
-
const stateFile = resolveRalphPath(flags.stateFile, "stateFile", ralphSettings);
|
|
36
|
-
|
|
37
|
-
const taskId = args.id;
|
|
38
|
-
|
|
39
|
-
if (taskId < 1) {
|
|
40
|
-
this.error("Invalid task ID");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const state = loadState(stateFile);
|
|
44
|
-
|
|
45
|
-
if (!state) {
|
|
46
|
-
this.error(`No state file found at: ${stateFile}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const task = state.tasks.find((t) => t.id === taskId);
|
|
50
|
-
|
|
51
|
-
if (!task) {
|
|
52
|
-
this.error(`Task #${taskId} not found. Use: tt ralph task list`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (task.status === "done") {
|
|
56
|
-
console.log(pc.yellow(`Task #${taskId} is already done.`));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
task.status = "done";
|
|
61
|
-
task.completedAt = new Date().toISOString();
|
|
62
|
-
saveState(state, stateFile);
|
|
63
|
-
|
|
64
|
-
console.log(pc.green(`✓ Marked task #${taskId} as done: ${task.description}`));
|
|
65
|
-
|
|
66
|
-
const remaining = state.tasks.filter((t) => t.status !== "done").length;
|
|
67
|
-
if (remaining === 0) {
|
|
68
|
-
console.log(pc.bold(pc.green("All tasks complete!")));
|
|
69
|
-
} else {
|
|
70
|
-
console.log(pc.dim(`Remaining tasks: ${remaining}`));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { Args, Flags } from "@oclif/core";
|
|
2
|
-
import pc from "picocolors";
|
|
3
|
-
import { BaseCommand } from "../../base.js";
|
|
4
|
-
import { DEFAULT_STATE_FILE, loadState, saveState, resolveRalphPath } from "../lib/state.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Remove a ralph task by ID
|
|
8
|
-
*/
|
|
9
|
-
export default class TaskRemove extends BaseCommand {
|
|
10
|
-
static override description = "Remove a task by ID";
|
|
11
|
-
|
|
12
|
-
static override examples = [
|
|
13
|
-
"<%= config.bin %> ralph task remove 1",
|
|
14
|
-
"<%= config.bin %> ralph task remove 5 --stateFile custom-state.json",
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
static override args = {
|
|
18
|
-
id: Args.integer({
|
|
19
|
-
description: "Task ID to remove",
|
|
20
|
-
required: true,
|
|
21
|
-
}),
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
static override flags = {
|
|
25
|
-
...BaseCommand.baseFlags,
|
|
26
|
-
stateFile: Flags.string({
|
|
27
|
-
char: "s",
|
|
28
|
-
description: `State file path (default: ${DEFAULT_STATE_FILE})`,
|
|
29
|
-
}),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
async run(): Promise<void> {
|
|
33
|
-
const { args, flags } = await this.parse(TaskRemove);
|
|
34
|
-
const ralphSettings = this.settings.settingsFile.settings.ralphSettings;
|
|
35
|
-
const stateFile = resolveRalphPath(flags.stateFile, "stateFile", ralphSettings);
|
|
36
|
-
|
|
37
|
-
const taskId = args.id;
|
|
38
|
-
|
|
39
|
-
if (taskId < 1) {
|
|
40
|
-
this.error("Invalid task ID");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const state = loadState(stateFile);
|
|
44
|
-
|
|
45
|
-
if (!state) {
|
|
46
|
-
this.error(`No state file found at: ${stateFile}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const taskIndex = state.tasks.findIndex((t) => t.id === taskId);
|
|
50
|
-
|
|
51
|
-
if (taskIndex === -1) {
|
|
52
|
-
this.error(`Task #${taskId} not found. Use: tt ralph task list`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const removedTask = state.tasks[taskIndex];
|
|
56
|
-
state.tasks.splice(taskIndex, 1);
|
|
57
|
-
saveState(state, stateFile);
|
|
58
|
-
|
|
59
|
-
console.log(pc.green(`✓ Removed task #${taskId}: ${removedTask.description}`));
|
|
60
|
-
console.log(pc.dim(`Remaining tasks: ${state.tasks.length}`));
|
|
61
|
-
}
|
|
62
|
-
}
|
package/src/config/context.ts
DELETED
package/src/constants.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ContentBlock,
|
|
3
|
-
ContentBlockParam,
|
|
4
|
-
Message,
|
|
5
|
-
MessageParam,
|
|
6
|
-
} from "@anthropic-ai/sdk/resources"; // only used for types
|
|
7
|
-
import { z } from "zod/v4";
|
|
8
|
-
|
|
9
|
-
// This file defines types and interfaces for Claude SDK messages and related structures.
|
|
10
|
-
// copied from https://github.com/tuanemuy/cc-jsonl/blob/main/src/core/domain/claude/types.ts
|
|
11
|
-
// which is also using the Anthropic claude-code package.
|
|
12
|
-
|
|
13
|
-
export const sendMessageInputSchema = z.object({
|
|
14
|
-
message: z.string().min(1),
|
|
15
|
-
sessionId: z.string().optional(),
|
|
16
|
-
cwd: z.string().optional(),
|
|
17
|
-
allowedTools: z.array(z.string()).optional(),
|
|
18
|
-
bypassPermissions: z.boolean().optional(),
|
|
19
|
-
});
|
|
20
|
-
export type SendMessageInput = z.infer<typeof sendMessageInputSchema>;
|
|
21
|
-
|
|
22
|
-
export type AssistantContent = ContentBlock[];
|
|
23
|
-
export type UserContent = string | ContentBlockParam[];
|
|
24
|
-
|
|
25
|
-
export interface AssistantMessage {
|
|
26
|
-
type: "assistant";
|
|
27
|
-
message: Message; // From Anthropic SDK
|
|
28
|
-
session_id: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function isAssistantMessage(message: SDKMessage): message is AssistantMessage {
|
|
32
|
-
return message.type === "assistant";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface UserMessage {
|
|
36
|
-
type: "user";
|
|
37
|
-
message: MessageParam; // Anthropic SDK
|
|
38
|
-
session_id: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function isUserMessage(message: SDKMessage): message is UserMessage {
|
|
42
|
-
return message.type === "user";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface ResultMessage {
|
|
46
|
-
type: "result";
|
|
47
|
-
subtype: "success" | "error_max_turns" | "error_during_execution";
|
|
48
|
-
duration_ms: number;
|
|
49
|
-
duration_api_ms: number;
|
|
50
|
-
is_error: boolean;
|
|
51
|
-
num_turns: number;
|
|
52
|
-
result?: string; // Only on success
|
|
53
|
-
session_id: string;
|
|
54
|
-
total_cost_usd: number;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function isResultMessage(message: SDKMessage): message is ResultMessage {
|
|
58
|
-
return message.type === "result";
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface SystemMessage {
|
|
62
|
-
type: "system";
|
|
63
|
-
subtype: "init";
|
|
64
|
-
apiKeySource: string;
|
|
65
|
-
cwd: string;
|
|
66
|
-
session_id: string;
|
|
67
|
-
tools: string[];
|
|
68
|
-
mcp_servers: {
|
|
69
|
-
name: string;
|
|
70
|
-
status: string;
|
|
71
|
-
}[];
|
|
72
|
-
model: string;
|
|
73
|
-
permissionMode: "default" | "acceptEdits" | "bypassPermissions" | "plan";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function isSystemMessage(message: SDKMessage): message is SystemMessage {
|
|
77
|
-
return message.type === "system";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export type SDKMessage = AssistantMessage | UserMessage | ResultMessage | SystemMessage;
|
|
81
|
-
|
|
82
|
-
// ChunkData is simply an SDKMessage
|
|
83
|
-
// The SDK already provides messages in the appropriate granularity for streaming
|
|
84
|
-
export type ChunkData = SDKMessage;
|
|
85
|
-
|
|
86
|
-
// Tool result type for handling tool execution results
|
|
87
|
-
export interface ToolResult {
|
|
88
|
-
type: "tool_result";
|
|
89
|
-
tool_use_id: string;
|
|
90
|
-
content?: string | Record<string, unknown>[];
|
|
91
|
-
is_error?: boolean;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function isToolResult(obj: unknown): obj is ToolResult {
|
|
95
|
-
return (
|
|
96
|
-
typeof obj === "object" &&
|
|
97
|
-
obj !== null &&
|
|
98
|
-
"type" in obj &&
|
|
99
|
-
obj.type === "tool_result" &&
|
|
100
|
-
"tool_use_id" in obj &&
|
|
101
|
-
typeof (obj as ToolResult).tool_use_id === "string"
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Type guard helpers for parseSDKMessage - use unknown input to allow type narrowing
|
|
106
|
-
function isValidAssistantMessage(obj: unknown): obj is AssistantMessage {
|
|
107
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
108
|
-
const o = obj as Record<string, unknown>;
|
|
109
|
-
return (
|
|
110
|
-
o.type === "assistant" &&
|
|
111
|
-
o.message !== null &&
|
|
112
|
-
typeof o.message === "object" &&
|
|
113
|
-
typeof o.session_id === "string"
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function isValidUserMessage(obj: unknown): obj is UserMessage {
|
|
118
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
119
|
-
const o = obj as Record<string, unknown>;
|
|
120
|
-
return (
|
|
121
|
-
o.type === "user" &&
|
|
122
|
-
o.message !== null &&
|
|
123
|
-
typeof o.message === "object" &&
|
|
124
|
-
typeof o.session_id === "string"
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function isValidResultMessage(obj: unknown): obj is ResultMessage {
|
|
129
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
130
|
-
const o = obj as Record<string, unknown>;
|
|
131
|
-
return (
|
|
132
|
-
o.type === "result" &&
|
|
133
|
-
typeof o.session_id === "string" &&
|
|
134
|
-
typeof o.subtype === "string" &&
|
|
135
|
-
typeof o.duration_ms === "number" &&
|
|
136
|
-
typeof o.is_error === "boolean"
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function isValidSystemMessage(obj: unknown): obj is SystemMessage {
|
|
141
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
142
|
-
const o = obj as Record<string, unknown>;
|
|
143
|
-
return (
|
|
144
|
-
o.type === "system" &&
|
|
145
|
-
typeof o.session_id === "string" &&
|
|
146
|
-
typeof o.subtype === "string" &&
|
|
147
|
-
typeof o.cwd === "string"
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Helper function to safely parse SDKMessage
|
|
152
|
-
export function parseSDKMessage(data: unknown): SDKMessage | null {
|
|
153
|
-
if (isValidAssistantMessage(data)) return data;
|
|
154
|
-
if (isValidUserMessage(data)) return data;
|
|
155
|
-
if (isValidResultMessage(data)) return data;
|
|
156
|
-
if (isValidSystemMessage(data)) return data;
|
|
157
|
-
return null;
|
|
158
|
-
}
|
package/src/utils/exec.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
// TODO change to use tinyexec or similar for better error handling
|
|
4
|
-
export function execCommand(cmd: string, cwd?: string) {
|
|
5
|
-
// Note about execSync, if the command fails or times out, it might not throw an error,
|
|
6
|
-
// if the child process intercepts the SIGTERM signal, we might not get an error.
|
|
7
|
-
return execSync(cmd, { encoding: "utf8", cwd }).trim();
|
|
8
|
-
}
|
package/src/utils/git/git.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// unJs/changelogen has some nice utilities for git
|
|
2
|
-
import { execCommand } from "../exec";
|
|
3
|
-
|
|
4
|
-
// using logic from https://github.com/unjs/changelogen/blob/main/src/git.ts
|
|
5
|
-
|
|
6
|
-
export async function getGitDiff(cwd: string): Promise<string> {
|
|
7
|
-
// https://git-scm.com/docs/pretty-formats
|
|
8
|
-
let r = execCommand(
|
|
9
|
-
// `git --no-pager log "${from ? `${from}...` : ''}${to}" `,
|
|
10
|
-
// using --cached because staged didn't work on MacOS
|
|
11
|
-
`git --no-pager diff --cached`,
|
|
12
|
-
// --name-status
|
|
13
|
-
// --pretty="----%n%s|%h|%an|%ae%n%b"
|
|
14
|
-
cwd,
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
// Limit output to 1000 lines maximum
|
|
18
|
-
const lines = r.split("\n");
|
|
19
|
-
if (lines.length > 1000) {
|
|
20
|
-
r = lines.slice(0, 1000).join("\n");
|
|
21
|
-
r += `\n\n[Output truncated to 1000 lines]`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return r;
|
|
25
|
-
}
|
|
File without changes
|
|
File without changes
|