vibe-coding-master 0.0.8 → 0.0.10
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 +35 -24
- package/dist/backend/adapters/filesystem.js +0 -7
- package/dist/backend/api/app-settings-routes.js +8 -0
- package/dist/backend/api/message-routes.js +3 -1
- package/dist/backend/api/session-routes.js +7 -1
- package/dist/backend/api/task-routes.js +3 -10
- package/dist/backend/api/translation-routes.js +21 -3
- package/dist/backend/runtime/terminal-submit.js +20 -0
- package/dist/backend/server.js +8 -4
- package/dist/backend/services/app-settings-service.js +118 -15
- package/dist/backend/services/claude-transcript-service.js +12 -8
- package/dist/backend/services/command-dispatcher.js +2 -1
- package/dist/backend/services/message-service.js +10 -6
- package/dist/backend/services/project-service.js +5 -27
- package/dist/backend/services/session-service.js +7 -4
- package/dist/backend/services/task-service.js +66 -57
- package/dist/backend/services/translation-service.js +264 -77
- package/dist/backend/templates/harness/gitignore.js +1 -4
- package/dist/shared/types/app-settings.js +1 -0
- package/dist-frontend/assets/index-B1vIIwLq.js +88 -0
- package/dist-frontend/assets/index-DPyKuEOz.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/cc-best-practices.md +4 -4
- package/docs/product-design.md +71 -31
- package/docs/v1-architecture-design.md +90 -56
- package/docs/v1-implementation-plan.md +76 -62
- package/package.json +3 -1
- package/dist/backend/ws/translation-ws.js +0 -35
- package/dist-frontend/assets/index-CuiNNOzj.css +0 -32
- package/dist-frontend/assets/index-D59GuHCR.js +0 -58
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { ROLE_NAMES } from "../../shared/constants.js";
|
|
4
4
|
import { VcmError } from "../errors.js";
|
|
5
5
|
import { resolveRepoPath } from "../adapters/filesystem.js";
|
|
6
|
+
import { submitTerminalInput } from "../runtime/terminal-submit.js";
|
|
6
7
|
import { renderManualStagePrompt, renderMessageEnvelope } from "../templates/message-envelope.js";
|
|
7
8
|
const PM_ROLE = "project-manager";
|
|
8
9
|
const PM_TO_ROLE_TYPES = new Set(["task", "question", "review-request", "revise", "cancel"]);
|
|
@@ -12,7 +13,7 @@ export function createMessageService(deps) {
|
|
|
12
13
|
const id = deps.id ?? (() => `msg_${randomUUID()}`);
|
|
13
14
|
return {
|
|
14
15
|
listMessages(input) {
|
|
15
|
-
return readLatestMessages(deps.fs, getMessagesPath(input
|
|
16
|
+
return readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
|
|
16
17
|
},
|
|
17
18
|
async sendMessage(input) {
|
|
18
19
|
await deps.taskService.loadTask(input.repoRoot, input.taskSlug);
|
|
@@ -56,7 +57,7 @@ export function createMessageService(deps) {
|
|
|
56
57
|
deliveredAt: timestamp,
|
|
57
58
|
failureReason: undefined
|
|
58
59
|
};
|
|
59
|
-
deps.runtime
|
|
60
|
+
await submitTerminalInput(deps.runtime, session.id, renderMessageEnvelope(delivered));
|
|
60
61
|
await appendMessageSnapshot(deps.fs, input, delivered);
|
|
61
62
|
return { message: delivered, delivered: true, requiresUserApproval: false };
|
|
62
63
|
},
|
|
@@ -95,7 +96,7 @@ export function createMessageService(deps) {
|
|
|
95
96
|
return rejected;
|
|
96
97
|
},
|
|
97
98
|
async getOrchestrationState(input) {
|
|
98
|
-
const statePath = getOrchestrationStatePath(input
|
|
99
|
+
const statePath = getOrchestrationStatePath(getStateRepoRoot(input), input.stateRoot, input.taskSlug);
|
|
99
100
|
if (!(await deps.fs.pathExists(statePath))) {
|
|
100
101
|
return {
|
|
101
102
|
taskSlug: input.taskSlug,
|
|
@@ -114,7 +115,7 @@ export function createMessageService(deps) {
|
|
|
114
115
|
paused: input.paused ?? current.paused,
|
|
115
116
|
updatedAt: now()
|
|
116
117
|
};
|
|
117
|
-
await deps.fs.writeJsonAtomic(getOrchestrationStatePath(input
|
|
118
|
+
await deps.fs.writeJsonAtomic(getOrchestrationStatePath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), next);
|
|
118
119
|
return next;
|
|
119
120
|
}
|
|
120
121
|
};
|
|
@@ -183,7 +184,7 @@ ${artifactRefs}
|
|
|
183
184
|
`;
|
|
184
185
|
}
|
|
185
186
|
async function getMessageOrThrow(fs, input) {
|
|
186
|
-
const messages = await readLatestMessages(fs, getMessagesPath(input
|
|
187
|
+
const messages = await readLatestMessages(fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
|
|
187
188
|
const message = messages.find((candidate) => candidate.id === input.messageId);
|
|
188
189
|
if (!message) {
|
|
189
190
|
throw new VcmError({
|
|
@@ -207,7 +208,7 @@ async function readLatestMessages(fs, messagesPath) {
|
|
|
207
208
|
return [...latest.values()].sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
208
209
|
}
|
|
209
210
|
async function appendMessageSnapshot(fs, input, message) {
|
|
210
|
-
await fs.appendText(getMessagesPath(input
|
|
211
|
+
await fs.appendText(getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), `${JSON.stringify(message)}\n`);
|
|
211
212
|
}
|
|
212
213
|
function getMessagesPath(repoRoot, stateRoot, taskSlug) {
|
|
213
214
|
return path.join(repoRoot, stateRoot, "messages", `${taskSlug}.jsonl`);
|
|
@@ -215,3 +216,6 @@ function getMessagesPath(repoRoot, stateRoot, taskSlug) {
|
|
|
215
216
|
function getOrchestrationStatePath(repoRoot, stateRoot, taskSlug) {
|
|
216
217
|
return path.join(repoRoot, stateRoot, "orchestration", `${taskSlug}.json`);
|
|
217
218
|
}
|
|
219
|
+
function getStateRepoRoot(input) {
|
|
220
|
+
return input.stateRepoRoot ?? input.repoRoot;
|
|
221
|
+
}
|
|
@@ -3,20 +3,8 @@ import { ROLE_NAMES } from "../../shared/constants.js";
|
|
|
3
3
|
import { VcmError } from "../errors.js";
|
|
4
4
|
const DEFAULT_HANDOFF_ROOT = ".ai/handoffs";
|
|
5
5
|
const DEFAULT_STATE_ROOT = ".ai/vcm";
|
|
6
|
-
const LEGACY_STATE_ROOT = ".vcm";
|
|
7
6
|
export function createProjectService(deps) {
|
|
8
7
|
let currentProject = null;
|
|
9
|
-
async function migrateLegacyStateRoot(repoRoot) {
|
|
10
|
-
if (!deps.fs.copyDir) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
const legacyStateRoot = path.join(repoRoot, LEGACY_STATE_ROOT);
|
|
14
|
-
const currentStateRoot = path.join(repoRoot, DEFAULT_STATE_ROOT);
|
|
15
|
-
if (!(await deps.fs.pathExists(legacyStateRoot)) || await deps.fs.pathExists(currentStateRoot)) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
await deps.fs.copyDir(legacyStateRoot, currentStateRoot);
|
|
19
|
-
}
|
|
20
8
|
return {
|
|
21
9
|
async connectProject(input) {
|
|
22
10
|
const requestedPath = input.repoPath.trim();
|
|
@@ -43,9 +31,6 @@ export function createProjectService(deps) {
|
|
|
43
31
|
const config = await this.loadConfig(repoRoot);
|
|
44
32
|
await deps.fs.ensureDir(path.join(repoRoot, config.handoffRoot));
|
|
45
33
|
await deps.fs.ensureDir(path.join(repoRoot, config.stateRoot, "tasks"));
|
|
46
|
-
await deps.fs.ensureDir(path.join(repoRoot, config.stateRoot, "sessions"));
|
|
47
|
-
await deps.fs.ensureDir(path.join(repoRoot, config.stateRoot, "messages"));
|
|
48
|
-
await deps.fs.ensureDir(path.join(repoRoot, config.stateRoot, "orchestration"));
|
|
49
34
|
await deps.fs.ensureDir(path.join(repoRoot, config.stateRoot, "worktrees"));
|
|
50
35
|
await this.saveConfig(config, true);
|
|
51
36
|
const warnings = [];
|
|
@@ -86,16 +71,9 @@ export function createProjectService(deps) {
|
|
|
86
71
|
return deps.appSettings.getRecentRepositoryPaths();
|
|
87
72
|
},
|
|
88
73
|
async loadConfig(repoRoot) {
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
-
return normalizeProjectConfig(
|
|
92
|
-
}
|
|
93
|
-
const legacyConfigPath = path.join(repoRoot, LEGACY_STATE_ROOT, "config.json");
|
|
94
|
-
if (await deps.fs.pathExists(legacyConfigPath)) {
|
|
95
|
-
const migratedConfig = normalizeProjectConfig(await deps.fs.readJson(legacyConfigPath), repoRoot);
|
|
96
|
-
await migrateLegacyStateRoot(repoRoot);
|
|
97
|
-
await this.saveConfig(migratedConfig, true);
|
|
98
|
-
return migratedConfig;
|
|
74
|
+
const appConfig = await deps.appSettings.loadProjectConfig(repoRoot);
|
|
75
|
+
if (appConfig) {
|
|
76
|
+
return normalizeProjectConfig(appConfig, repoRoot);
|
|
99
77
|
}
|
|
100
78
|
return buildDefaultProjectConfig(repoRoot);
|
|
101
79
|
},
|
|
@@ -105,10 +83,10 @@ export function createProjectService(deps) {
|
|
|
105
83
|
if (!force && await deps.fs.pathExists(configPath)) {
|
|
106
84
|
return;
|
|
107
85
|
}
|
|
108
|
-
await deps.
|
|
86
|
+
await deps.appSettings.saveProjectConfig(normalizedConfig);
|
|
109
87
|
},
|
|
110
88
|
getConfigPath(repoRoot) {
|
|
111
|
-
return
|
|
89
|
+
return deps.appSettings.getProjectConfigPath(repoRoot);
|
|
112
90
|
}
|
|
113
91
|
};
|
|
114
92
|
}
|
|
@@ -16,7 +16,7 @@ export function createSessionService(deps) {
|
|
|
16
16
|
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
17
17
|
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
18
18
|
const paths = deps.artifactService.getHandoffPaths(taskRepoRoot, task.handoffDir);
|
|
19
|
-
const persisted = await loadPersistedRoleRecord(deps.fs,
|
|
19
|
+
const persisted = await loadPersistedRoleRecord(deps.fs, taskRepoRoot, config.stateRoot, taskSlug, role);
|
|
20
20
|
const permissionMode = input.permissionMode ?? persisted?.permissionMode ?? "default";
|
|
21
21
|
const claudeSessionId = launchMode === "resume"
|
|
22
22
|
? persisted?.claudeSessionId
|
|
@@ -73,7 +73,7 @@ export function createSessionService(deps) {
|
|
|
73
73
|
exitCode: runtimeSession.exitCode
|
|
74
74
|
};
|
|
75
75
|
deps.registry.upsert(record);
|
|
76
|
-
await persistTaskSession(deps.fs,
|
|
76
|
+
await persistTaskSession(deps.fs, taskRepoRoot, config.stateRoot, record);
|
|
77
77
|
await deps.taskService.updateTaskStatus(repoRoot, taskSlug, "running");
|
|
78
78
|
return record;
|
|
79
79
|
}
|
|
@@ -103,7 +103,8 @@ export function createSessionService(deps) {
|
|
|
103
103
|
};
|
|
104
104
|
deps.registry.upsert(updated);
|
|
105
105
|
const config = await deps.projectService.loadConfig(repoRoot);
|
|
106
|
-
await
|
|
106
|
+
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
107
|
+
await persistTaskSession(deps.fs, getTaskRuntimeRepoRoot(task), config.stateRoot, updated);
|
|
107
108
|
return updated;
|
|
108
109
|
},
|
|
109
110
|
async restartRoleSession(repoRoot, taskSlug, role, input = {}) {
|
|
@@ -119,8 +120,10 @@ export function createSessionService(deps) {
|
|
|
119
120
|
},
|
|
120
121
|
async getRoleSession(repoRoot, taskSlug, role) {
|
|
121
122
|
const config = await deps.projectService.loadConfig(repoRoot);
|
|
123
|
+
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
124
|
+
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
122
125
|
const record = deps.registry.getByRole(taskSlug, role)
|
|
123
|
-
?? await loadPersistedRoleRecord(deps.fs,
|
|
126
|
+
?? await loadPersistedRoleRecord(deps.fs, taskRepoRoot, config.stateRoot, taskSlug, role);
|
|
124
127
|
if (!record) {
|
|
125
128
|
return undefined;
|
|
126
129
|
}
|
|
@@ -8,8 +8,13 @@ export function createTaskService(deps) {
|
|
|
8
8
|
assertValidTaskSlug(input.taskSlug);
|
|
9
9
|
const config = await deps.projectService.loadConfig(repoRoot);
|
|
10
10
|
const taskPath = getTaskPath(repoRoot, config.stateRoot, input.taskSlug);
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const shouldCreateWorktree = input.createWorktree !== false;
|
|
12
|
+
const taskBranch = shouldCreateWorktree
|
|
13
|
+
? `feature/${input.taskSlug}`
|
|
14
|
+
: await deps.git.getCurrentBranch(repoRoot);
|
|
15
|
+
const worktreePath = shouldCreateWorktree
|
|
16
|
+
? getTaskWorktreePath(repoRoot, config.stateRoot, input.taskSlug)
|
|
17
|
+
: undefined;
|
|
13
18
|
if (await deps.fs.pathExists(taskPath)) {
|
|
14
19
|
throw new VcmError({
|
|
15
20
|
code: "TASK_EXISTS",
|
|
@@ -17,23 +22,7 @@ export function createTaskService(deps) {
|
|
|
17
22
|
statusCode: 409
|
|
18
23
|
});
|
|
19
24
|
}
|
|
20
|
-
if (await deps.git.
|
|
21
|
-
throw new VcmError({
|
|
22
|
-
code: "TASK_BRANCH_EXISTS",
|
|
23
|
-
message: `Task branch already exists: ${branch}`,
|
|
24
|
-
statusCode: 409,
|
|
25
|
-
hint: "Choose a different task name or clean up the existing branch."
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
if (await deps.fs.pathExists(worktreePath)) {
|
|
29
|
-
throw new VcmError({
|
|
30
|
-
code: "TASK_WORKTREE_EXISTS",
|
|
31
|
-
message: `Task worktree already exists: ${worktreePath}`,
|
|
32
|
-
statusCode: 409,
|
|
33
|
-
hint: "Choose a different task name or clean up the existing worktree."
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
if (!(await deps.git.isIgnored(repoRoot, `${config.stateRoot}/config.json`))) {
|
|
25
|
+
if (!(await deps.git.isIgnored(repoRoot, `${config.stateRoot}/tasks/.probe`))) {
|
|
37
26
|
throw new VcmError({
|
|
38
27
|
code: "VCM_STATE_NOT_IGNORED",
|
|
39
28
|
message: `${config.stateRoot}/ is not ignored by Git.`,
|
|
@@ -41,23 +30,42 @@ export function createTaskService(deps) {
|
|
|
41
30
|
hint: "Apply VCM Harness first so .gitignore contains the VCM managed block."
|
|
42
31
|
});
|
|
43
32
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
33
|
+
if (shouldCreateWorktree && worktreePath) {
|
|
34
|
+
if (await deps.git.branchExists(repoRoot, taskBranch)) {
|
|
35
|
+
throw new VcmError({
|
|
36
|
+
code: "TASK_BRANCH_EXISTS",
|
|
37
|
+
message: `Task branch already exists: ${taskBranch}`,
|
|
38
|
+
statusCode: 409,
|
|
39
|
+
hint: "Choose a different task name or clean up the existing branch."
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (await deps.fs.pathExists(worktreePath)) {
|
|
43
|
+
throw new VcmError({
|
|
44
|
+
code: "TASK_WORKTREE_EXISTS",
|
|
45
|
+
message: `Task worktree already exists: ${worktreePath}`,
|
|
46
|
+
statusCode: 409,
|
|
47
|
+
hint: "Choose a different task name or clean up the existing worktree."
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const baseStatus = await deps.git.getStatusPorcelain(repoRoot);
|
|
51
|
+
if (baseStatus.trim()) {
|
|
52
|
+
throw new VcmError({
|
|
53
|
+
code: "BASE_REPO_DIRTY",
|
|
54
|
+
message: "The connected repository has uncommitted changes.",
|
|
55
|
+
statusCode: 409,
|
|
56
|
+
hint: "Commit, stash, or discard base repository changes before creating a task worktree."
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
await deps.fs.ensureDir(path.dirname(worktreePath));
|
|
60
|
+
await deps.git.createWorktree({
|
|
61
|
+
repoRoot,
|
|
62
|
+
branch: taskBranch,
|
|
63
|
+
worktreePath,
|
|
64
|
+
baseRef: "HEAD"
|
|
51
65
|
});
|
|
52
66
|
}
|
|
53
|
-
await deps.fs.ensureDir(path.dirname(worktreePath));
|
|
54
|
-
await deps.git.createWorktree({
|
|
55
|
-
repoRoot,
|
|
56
|
-
branch,
|
|
57
|
-
worktreePath,
|
|
58
|
-
baseRef: "HEAD"
|
|
59
|
-
});
|
|
60
67
|
const timestamp = now();
|
|
68
|
+
const taskRepoRoot = worktreePath ?? repoRoot;
|
|
61
69
|
const task = {
|
|
62
70
|
version: 1,
|
|
63
71
|
taskSlug: input.taskSlug,
|
|
@@ -66,19 +74,20 @@ export function createTaskService(deps) {
|
|
|
66
74
|
updatedAt: timestamp,
|
|
67
75
|
repoRoot,
|
|
68
76
|
worktreePath,
|
|
69
|
-
branch,
|
|
77
|
+
branch: taskBranch,
|
|
70
78
|
handoffDir: path.posix.join(config.handoffRoot, input.taskSlug),
|
|
71
79
|
status: "created",
|
|
72
80
|
specPath: input.specPath,
|
|
73
81
|
cleanupStatus: "active"
|
|
74
82
|
};
|
|
83
|
+
await ensureTaskRuntimeStateDirs(deps.fs, taskRepoRoot, config.stateRoot);
|
|
75
84
|
await deps.artifactService.ensureHandoffStructure({
|
|
76
|
-
repoRoot:
|
|
85
|
+
repoRoot: taskRepoRoot,
|
|
77
86
|
taskSlug: input.taskSlug,
|
|
78
87
|
handoffDir: task.handoffDir
|
|
79
88
|
});
|
|
80
89
|
await deps.artifactService.createArtifactTemplates({
|
|
81
|
-
repoRoot:
|
|
90
|
+
repoRoot: taskRepoRoot,
|
|
82
91
|
taskSlug: input.taskSlug,
|
|
83
92
|
handoffDir: task.handoffDir
|
|
84
93
|
});
|
|
@@ -136,30 +145,23 @@ export function createTaskService(deps) {
|
|
|
136
145
|
}
|
|
137
146
|
const config = await deps.projectService.loadConfig(repoRoot);
|
|
138
147
|
const task = await this.loadTask(repoRoot, taskSlug);
|
|
148
|
+
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
149
|
+
const statePaths = getTaskStatePaths(repoRoot, taskRepoRoot, config.stateRoot, taskSlug);
|
|
139
150
|
const removedStatePaths = [];
|
|
140
151
|
const cleanedAt = now();
|
|
141
152
|
if (task.worktreePath) {
|
|
142
153
|
assertTaskWorktreePath(repoRoot, config.stateRoot, task.worktreePath);
|
|
143
|
-
|
|
144
|
-
if (status.trim() && !options.force) {
|
|
145
|
-
throw new VcmError({
|
|
146
|
-
code: "TASK_WORKTREE_DIRTY",
|
|
147
|
-
message: `Task worktree has uncommitted changes: ${task.worktreePath}`,
|
|
148
|
-
statusCode: 409,
|
|
149
|
-
hint: "Commit, stash, or discard the task worktree changes before cleanup, or retry with force."
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
await deps.git.removeWorktree(repoRoot, task.worktreePath, { force: options.force });
|
|
153
|
-
}
|
|
154
|
-
for (const statePath of getTaskStatePaths(repoRoot, config.stateRoot, taskSlug)) {
|
|
155
|
-
await deps.fs.removePath(statePath, { force: true });
|
|
156
|
-
removedStatePaths.push(statePath);
|
|
154
|
+
await deps.git.removeWorktree(repoRoot, task.worktreePath, { force: options.force ?? true });
|
|
157
155
|
}
|
|
158
156
|
let deletedBranch;
|
|
159
|
-
if (options.deleteBranch) {
|
|
160
|
-
await deps.git.deleteBranch(repoRoot, task.branch, { force: options.forceDeleteBranch });
|
|
157
|
+
if (task.worktreePath && (options.deleteBranch ?? true)) {
|
|
158
|
+
await deps.git.deleteBranch(repoRoot, task.branch, { force: options.forceDeleteBranch ?? true });
|
|
161
159
|
deletedBranch = task.branch;
|
|
162
160
|
}
|
|
161
|
+
for (const statePath of statePaths) {
|
|
162
|
+
await deps.fs.removePath(statePath, { recursive: true, force: true });
|
|
163
|
+
removedStatePaths.push(statePath);
|
|
164
|
+
}
|
|
163
165
|
return {
|
|
164
166
|
taskSlug,
|
|
165
167
|
removedWorktreePath: task.worktreePath,
|
|
@@ -179,12 +181,19 @@ function getTaskPath(repoRoot, stateRoot, taskSlug) {
|
|
|
179
181
|
function getTaskWorktreePath(repoRoot, stateRoot, taskSlug) {
|
|
180
182
|
return path.join(repoRoot, stateRoot, "worktrees", taskSlug);
|
|
181
183
|
}
|
|
182
|
-
function
|
|
184
|
+
async function ensureTaskRuntimeStateDirs(fs, taskRepoRoot, stateRoot) {
|
|
185
|
+
await fs.ensureDir(path.join(taskRepoRoot, stateRoot, "sessions"));
|
|
186
|
+
await fs.ensureDir(path.join(taskRepoRoot, stateRoot, "messages"));
|
|
187
|
+
await fs.ensureDir(path.join(taskRepoRoot, stateRoot, "orchestration"));
|
|
188
|
+
await fs.ensureDir(path.join(taskRepoRoot, stateRoot, "translation"));
|
|
189
|
+
}
|
|
190
|
+
function getTaskStatePaths(baseRepoRoot, taskRepoRoot, stateRoot, taskSlug) {
|
|
183
191
|
return [
|
|
184
|
-
path.join(
|
|
185
|
-
path.join(
|
|
186
|
-
path.join(
|
|
187
|
-
path.join(
|
|
192
|
+
path.join(baseRepoRoot, stateRoot, "tasks", `${taskSlug}.json`),
|
|
193
|
+
path.join(taskRepoRoot, stateRoot, "sessions", `${taskSlug}.json`),
|
|
194
|
+
path.join(taskRepoRoot, stateRoot, "messages", `${taskSlug}.jsonl`),
|
|
195
|
+
path.join(taskRepoRoot, stateRoot, "orchestration", `${taskSlug}.json`),
|
|
196
|
+
path.join(taskRepoRoot, stateRoot, "translation", taskSlug)
|
|
188
197
|
];
|
|
189
198
|
}
|
|
190
199
|
function assertTaskWorktreePath(repoRoot, stateRoot, worktreePath) {
|