@nseng-ai/areg 0.1.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/package.json +29 -0
- package/src/cli.ts +109 -0
- package/src/context.ts +59 -0
- package/src/fake-gateways.ts +1009 -0
- package/src/gateways/command-constants.ts +1 -0
- package/src/gateways/errors.ts +5 -0
- package/src/gateways/fs-utils.ts +26 -0
- package/src/gateways/github-gateway.ts +111 -0
- package/src/gateways/host-gateway.ts +35 -0
- package/src/gateways/mutation-policy.ts +94 -0
- package/src/gateways/npx-skills-gateway.ts +53 -0
- package/src/gateways/project-fs.ts +320 -0
- package/src/gateways/project-gateway.ts +801 -0
- package/src/gateways/prompt-gateway.ts +21 -0
- package/src/gateways/skill-kind-classification.ts +39 -0
- package/src/gateways/skillx-workspace-gateway.ts +220 -0
- package/src/gateways.ts +361 -0
- package/src/index.ts +48 -0
- package/src/operations/check.ts +559 -0
- package/src/operations/doctor-skills-report.ts +109 -0
- package/src/operations/doctor-skills-severity.ts +9 -0
- package/src/operations/doctor-skills.ts +471 -0
- package/src/operations/file-state.ts +77 -0
- package/src/operations/frontmatter.ts +151 -0
- package/src/operations/init.ts +548 -0
- package/src/operations/lockfile.ts +107 -0
- package/src/operations/managed-markdown-block.ts +47 -0
- package/src/operations/pi-replacement.ts +33 -0
- package/src/operations/pi-settings.ts +60 -0
- package/src/operations/project-agents.ts +115 -0
- package/src/operations/project-inspection.ts +165 -0
- package/src/operations/project-mutations.ts +383 -0
- package/src/operations/project-resolution.ts +53 -0
- package/src/operations/skill-find.ts +295 -0
- package/src/operations/skill-kind-apply-plan.ts +531 -0
- package/src/operations/skill-kind-frontmatter.ts +55 -0
- package/src/operations/skill-kind-inference.ts +391 -0
- package/src/operations/skill-kind.ts +525 -0
- package/src/operations/skill-mirror-conventions.ts +126 -0
- package/src/operations/skillx.ts +305 -0
- package/src/operations/toml-section.ts +53 -0
- package/src/operations/update-skills.ts +325 -0
- package/src/real-gateways.ts +6 -0
- package/src/skill-lookup.ts +254 -0
- package/src/sort.ts +7 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import type { AregCliContext } from "../context.ts";
|
|
2
|
+
import type {
|
|
3
|
+
AregErrorInfo,
|
|
4
|
+
AregProjectGateway,
|
|
5
|
+
AregProjectManagedTargetRequest,
|
|
6
|
+
AregProjectMutationPolicy,
|
|
7
|
+
AregProjectMutationResult,
|
|
8
|
+
} from "../gateways.ts";
|
|
9
|
+
|
|
10
|
+
interface ProjectTextWritePlan {
|
|
11
|
+
relativePath: string;
|
|
12
|
+
content: string;
|
|
13
|
+
description: string;
|
|
14
|
+
createParent: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ProjectDeletePlan {
|
|
18
|
+
relativePath: string;
|
|
19
|
+
description: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ProjectRemoveEmptyDirPlan {
|
|
23
|
+
relativePath: string;
|
|
24
|
+
description: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ProjectDeleteSymlinkPlan {
|
|
28
|
+
relativePath: string;
|
|
29
|
+
description: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface BaseApplyProjectMutationPlanRequest {
|
|
33
|
+
ctx: AregCliContext;
|
|
34
|
+
projectDir: string;
|
|
35
|
+
writes: readonly ProjectTextWritePlan[];
|
|
36
|
+
execute?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type ApplyProjectMutationPlanRequest =
|
|
40
|
+
| (BaseApplyProjectMutationPlanRequest & {
|
|
41
|
+
policy: Exclude<AregProjectMutationPolicy, "skill-kind">;
|
|
42
|
+
})
|
|
43
|
+
| (BaseApplyProjectMutationPlanRequest & {
|
|
44
|
+
policy: "skill-kind";
|
|
45
|
+
deletes: readonly ProjectDeletePlan[];
|
|
46
|
+
deleteSymlinks: readonly ProjectDeleteSymlinkPlan[];
|
|
47
|
+
removeEmptyDirs: readonly ProjectRemoveEmptyDirPlan[];
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const PROJECT_FILE_MUTATION_OPERATION_TYPES = [
|
|
51
|
+
"write",
|
|
52
|
+
"delete",
|
|
53
|
+
"delete-symlink",
|
|
54
|
+
"remove-empty-dir",
|
|
55
|
+
] as const;
|
|
56
|
+
export const PROJECT_MUTATION_OPERATION_TYPES = [
|
|
57
|
+
...PROJECT_FILE_MUTATION_OPERATION_TYPES,
|
|
58
|
+
"external",
|
|
59
|
+
] as const;
|
|
60
|
+
export const PROJECT_MUTATION_OPERATION_STATUSES = [
|
|
61
|
+
"applied",
|
|
62
|
+
"failed",
|
|
63
|
+
"not-attempted",
|
|
64
|
+
"skipped",
|
|
65
|
+
] as const;
|
|
66
|
+
|
|
67
|
+
export type ProjectFileMutationOperationType =
|
|
68
|
+
(typeof PROJECT_FILE_MUTATION_OPERATION_TYPES)[number];
|
|
69
|
+
export type ProjectMutationOperationType = (typeof PROJECT_MUTATION_OPERATION_TYPES)[number];
|
|
70
|
+
export type ProjectMutationOperationStatus = (typeof PROJECT_MUTATION_OPERATION_STATUSES)[number];
|
|
71
|
+
|
|
72
|
+
export interface ProjectMutationOperationStatusRecord {
|
|
73
|
+
type: ProjectMutationOperationType;
|
|
74
|
+
path: string;
|
|
75
|
+
description: string;
|
|
76
|
+
status: ProjectMutationOperationStatus;
|
|
77
|
+
error?: AregErrorInfo;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface ProjectMutationOperationBase {
|
|
81
|
+
type: ProjectFileMutationOperationType;
|
|
82
|
+
relativePath: string;
|
|
83
|
+
description: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface WriteOperation extends ProjectMutationOperationBase {
|
|
87
|
+
type: "write";
|
|
88
|
+
plan: ProjectTextWritePlan;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface DeleteOperation extends ProjectMutationOperationBase {
|
|
92
|
+
type: "delete";
|
|
93
|
+
plan: ProjectDeletePlan;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface RemoveEmptyDirOperation extends ProjectMutationOperationBase {
|
|
97
|
+
type: "remove-empty-dir";
|
|
98
|
+
plan: ProjectRemoveEmptyDirPlan;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface DeleteSymlinkOperation extends ProjectMutationOperationBase {
|
|
102
|
+
type: "delete-symlink";
|
|
103
|
+
plan: ProjectDeleteSymlinkPlan;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type ProjectMutationOperation =
|
|
107
|
+
| WriteOperation
|
|
108
|
+
| DeleteOperation
|
|
109
|
+
| DeleteSymlinkOperation
|
|
110
|
+
| RemoveEmptyDirOperation;
|
|
111
|
+
|
|
112
|
+
interface ApplyProjectMutationPlanSuccessFields {
|
|
113
|
+
appliedPaths: AppliedProjectMutationPaths;
|
|
114
|
+
operationStatuses: readonly ProjectMutationOperationStatusRecord[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface AppliedProjectMutationPaths {
|
|
118
|
+
written: string[];
|
|
119
|
+
deleted: string[];
|
|
120
|
+
deletedSymlink: string[];
|
|
121
|
+
removedEmptyDir: string[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
type ProjectMutationOperationResult =
|
|
125
|
+
| { ok: true; removed?: boolean }
|
|
126
|
+
| { ok: false; error: AregErrorInfo };
|
|
127
|
+
|
|
128
|
+
interface ProjectMutationOperationHandler<Operation extends ProjectMutationOperation> {
|
|
129
|
+
preflight(
|
|
130
|
+
request: ApplyProjectMutationPlanRequest,
|
|
131
|
+
operation: Operation,
|
|
132
|
+
): Promise<{ ok: true } | { ok: false; error: AregErrorInfo }>;
|
|
133
|
+
apply(
|
|
134
|
+
request: ApplyProjectMutationPlanRequest,
|
|
135
|
+
operation: Operation,
|
|
136
|
+
): Promise<ProjectMutationOperationResult>;
|
|
137
|
+
recordSuccess(
|
|
138
|
+
paths: AppliedProjectMutationPaths,
|
|
139
|
+
operation: Operation,
|
|
140
|
+
result: Extract<ProjectMutationOperationResult, { ok: true }>,
|
|
141
|
+
): ProjectMutationOperationStatus;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type ApplyProjectMutationPlanResult =
|
|
145
|
+
| ({ ok: true } & ApplyProjectMutationPlanSuccessFields)
|
|
146
|
+
| ({ ok: false; error: AregErrorInfo } & ApplyProjectMutationPlanSuccessFields);
|
|
147
|
+
|
|
148
|
+
export async function applyProjectMutationPlan(
|
|
149
|
+
request: ApplyProjectMutationPlanRequest,
|
|
150
|
+
): Promise<ApplyProjectMutationPlanResult> {
|
|
151
|
+
const operations = flattenProjectMutationPlan(request);
|
|
152
|
+
const preflightStatuses: ProjectMutationOperationStatusRecord[] = [];
|
|
153
|
+
let firstPreflightError: AregErrorInfo | undefined;
|
|
154
|
+
|
|
155
|
+
for (const operation of operations) {
|
|
156
|
+
const result = await operationHandler(operation).preflight(request, operation);
|
|
157
|
+
if (result.ok) {
|
|
158
|
+
preflightStatuses.push(operationStatus(operation, "not-attempted"));
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
firstPreflightError ??= result.error;
|
|
162
|
+
preflightStatuses.push(operationStatus(operation, "failed", result.error));
|
|
163
|
+
}
|
|
164
|
+
if (firstPreflightError !== undefined) {
|
|
165
|
+
return emptyFailure(firstPreflightError, preflightStatuses);
|
|
166
|
+
}
|
|
167
|
+
if (request.execute === false) {
|
|
168
|
+
return {
|
|
169
|
+
ok: true,
|
|
170
|
+
appliedPaths: emptyAppliedProjectMutationPaths(),
|
|
171
|
+
operationStatuses: preflightStatuses,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const appliedPaths = emptyAppliedProjectMutationPaths();
|
|
176
|
+
const operationStatuses: ProjectMutationOperationStatusRecord[] = [];
|
|
177
|
+
for (let index = 0; index < operations.length; index += 1) {
|
|
178
|
+
const operation = operations[index];
|
|
179
|
+
if (operation === undefined) continue;
|
|
180
|
+
const result = await operationHandler(operation).apply(request, operation);
|
|
181
|
+
if (!result.ok) {
|
|
182
|
+
operationStatuses.push(operationStatus(operation, "failed", result.error));
|
|
183
|
+
for (const remaining of operations.slice(index + 1))
|
|
184
|
+
operationStatuses.push(operationStatus(remaining, "not-attempted"));
|
|
185
|
+
return {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: result.error,
|
|
188
|
+
appliedPaths,
|
|
189
|
+
operationStatuses,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const status = operationHandler(operation).recordSuccess(appliedPaths, operation, result);
|
|
193
|
+
operationStatuses.push(operationStatus(operation, status));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
ok: true,
|
|
198
|
+
appliedPaths,
|
|
199
|
+
operationStatuses,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function flattenProjectMutationPlan(
|
|
204
|
+
request: ApplyProjectMutationPlanRequest,
|
|
205
|
+
): ProjectMutationOperation[] {
|
|
206
|
+
const writes = request.writes.map(
|
|
207
|
+
(plan): WriteOperation => ({
|
|
208
|
+
type: "write",
|
|
209
|
+
relativePath: plan.relativePath,
|
|
210
|
+
description: plan.description,
|
|
211
|
+
plan,
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
if (request.policy !== "skill-kind") return writes;
|
|
215
|
+
const deletes = request.deletes.map(
|
|
216
|
+
(plan): DeleteOperation => ({
|
|
217
|
+
type: "delete",
|
|
218
|
+
relativePath: plan.relativePath,
|
|
219
|
+
description: plan.description,
|
|
220
|
+
plan,
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
223
|
+
const deleteSymlinks = request.deleteSymlinks.map(
|
|
224
|
+
(plan): DeleteSymlinkOperation => ({
|
|
225
|
+
type: "delete-symlink",
|
|
226
|
+
relativePath: plan.relativePath,
|
|
227
|
+
description: plan.description,
|
|
228
|
+
plan,
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
231
|
+
const removeEmptyDirs = request.removeEmptyDirs.map(
|
|
232
|
+
(plan): RemoveEmptyDirOperation => ({
|
|
233
|
+
type: "remove-empty-dir",
|
|
234
|
+
relativePath: plan.relativePath,
|
|
235
|
+
description: plan.description,
|
|
236
|
+
plan,
|
|
237
|
+
}),
|
|
238
|
+
);
|
|
239
|
+
return [...writes, ...deletes, ...deleteSymlinks, ...removeEmptyDirs];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const PROJECT_MUTATION_OPERATION_HANDLERS = {
|
|
243
|
+
write: {
|
|
244
|
+
async preflight(request, operation) {
|
|
245
|
+
return await request.ctx.project.preflightWriteTextFile({
|
|
246
|
+
projectDir: request.projectDir,
|
|
247
|
+
relativePath: operation.plan.relativePath,
|
|
248
|
+
content: operation.plan.content,
|
|
249
|
+
description: operation.plan.description,
|
|
250
|
+
createParent: operation.plan.createParent,
|
|
251
|
+
policy: request.policy,
|
|
252
|
+
env: request.ctx.env,
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
async apply(request, operation) {
|
|
256
|
+
return await request.ctx.project.writeTextFile({
|
|
257
|
+
projectDir: request.projectDir,
|
|
258
|
+
relativePath: operation.plan.relativePath,
|
|
259
|
+
content: operation.plan.content,
|
|
260
|
+
description: operation.plan.description,
|
|
261
|
+
createParent: operation.plan.createParent,
|
|
262
|
+
policy: request.policy,
|
|
263
|
+
env: request.ctx.env,
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
recordSuccess(paths, operation) {
|
|
267
|
+
paths.written.push(operation.relativePath);
|
|
268
|
+
return "applied";
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
delete: createSimpleMutationHandler<DeleteOperation>({
|
|
272
|
+
preflight: (project, request) => project.preflightDeleteFile(request),
|
|
273
|
+
apply: (project, request) => project.deleteFile(request),
|
|
274
|
+
recordAppliedPath: (paths, relativePath) => paths.deleted.push(relativePath),
|
|
275
|
+
}),
|
|
276
|
+
"delete-symlink": createSimpleMutationHandler<DeleteSymlinkOperation>({
|
|
277
|
+
preflight: (project, request) => project.preflightDeleteSymlink(request),
|
|
278
|
+
apply: (project, request) => project.deleteSymlink(request),
|
|
279
|
+
recordAppliedPath: (paths, relativePath) => paths.deletedSymlink.push(relativePath),
|
|
280
|
+
}),
|
|
281
|
+
"remove-empty-dir": {
|
|
282
|
+
async preflight(request, operation) {
|
|
283
|
+
return await request.ctx.project.preflightRemoveEmptyDir(
|
|
284
|
+
managedTargetRequest(request, operation),
|
|
285
|
+
);
|
|
286
|
+
},
|
|
287
|
+
async apply(request, operation) {
|
|
288
|
+
const result = await request.ctx.project.removeEmptyDir(
|
|
289
|
+
managedTargetRequest(request, operation),
|
|
290
|
+
);
|
|
291
|
+
return result.ok ? { ok: true, removed: result.removed } : result;
|
|
292
|
+
},
|
|
293
|
+
recordSuccess(paths, operation, result) {
|
|
294
|
+
if (result.removed) {
|
|
295
|
+
paths.removedEmptyDir.push(operation.relativePath);
|
|
296
|
+
return "applied";
|
|
297
|
+
}
|
|
298
|
+
return "skipped";
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
} satisfies {
|
|
302
|
+
readonly [OperationType in ProjectFileMutationOperationType]: ProjectMutationOperationHandler<
|
|
303
|
+
Extract<ProjectMutationOperation, { type: OperationType }>
|
|
304
|
+
>;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
function createSimpleMutationHandler<
|
|
308
|
+
Operation extends DeleteOperation | DeleteSymlinkOperation,
|
|
309
|
+
>(options: {
|
|
310
|
+
preflight: (
|
|
311
|
+
project: AregProjectGateway,
|
|
312
|
+
request: AregProjectManagedTargetRequest,
|
|
313
|
+
) => Promise<AregProjectMutationResult>;
|
|
314
|
+
apply: (
|
|
315
|
+
project: AregProjectGateway,
|
|
316
|
+
request: AregProjectManagedTargetRequest,
|
|
317
|
+
) => Promise<AregProjectMutationResult>;
|
|
318
|
+
recordAppliedPath: (paths: AppliedProjectMutationPaths, relativePath: string) => void;
|
|
319
|
+
}): ProjectMutationOperationHandler<Operation> {
|
|
320
|
+
return {
|
|
321
|
+
async preflight(request, operation) {
|
|
322
|
+
return await options.preflight(request.ctx.project, managedTargetRequest(request, operation));
|
|
323
|
+
},
|
|
324
|
+
async apply(request, operation) {
|
|
325
|
+
return await options.apply(request.ctx.project, managedTargetRequest(request, operation));
|
|
326
|
+
},
|
|
327
|
+
recordSuccess(paths, operation) {
|
|
328
|
+
options.recordAppliedPath(paths, operation.relativePath);
|
|
329
|
+
return "applied";
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function managedTargetRequest(
|
|
335
|
+
request: ApplyProjectMutationPlanRequest,
|
|
336
|
+
operation: DeleteOperation | DeleteSymlinkOperation | RemoveEmptyDirOperation,
|
|
337
|
+
): AregProjectManagedTargetRequest {
|
|
338
|
+
return {
|
|
339
|
+
projectDir: request.projectDir,
|
|
340
|
+
relativePath: operation.plan.relativePath,
|
|
341
|
+
description: operation.plan.description,
|
|
342
|
+
policy: "skill-kind",
|
|
343
|
+
env: request.ctx.env,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function operationHandler<Operation extends ProjectMutationOperation>(
|
|
348
|
+
operation: Operation,
|
|
349
|
+
): ProjectMutationOperationHandler<Operation> {
|
|
350
|
+
return PROJECT_MUTATION_OPERATION_HANDLERS[
|
|
351
|
+
operation.type
|
|
352
|
+
] as ProjectMutationOperationHandler<Operation>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function emptyAppliedProjectMutationPaths(): AppliedProjectMutationPaths {
|
|
356
|
+
return { written: [], deleted: [], deletedSymlink: [], removedEmptyDir: [] };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function emptyFailure(
|
|
360
|
+
error: AregErrorInfo,
|
|
361
|
+
operationStatuses: readonly ProjectMutationOperationStatusRecord[],
|
|
362
|
+
): ApplyProjectMutationPlanResult {
|
|
363
|
+
return {
|
|
364
|
+
ok: false,
|
|
365
|
+
error,
|
|
366
|
+
appliedPaths: emptyAppliedProjectMutationPaths(),
|
|
367
|
+
operationStatuses,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function operationStatus(
|
|
372
|
+
operation: ProjectMutationOperation,
|
|
373
|
+
status: ProjectMutationOperationStatus,
|
|
374
|
+
error?: AregErrorInfo,
|
|
375
|
+
): ProjectMutationOperationStatusRecord {
|
|
376
|
+
return {
|
|
377
|
+
type: operation.type,
|
|
378
|
+
path: operation.relativePath,
|
|
379
|
+
description: operation.description,
|
|
380
|
+
status,
|
|
381
|
+
...(error === undefined ? {} : { error }),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AregCliContext } from "../context.ts";
|
|
2
|
+
import type { AregPathState } from "../gateways.ts";
|
|
3
|
+
|
|
4
|
+
export interface ProjectPathInspection {
|
|
5
|
+
projectDir: string;
|
|
6
|
+
projectPathState: AregPathState;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ResolvedProjectGitRoot<TInspection extends ProjectPathInspection> =
|
|
10
|
+
| {
|
|
11
|
+
type: "ok";
|
|
12
|
+
targetInspection: TInspection;
|
|
13
|
+
projectDir: string;
|
|
14
|
+
}
|
|
15
|
+
| { type: "error"; message: string; projectDir: string };
|
|
16
|
+
|
|
17
|
+
export async function inspectResolvedProjectGitRoot<TInspection extends ProjectPathInspection>(
|
|
18
|
+
ctx: AregCliContext,
|
|
19
|
+
requestPath: string,
|
|
20
|
+
inspectProjectPath: (ctx: AregCliContext, requestPath: string) => Promise<TInspection>,
|
|
21
|
+
): Promise<ResolvedProjectGitRoot<TInspection>> {
|
|
22
|
+
const targetInspection = await inspectProjectPath(ctx, requestPath);
|
|
23
|
+
if (targetInspection.projectPathState.type === "missing") {
|
|
24
|
+
return {
|
|
25
|
+
type: "error",
|
|
26
|
+
message: `Target ${targetInspection.projectDir} does not exist.`,
|
|
27
|
+
projectDir: targetInspection.projectDir,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (targetInspection.projectPathState.type !== "directory") {
|
|
31
|
+
return {
|
|
32
|
+
type: "error",
|
|
33
|
+
message: `${targetInspection.projectDir} is not a directory.`,
|
|
34
|
+
projectDir: targetInspection.projectDir,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const repoRoot = await ctx.git.optionalRepoRoot({ cwd: targetInspection.projectDir });
|
|
38
|
+
if (repoRoot.type === "error") {
|
|
39
|
+
return {
|
|
40
|
+
type: "error",
|
|
41
|
+
message: repoRoot.error.message,
|
|
42
|
+
projectDir: targetInspection.projectDir,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (repoRoot.type === "missing") {
|
|
46
|
+
return {
|
|
47
|
+
type: "error",
|
|
48
|
+
message: `No Git root found containing ${targetInspection.projectDir}.`,
|
|
49
|
+
projectDir: targetInspection.projectDir,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { type: "ok", targetInspection, projectDir: repoRoot.value };
|
|
53
|
+
}
|