wave-agent-sdk 1.0.4 → 1.0.6
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/dist/agent.d.ts +23 -1
- package/dist/agent.js +34 -4
- package/dist/managers/aiManager.d.ts +36 -0
- package/dist/managers/aiManager.js +240 -8
- package/dist/managers/messageManager.d.ts +1 -3
- package/dist/managers/messageManager.js +19 -9
- package/dist/managers/slashCommandManager.js +28 -0
- package/dist/managers/subagentManager.d.ts +0 -2
- package/dist/services/session.js +2 -9
- package/dist/telemetry/instrumentation.d.ts +1 -1
- package/dist/telemetry/instrumentation.js +57 -28
- package/dist/tools/bashTool.js +16 -6
- package/dist/tools/grepTool.js +11 -3
- package/dist/types/index.d.ts +3 -0
- package/dist/types/messaging.d.ts +2 -0
- package/dist/utils/containerSetup.js +0 -3
- package/dist/utils/messageOperations.d.ts +10 -1
- package/dist/utils/messageOperations.js +19 -1
- package/dist/utils/notificationXml.js +5 -0
- package/dist/utils/ripgrep.d.ts +4 -4
- package/dist/utils/ripgrep.js +4 -27
- package/package.json +20 -8
- package/bin/rg +0 -79
- package/dist/managers/forkedAgentManager.d.ts +0 -50
- package/dist/managers/forkedAgentManager.js +0 -130
- package/scripts/install_ripgrep.js +0 -111
- package/scripts/postinstall.js +0 -38
- package/src/agent.ts +0 -1271
- package/src/constants/subagents.ts +0 -4
- package/src/constants/toolLimits.ts +0 -15
- package/src/constants/tools.ts +0 -24
- package/src/core/plugin.ts +0 -237
- package/src/core/session.ts +0 -9
- package/src/index.ts +0 -38
- package/src/managers/MemoryRuleManager.ts +0 -198
- package/src/managers/aiManager.ts +0 -2429
- package/src/managers/backgroundTaskManager.ts +0 -480
- package/src/managers/bangManager.ts +0 -119
- package/src/managers/cronManager.ts +0 -401
- package/src/managers/foregroundTaskManager.ts +0 -30
- package/src/managers/forkedAgentManager.ts +0 -216
- package/src/managers/hookManager.ts +0 -1089
- package/src/managers/liveConfigManager.ts +0 -445
- package/src/managers/lspManager.ts +0 -470
- package/src/managers/mcpManager.ts +0 -1010
- package/src/managers/messageManager.ts +0 -1180
- package/src/managers/messageQueue.ts +0 -157
- package/src/managers/permissionManager.ts +0 -1140
- package/src/managers/planManager.ts +0 -112
- package/src/managers/pluginManager.ts +0 -330
- package/src/managers/pluginScopeManager.ts +0 -124
- package/src/managers/reversionManager.ts +0 -178
- package/src/managers/skillManager.ts +0 -578
- package/src/managers/slashCommandManager.ts +0 -564
- package/src/managers/subagentManager.ts +0 -915
- package/src/managers/toolManager.ts +0 -489
- package/src/managers/workflowManager.ts +0 -493
- package/src/prompts/autoMemory.ts +0 -33
- package/src/prompts/autoMemoryExtraction.ts +0 -146
- package/src/prompts/index.ts +0 -506
- package/src/prompts/planModeReminders.ts +0 -126
- package/src/services/GitService.ts +0 -131
- package/src/services/MarketplaceService.ts +0 -1052
- package/src/services/MemoryRuleService.ts +0 -71
- package/src/services/aiService.ts +0 -919
- package/src/services/authService.ts +0 -565
- package/src/services/autoMemoryService.ts +0 -280
- package/src/services/configurationService.ts +0 -1520
- package/src/services/fileWatcher.ts +0 -319
- package/src/services/hook.ts +0 -348
- package/src/services/initializationService.ts +0 -357
- package/src/services/interactionService.ts +0 -229
- package/src/services/jsonlHandler.ts +0 -295
- package/src/services/memory.ts +0 -234
- package/src/services/pluginLoader.ts +0 -252
- package/src/services/remoteSettingsService.ts +0 -366
- package/src/services/reversionService.ts +0 -122
- package/src/services/session.ts +0 -922
- package/src/services/taskManager.ts +0 -321
- package/src/telemetry/events.ts +0 -65
- package/src/telemetry/instrumentation.ts +0 -499
- package/src/telemetry/sessionTracing.ts +0 -348
- package/src/tools/agentTool.ts +0 -322
- package/src/tools/askUserQuestion.ts +0 -160
- package/src/tools/bashTool.ts +0 -621
- package/src/tools/buildTool.ts +0 -61
- package/src/tools/cronCreateTool.ts +0 -161
- package/src/tools/cronDeleteTool.ts +0 -51
- package/src/tools/cronListTool.ts +0 -47
- package/src/tools/editTool.ts +0 -313
- package/src/tools/enterPlanMode.ts +0 -124
- package/src/tools/enterWorktreeTool.ts +0 -190
- package/src/tools/exitPlanMode.ts +0 -139
- package/src/tools/exitWorktreeTool.ts +0 -236
- package/src/tools/globTool.ts +0 -167
- package/src/tools/grepTool.ts +0 -399
- package/src/tools/lspTool.ts +0 -883
- package/src/tools/readTool.ts +0 -426
- package/src/tools/skillTool.ts +0 -248
- package/src/tools/taskManagementTools.ts +0 -574
- package/src/tools/taskStopTool.ts +0 -78
- package/src/tools/types.ts +0 -137
- package/src/tools/webFetchTool.ts +0 -373
- package/src/tools/workflowTool.ts +0 -205
- package/src/tools/writeTool.ts +0 -267
- package/src/types/agent.ts +0 -128
- package/src/types/auth.ts +0 -19
- package/src/types/commands.ts +0 -30
- package/src/types/config.ts +0 -43
- package/src/types/configuration.ts +0 -168
- package/src/types/core.ts +0 -105
- package/src/types/cron.ts +0 -13
- package/src/types/environment.ts +0 -106
- package/src/types/fileSearch.ts +0 -4
- package/src/types/history.ts +0 -7
- package/src/types/hooks.ts +0 -256
- package/src/types/index.ts +0 -41
- package/src/types/lsp.ts +0 -98
- package/src/types/marketplace.ts +0 -71
- package/src/types/mcp.ts +0 -43
- package/src/types/memoryRule.ts +0 -31
- package/src/types/messaging.ts +0 -114
- package/src/types/permissions.ts +0 -80
- package/src/types/plugins.ts +0 -39
- package/src/types/processes.ts +0 -99
- package/src/types/reversion.ts +0 -29
- package/src/types/session.ts +0 -12
- package/src/types/skills.ts +0 -108
- package/src/types/tasks.ts +0 -13
- package/src/types/telemetry.ts +0 -98
- package/src/types/tools.ts +0 -44
- package/src/types/workflow.ts +0 -6
- package/src/utils/abortUtils.ts +0 -118
- package/src/utils/atomicWrite.ts +0 -61
- package/src/utils/bashParser.ts +0 -899
- package/src/utils/cacheControlUtils.ts +0 -426
- package/src/utils/commandArgumentParser.ts +0 -161
- package/src/utils/commandPathResolver.ts +0 -88
- package/src/utils/configPaths.ts +0 -220
- package/src/utils/configValidator.ts +0 -100
- package/src/utils/constants.ts +0 -39
- package/src/utils/container.ts +0 -92
- package/src/utils/containerSetup.ts +0 -371
- package/src/utils/convertMessagesForAPI.ts +0 -326
- package/src/utils/cronTasks.ts +0 -128
- package/src/utils/cronTasksLock.ts +0 -182
- package/src/utils/cronToHuman.ts +0 -99
- package/src/utils/customCommands.ts +0 -95
- package/src/utils/editUtils.ts +0 -20
- package/src/utils/fileFormat.ts +0 -40
- package/src/utils/fileSearch.ts +0 -151
- package/src/utils/fileUtils.ts +0 -223
- package/src/utils/gitUtils.ts +0 -315
- package/src/utils/globalLogger.ts +0 -128
- package/src/utils/groupMessagesByApiRound.ts +0 -121
- package/src/utils/hookMatcher.ts +0 -176
- package/src/utils/markdownParser.ts +0 -317
- package/src/utils/mcpUtils.ts +0 -138
- package/src/utils/messageOperations.ts +0 -641
- package/src/utils/modelCapabilities.ts +0 -30
- package/src/utils/nameGenerator.ts +0 -99
- package/src/utils/notificationXml.ts +0 -57
- package/src/utils/openaiClient.ts +0 -251
- package/src/utils/parseCronExpression.ts +0 -78
- package/src/utils/path.ts +0 -72
- package/src/utils/pathEncoder.ts +0 -400
- package/src/utils/pathSafety.ts +0 -40
- package/src/utils/promptHistory.ts +0 -168
- package/src/utils/ripgrep.ts +0 -35
- package/src/utils/shellResolver.ts +0 -190
- package/src/utils/skillParser.ts +0 -246
- package/src/utils/stringUtils.ts +0 -156
- package/src/utils/subagentParser.ts +0 -279
- package/src/utils/taskReminder.ts +0 -96
- package/src/utils/tokenCalculation.ts +0 -43
- package/src/utils/tokenEstimate.ts +0 -34
- package/src/utils/toolResultStorage.ts +0 -117
- package/src/utils/worktreeSession.ts +0 -26
- package/src/utils/worktreeUtils.ts +0 -786
- package/src/workflow/budgetTracker.ts +0 -34
- package/src/workflow/concurrencyLimiter.ts +0 -47
- package/src/workflow/journal.ts +0 -95
- package/src/workflow/progressReporter.ts +0 -141
- package/src/workflow/runState.ts +0 -65
- package/src/workflow/scriptRuntime.ts +0 -274
- package/src/workflow/structuredOutput.ts +0 -123
- package/src/workflow/types.ts +0 -101
- package/src/workflow/workflowApis.ts +0 -410
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/linux-x86_64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
package/src/utils/cronTasks.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readFileSync,
|
|
5
|
-
renameSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
} from "fs";
|
|
8
|
-
import { join } from "path";
|
|
9
|
-
import { randomUUID } from "crypto";
|
|
10
|
-
import { CronJob } from "../types/cron.js";
|
|
11
|
-
|
|
12
|
-
const WAVE_DIR = ".wave";
|
|
13
|
-
const TASKS_FILE = "scheduled_tasks.json";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Runtime-only flags that should never be persisted to disk.
|
|
17
|
-
*/
|
|
18
|
-
const RUNTIME_FLAGS = ["nextRun", "periodMs"] as const;
|
|
19
|
-
|
|
20
|
-
function getFilePath(dir: string): string {
|
|
21
|
-
return join(dir, WAVE_DIR, TASKS_FILE);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function getCronTasksFilePath(dir: string = process.cwd()): string {
|
|
25
|
-
return getFilePath(dir);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function ensureWaveDir(dir: string): void {
|
|
29
|
-
const waveDir = join(dir, WAVE_DIR);
|
|
30
|
-
if (!existsSync(waveDir)) {
|
|
31
|
-
mkdirSync(waveDir, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function stripRuntimeFlags(job: CronJob): Record<string, unknown> {
|
|
36
|
-
const stripped: Record<string, unknown> = {};
|
|
37
|
-
for (const [key, value] of Object.entries(job)) {
|
|
38
|
-
if (!(RUNTIME_FLAGS as readonly string[]).includes(key)) {
|
|
39
|
-
stripped[key] = value;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return stripped;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function parseFile(dir: string): CronJob[] {
|
|
46
|
-
const filePath = getFilePath(dir);
|
|
47
|
-
if (!existsSync(filePath)) return [];
|
|
48
|
-
try {
|
|
49
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
50
|
-
const parsed = JSON.parse(raw);
|
|
51
|
-
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.tasks)) {
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
const tasks: CronJob[] = [];
|
|
55
|
-
for (const entry of parsed.tasks) {
|
|
56
|
-
if (
|
|
57
|
-
typeof entry === "object" &&
|
|
58
|
-
entry !== null &&
|
|
59
|
-
typeof entry.id === "string" &&
|
|
60
|
-
typeof entry.cron === "string" &&
|
|
61
|
-
typeof entry.prompt === "string" &&
|
|
62
|
-
typeof entry.recurring === "boolean" &&
|
|
63
|
-
typeof entry.createdAt === "number"
|
|
64
|
-
) {
|
|
65
|
-
tasks.push(entry as CronJob);
|
|
66
|
-
}
|
|
67
|
-
// Drop malformed entries silently
|
|
68
|
-
}
|
|
69
|
-
return tasks;
|
|
70
|
-
} catch {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function writeToFile(jobs: CronJob[], dir: string): void {
|
|
76
|
-
ensureWaveDir(dir);
|
|
77
|
-
const filePath = getFilePath(dir);
|
|
78
|
-
const payload = {
|
|
79
|
-
tasks: jobs.map(stripRuntimeFlags),
|
|
80
|
-
};
|
|
81
|
-
const json = JSON.stringify(payload, null, 2);
|
|
82
|
-
// Atomic write: write to temp file then rename
|
|
83
|
-
const tmpFile = join(dir, WAVE_DIR, `${randomUUID()}.tmp`);
|
|
84
|
-
writeFileSync(tmpFile, json, "utf-8");
|
|
85
|
-
renameSync(tmpFile, filePath);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function readCronTasks(dir: string = process.cwd()): CronJob[] {
|
|
89
|
-
return parseFile(dir);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function writeCronTasks(
|
|
93
|
-
tasks: CronJob[],
|
|
94
|
-
dir: string = process.cwd(),
|
|
95
|
-
): void {
|
|
96
|
-
writeToFile(tasks, dir);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function addCronTask(job: CronJob, dir: string = process.cwd()): void {
|
|
100
|
-
const existing = readCronTasks(dir);
|
|
101
|
-
existing.push(job);
|
|
102
|
-
writeToFile(existing, dir);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function removeCronTasks(
|
|
106
|
-
ids: string[],
|
|
107
|
-
dir: string = process.cwd(),
|
|
108
|
-
): void {
|
|
109
|
-
const existing = readCronTasks(dir);
|
|
110
|
-
const idSet = new Set(ids);
|
|
111
|
-
const filtered = existing.filter((t) => !idSet.has(t.id));
|
|
112
|
-
writeToFile(filtered, dir);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function markCronTasksFired(
|
|
116
|
-
ids: string[],
|
|
117
|
-
firedAt: number,
|
|
118
|
-
dir: string = process.cwd(),
|
|
119
|
-
): void {
|
|
120
|
-
const existing = readCronTasks(dir);
|
|
121
|
-
const idSet = new Set(ids);
|
|
122
|
-
for (const task of existing) {
|
|
123
|
-
if (idSet.has(task.id)) {
|
|
124
|
-
task.lastFiredAt = firedAt;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
writeToFile(existing, dir);
|
|
128
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readFileSync,
|
|
5
|
-
unlinkSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
renameSync,
|
|
8
|
-
} from "fs";
|
|
9
|
-
import { join } from "path";
|
|
10
|
-
import { randomUUID } from "crypto";
|
|
11
|
-
import { logger } from "./globalLogger.js";
|
|
12
|
-
|
|
13
|
-
const WAVE_DIR = ".wave";
|
|
14
|
-
const LOCK_FILE = "scheduled_tasks.lock";
|
|
15
|
-
|
|
16
|
-
export interface SchedulerLockOptions {
|
|
17
|
-
dir?: string;
|
|
18
|
-
sessionId?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface LockData {
|
|
22
|
-
sessionId: string;
|
|
23
|
-
pid: number;
|
|
24
|
-
acquiredAt: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getLockPath(dir: string): string {
|
|
28
|
-
return join(dir, WAVE_DIR, LOCK_FILE);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function ensureWaveDir(dir: string): void {
|
|
32
|
-
const waveDir = join(dir, WAVE_DIR);
|
|
33
|
-
if (!existsSync(waveDir)) {
|
|
34
|
-
mkdirSync(waveDir, { recursive: true });
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function isPidAlive(pid: number): boolean {
|
|
39
|
-
try {
|
|
40
|
-
process.kill(pid, 0);
|
|
41
|
-
return true;
|
|
42
|
-
} catch {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Try to acquire the scheduler lock using an atomic exclusive create.
|
|
49
|
-
* If the lock already exists, checks PID liveness for stale recovery.
|
|
50
|
-
* Returns true if this process now owns the lock.
|
|
51
|
-
*/
|
|
52
|
-
export async function tryAcquireSchedulerLock(
|
|
53
|
-
opts: SchedulerLockOptions = {},
|
|
54
|
-
): Promise<boolean> {
|
|
55
|
-
const dir = opts.dir ?? process.cwd();
|
|
56
|
-
const sessionId = opts.sessionId ?? "unknown";
|
|
57
|
-
const lockPath = getLockPath(dir);
|
|
58
|
-
|
|
59
|
-
ensureWaveDir(dir);
|
|
60
|
-
|
|
61
|
-
// Step 1: Try to create lock file with 'wx' flag (exclusive create)
|
|
62
|
-
try {
|
|
63
|
-
const lockData: LockData = {
|
|
64
|
-
sessionId,
|
|
65
|
-
pid: process.pid,
|
|
66
|
-
acquiredAt: Date.now(),
|
|
67
|
-
};
|
|
68
|
-
const tmpPath = join(dir, WAVE_DIR, `${randomUUID()}.lock.tmp`);
|
|
69
|
-
writeFileSync(tmpPath, JSON.stringify(lockData), { flag: "wx" });
|
|
70
|
-
renameSync(tmpPath, lockPath);
|
|
71
|
-
logger?.info(
|
|
72
|
-
`CronSchedulerLock: acquired lock (pid=${process.pid}, session=${sessionId})`,
|
|
73
|
-
);
|
|
74
|
-
return true;
|
|
75
|
-
} catch (err: unknown) {
|
|
76
|
-
// EEXIST: file already exists — check if stale
|
|
77
|
-
if (
|
|
78
|
-
err &&
|
|
79
|
-
typeof err === "object" &&
|
|
80
|
-
"code" in err &&
|
|
81
|
-
(err as { code: string }).code !== "EEXIST"
|
|
82
|
-
) {
|
|
83
|
-
logger?.error(`CronSchedulerLock: unexpected error creating lock:`, err);
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Step 2: File exists — read it and check PID liveness
|
|
89
|
-
try {
|
|
90
|
-
const raw = readFileSync(lockPath, "utf-8");
|
|
91
|
-
const existing = JSON.parse(raw) as LockData;
|
|
92
|
-
|
|
93
|
-
if (isPidAlive(existing.pid)) {
|
|
94
|
-
// Another live session owns the lock
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Stale lock — delete and retry
|
|
99
|
-
logger?.info(
|
|
100
|
-
`CronSchedulerLock: removing stale lock (dead pid=${existing.pid}, session=${existing.sessionId})`,
|
|
101
|
-
);
|
|
102
|
-
unlinkSync(lockPath);
|
|
103
|
-
} catch {
|
|
104
|
-
// If we can't read the lock, try to delete it anyway
|
|
105
|
-
try {
|
|
106
|
-
unlinkSync(lockPath);
|
|
107
|
-
} catch {
|
|
108
|
-
// Give up
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Step 3: Retry — try exclusive create again
|
|
114
|
-
try {
|
|
115
|
-
const lockData: LockData = {
|
|
116
|
-
sessionId,
|
|
117
|
-
pid: process.pid,
|
|
118
|
-
acquiredAt: Date.now(),
|
|
119
|
-
};
|
|
120
|
-
const tmpPath = join(dir, WAVE_DIR, `${randomUUID()}.lock.tmp`);
|
|
121
|
-
writeFileSync(tmpPath, JSON.stringify(lockData), { flag: "wx" });
|
|
122
|
-
renameSync(tmpPath, lockPath);
|
|
123
|
-
logger?.info(
|
|
124
|
-
`CronSchedulerLock: acquired lock after stale recovery (pid=${process.pid}, session=${sessionId})`,
|
|
125
|
-
);
|
|
126
|
-
return true;
|
|
127
|
-
} catch {
|
|
128
|
-
// Another process beat us to it during the retry window
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Release the scheduler lock if this process owns it.
|
|
135
|
-
*/
|
|
136
|
-
export async function releaseSchedulerLock(
|
|
137
|
-
opts: SchedulerLockOptions = {},
|
|
138
|
-
): Promise<void> {
|
|
139
|
-
releaseSchedulerLockSync(opts);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Synchronous version for use in process exit handlers where async is not available.
|
|
144
|
-
*/
|
|
145
|
-
export function releaseSchedulerLockSync(
|
|
146
|
-
opts: SchedulerLockOptions = {},
|
|
147
|
-
): void {
|
|
148
|
-
const dir = opts.dir ?? process.cwd();
|
|
149
|
-
const lockPath = getLockPath(dir);
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
const raw = readFileSync(lockPath, "utf-8");
|
|
153
|
-
const existing = JSON.parse(raw) as LockData;
|
|
154
|
-
|
|
155
|
-
if (existing.pid === process.pid) {
|
|
156
|
-
unlinkSync(lockPath);
|
|
157
|
-
logger?.info(`CronSchedulerLock: released lock (pid=${process.pid})`);
|
|
158
|
-
}
|
|
159
|
-
} catch {
|
|
160
|
-
// Lock doesn't exist or can't be read — nothing to do
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Register a cleanup handler to release the lock on process exit.
|
|
166
|
-
* Returns a dispose function to remove the listeners (call in test teardown).
|
|
167
|
-
*/
|
|
168
|
-
export function registerSchedulerLockCleanup(
|
|
169
|
-
opts: SchedulerLockOptions = {},
|
|
170
|
-
): () => void {
|
|
171
|
-
const handler = () => {
|
|
172
|
-
releaseSchedulerLockSync(opts);
|
|
173
|
-
};
|
|
174
|
-
process.on("exit", handler);
|
|
175
|
-
process.on("SIGINT", handler);
|
|
176
|
-
process.on("SIGTERM", handler);
|
|
177
|
-
return () => {
|
|
178
|
-
process.off("exit", handler);
|
|
179
|
-
process.off("SIGINT", handler);
|
|
180
|
-
process.off("SIGTERM", handler);
|
|
181
|
-
};
|
|
182
|
-
}
|
package/src/utils/cronToHuman.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// Human-readable cron expression display.
|
|
2
|
-
// Intentionally narrow: covers common patterns; falls through to raw cron
|
|
3
|
-
// string for anything else. Based on Claude Code's cronToHuman implementation.
|
|
4
|
-
|
|
5
|
-
const DAY_NAMES = [
|
|
6
|
-
"Sunday",
|
|
7
|
-
"Monday",
|
|
8
|
-
"Tuesday",
|
|
9
|
-
"Wednesday",
|
|
10
|
-
"Thursday",
|
|
11
|
-
"Friday",
|
|
12
|
-
"Saturday",
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
function formatLocalTime(minute: number, hour: number): string {
|
|
16
|
-
const d = new Date(2000, 0, 1, hour, minute);
|
|
17
|
-
return d.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Convert a cron expression to a human-readable string.
|
|
22
|
-
* Covers common patterns; falls through to raw cron for anything else.
|
|
23
|
-
*/
|
|
24
|
-
export function cronToHuman(cron: string): string {
|
|
25
|
-
const parts = cron.trim().split(/\s+/);
|
|
26
|
-
if (parts.length !== 5) return cron;
|
|
27
|
-
|
|
28
|
-
const [minute, hour, dayOfMonth, month, dayOfWeek] = parts as [
|
|
29
|
-
string,
|
|
30
|
-
string,
|
|
31
|
-
string,
|
|
32
|
-
string,
|
|
33
|
-
string,
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
// Every N minutes: step/N * * * *
|
|
37
|
-
const everyMinMatch = minute.match(/^\*\/(\d+)$/);
|
|
38
|
-
if (
|
|
39
|
-
everyMinMatch &&
|
|
40
|
-
hour === "*" &&
|
|
41
|
-
dayOfMonth === "*" &&
|
|
42
|
-
month === "*" &&
|
|
43
|
-
dayOfWeek === "*"
|
|
44
|
-
) {
|
|
45
|
-
const n = parseInt(everyMinMatch[1]!, 10);
|
|
46
|
-
return n === 1 ? "Every minute" : `Every ${n} minutes`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Every hour: 0 * * * *
|
|
50
|
-
if (
|
|
51
|
-
minute.match(/^\d+$/) &&
|
|
52
|
-
hour === "*" &&
|
|
53
|
-
dayOfMonth === "*" &&
|
|
54
|
-
month === "*" &&
|
|
55
|
-
dayOfWeek === "*"
|
|
56
|
-
) {
|
|
57
|
-
const m = parseInt(minute, 10);
|
|
58
|
-
if (m === 0) return "Every hour";
|
|
59
|
-
return `Every hour at :${m.toString().padStart(2, "0")}`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Every N hours: 0 step/N * * *
|
|
63
|
-
const everyHourMatch = hour.match(/^\*\/(\d+)$/);
|
|
64
|
-
if (
|
|
65
|
-
minute.match(/^\d+$/) &&
|
|
66
|
-
everyHourMatch &&
|
|
67
|
-
dayOfMonth === "*" &&
|
|
68
|
-
month === "*" &&
|
|
69
|
-
dayOfWeek === "*"
|
|
70
|
-
) {
|
|
71
|
-
const n = parseInt(everyHourMatch[1]!, 10);
|
|
72
|
-
const m = parseInt(minute, 10);
|
|
73
|
-
const suffix = m === 0 ? "" : ` at :${m.toString().padStart(2, "0")}`;
|
|
74
|
-
return n === 1 ? `Every hour${suffix}` : `Every ${n} hours${suffix}`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!minute.match(/^\d+$/) || !hour.match(/^\d+$/)) return cron;
|
|
78
|
-
const m = parseInt(minute, 10);
|
|
79
|
-
const h = parseInt(hour, 10);
|
|
80
|
-
|
|
81
|
-
// Daily at specific time: M H * * *
|
|
82
|
-
if (dayOfMonth === "*" && month === "*" && dayOfWeek === "*") {
|
|
83
|
-
return `Every day at ${formatLocalTime(m, h)}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Specific day of week: M H * * D
|
|
87
|
-
if (dayOfMonth === "*" && month === "*" && dayOfWeek.match(/^\d$/)) {
|
|
88
|
-
const dayIndex = parseInt(dayOfWeek, 10) % 7;
|
|
89
|
-
const dayName = DAY_NAMES[dayIndex];
|
|
90
|
-
if (dayName) return `Every ${dayName} at ${formatLocalTime(m, h)}`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Weekdays: M H * * 1-5
|
|
94
|
-
if (dayOfMonth === "*" && month === "*" && dayOfWeek === "1-5") {
|
|
95
|
-
return `Weekdays at ${formatLocalTime(m, h)}`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return cron;
|
|
99
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, statSync } from "fs";
|
|
2
|
-
import { join, extname, basename } from "path";
|
|
3
|
-
import { homedir } from "os";
|
|
4
|
-
import type { CustomSlashCommand } from "../types/index.js";
|
|
5
|
-
import { parseMarkdownFile } from "./markdownParser.js";
|
|
6
|
-
import { generateCommandId } from "./commandPathResolver.js";
|
|
7
|
-
import { logger } from "./globalLogger.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Get the project-specific commands directory
|
|
11
|
-
*/
|
|
12
|
-
export function getProjectCommandsDir(workdir: string): string {
|
|
13
|
-
return join(workdir, ".wave", "commands");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get the user-specific commands directory
|
|
18
|
-
*/
|
|
19
|
-
export function getUserCommandsDir(): string {
|
|
20
|
-
return join(homedir(), ".wave", "commands");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Scan a directory for markdown command files (flat structure only)
|
|
25
|
-
* @param dirPath - Root commands directory path
|
|
26
|
-
*/
|
|
27
|
-
export function scanCommandsDirectory(dirPath: string): CustomSlashCommand[] {
|
|
28
|
-
if (!existsSync(dirPath)) {
|
|
29
|
-
return [];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const commands: CustomSlashCommand[] = [];
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
const entries = readdirSync(dirPath);
|
|
36
|
-
|
|
37
|
-
for (const entryName of entries) {
|
|
38
|
-
const fullPath = join(dirPath, entryName);
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const stats = statSync(fullPath);
|
|
42
|
-
if (!stats.isFile() || extname(entryName) !== ".md") {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Process markdown file
|
|
47
|
-
const commandId = generateCommandId(fullPath, dirPath);
|
|
48
|
-
const { content, config } = parseMarkdownFile(fullPath);
|
|
49
|
-
|
|
50
|
-
commands.push({
|
|
51
|
-
id: commandId,
|
|
52
|
-
name: basename(entryName, ".md"),
|
|
53
|
-
description: config?.description,
|
|
54
|
-
filePath: fullPath,
|
|
55
|
-
content,
|
|
56
|
-
config,
|
|
57
|
-
});
|
|
58
|
-
} catch (error) {
|
|
59
|
-
logger.warn(`Failed to load custom command from ${fullPath}:`, error);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
} catch (error) {
|
|
63
|
-
logger.warn(`Failed to scan commands directory ${dirPath}:`, error);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return commands;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Load all custom slash commands from both project and user directories
|
|
71
|
-
*/
|
|
72
|
-
export function loadCustomSlashCommands(workdir: string): CustomSlashCommand[] {
|
|
73
|
-
const userClaudeCommands = scanCommandsDirectory(
|
|
74
|
-
join(homedir(), ".claude", "commands"),
|
|
75
|
-
);
|
|
76
|
-
const userWaveCommands = scanCommandsDirectory(getUserCommandsDir());
|
|
77
|
-
const projectClaudeCommands = scanCommandsDirectory(
|
|
78
|
-
join(workdir, ".claude", "commands"),
|
|
79
|
-
);
|
|
80
|
-
const projectWaveCommands = scanCommandsDirectory(
|
|
81
|
-
getProjectCommandsDir(workdir),
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const commandMap = new Map<string, CustomSlashCommand>();
|
|
85
|
-
// Write in priority order: lowest first, highest last (overwrites)
|
|
86
|
-
for (const command of [
|
|
87
|
-
...userClaudeCommands,
|
|
88
|
-
...userWaveCommands,
|
|
89
|
-
...projectClaudeCommands,
|
|
90
|
-
...projectWaveCommands,
|
|
91
|
-
]) {
|
|
92
|
-
commandMap.set(command.id, command);
|
|
93
|
-
}
|
|
94
|
-
return Array.from(commandMap.values());
|
|
95
|
-
}
|
package/src/utils/editUtils.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for file editing tools
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Escape regular expression special characters
|
|
7
|
-
*/
|
|
8
|
-
export function escapeRegExp(string: string): string {
|
|
9
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Returns an error message when old_string is not found, including
|
|
14
|
-
* the attempted string to help the model self-correct on retry.
|
|
15
|
-
*/
|
|
16
|
-
export function analyzeEditMismatch(oldString: string): string {
|
|
17
|
-
const displayString =
|
|
18
|
-
oldString.length > 200 ? oldString.substring(0, 200) + "..." : oldString;
|
|
19
|
-
return `String to replace not found in file.\nString: ${displayString}`;
|
|
20
|
-
}
|
package/src/utils/fileFormat.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { extname } from "path";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* List of binary document file extensions that should not be read as text
|
|
5
|
-
*/
|
|
6
|
-
export const BINARY_DOCUMENT_EXTENSIONS = [
|
|
7
|
-
".pdf",
|
|
8
|
-
".doc",
|
|
9
|
-
".docx",
|
|
10
|
-
".xls",
|
|
11
|
-
".xlsx",
|
|
12
|
-
".ppt",
|
|
13
|
-
".pptx",
|
|
14
|
-
".odt",
|
|
15
|
-
".ods",
|
|
16
|
-
".odp",
|
|
17
|
-
".rtf",
|
|
18
|
-
] as const;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Check if a file is a binary document format based on its extension
|
|
22
|
-
* @param filePath - The path to the file
|
|
23
|
-
* @returns true if the file is a binary document format, false otherwise
|
|
24
|
-
*/
|
|
25
|
-
export function isBinaryDocument(filePath: string): boolean {
|
|
26
|
-
const fileExtension = extname(filePath).toLowerCase();
|
|
27
|
-
return (BINARY_DOCUMENT_EXTENSIONS as readonly string[]).includes(
|
|
28
|
-
fileExtension,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Get a human-readable error message for unsupported binary document formats
|
|
34
|
-
* @param filePath - The path to the file
|
|
35
|
-
* @returns Error message string
|
|
36
|
-
*/
|
|
37
|
-
export function getBinaryDocumentError(filePath: string): string {
|
|
38
|
-
const fileExtension = extname(filePath).toLowerCase();
|
|
39
|
-
return `Reading binary document files with extension '${fileExtension}' is not supported. Supported formats include text files, code files, images, and Jupyter notebooks.`;
|
|
40
|
-
}
|